summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:49:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:49:25 +0000
commit464df1d5e5ab1322e2dd0a7796939fff1aeefa9a (patch)
tree6a403684e0978f0287d7f0ec0e5aab1fd31a59e1 /lib
parentInitial commit. (diff)
downloade2fsprogs-464df1d5e5ab1322e2dd0a7796939fff1aeefa9a.tar.xz
e2fsprogs-464df1d5e5ab1322e2dd0a7796939fff1aeefa9a.zip
Adding upstream version 1.47.0.upstream/1.47.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/Android.bp45
-rw-r--r--lib/Makefile.bsd-lib47
-rw-r--r--lib/Makefile.checker3
-rw-r--r--lib/Makefile.darwin-lib52
-rw-r--r--lib/Makefile.elf-lib75
-rw-r--r--lib/Makefile.library27
-rw-r--r--lib/Makefile.profile27
-rw-r--r--lib/Makefile.solaris-lib65
-rw-r--r--lib/blkid/Android.bp49
-rw-r--r--lib/blkid/Makefile.in224
-rw-r--r--lib/blkid/blkid.h.in110
-rw-r--r--lib/blkid/blkid.pc.in11
-rw-r--r--lib/blkid/blkidP.h204
-rw-r--r--lib/blkid/blkid_types.h.in167
-rw-r--r--lib/blkid/cache.c209
-rw-r--r--lib/blkid/dev.c254
-rw-r--r--lib/blkid/devname.c561
-rw-r--r--lib/blkid/devno.c242
-rw-r--r--lib/blkid/getsize.c217
-rw-r--r--lib/blkid/libblkid.3.in80
-rw-r--r--lib/blkid/list.h184
-rw-r--r--lib/blkid/llseek.c147
-rw-r--r--lib/blkid/probe.c1841
-rw-r--r--lib/blkid/probe.h852
-rw-r--r--lib/blkid/read.c494
-rw-r--r--lib/blkid/resolve.c140
-rw-r--r--lib/blkid/save.c213
-rw-r--r--lib/blkid/tag.c471
-rw-r--r--lib/blkid/test_probe.in62
-rw-r--r--lib/blkid/tests/cramfs.img.bz2bin0 -> 124 bytes
-rw-r--r--lib/blkid/tests/cramfs.results2
-rw-r--r--lib/blkid/tests/ext2.img.bz2bin0 -> 356 bytes
-rw-r--r--lib/blkid/tests/ext2.results3
-rw-r--r--lib/blkid/tests/ext3.img.bz2bin0 -> 1489 bytes
-rw-r--r--lib/blkid/tests/ext3.results3
-rw-r--r--lib/blkid/tests/fat.img.bz2bin0 -> 249 bytes
-rw-r--r--lib/blkid/tests/fat.results3
-rw-r--r--lib/blkid/tests/fat32_label_64MB.img.bz2bin0 -> 1151 bytes
-rw-r--r--lib/blkid/tests/fat32_label_64MB.results3
-rw-r--r--lib/blkid/tests/iso.img.bz2bin0 -> 336 bytes
-rw-r--r--lib/blkid/tests/iso.results2
-rw-r--r--lib/blkid/tests/jbd.img.bz2bin0 -> 205 bytes
-rw-r--r--lib/blkid/tests/jbd.results2
-rw-r--r--lib/blkid/tests/jfs.img.bz2bin0 -> 3434 bytes
-rw-r--r--lib/blkid/tests/jfs.results3
-rw-r--r--lib/blkid/tests/minix.img.bz2bin0 -> 138 bytes
-rw-r--r--lib/blkid/tests/minix.results1
-rw-r--r--lib/blkid/tests/ocfs2.img.bz2bin0 -> 1002 bytes
-rw-r--r--lib/blkid/tests/ocfs2.results3
-rw-r--r--lib/blkid/tests/reiser3.img.bz2bin0 -> 282 bytes
-rw-r--r--lib/blkid/tests/reiser3.results3
-rw-r--r--lib/blkid/tests/reiser4.img.bz2bin0 -> 366 bytes
-rw-r--r--lib/blkid/tests/reiser4.results3
-rw-r--r--lib/blkid/tests/romfs.img.bz2bin0 -> 105 bytes
-rw-r--r--lib/blkid/tests/romfs.results2
-rw-r--r--lib/blkid/tests/small-fat32.img.bz2bin0 -> 372 bytes
-rw-r--r--lib/blkid/tests/small-fat32.results3
-rw-r--r--lib/blkid/tests/swap0.img.bz2bin0 -> 72 bytes
-rw-r--r--lib/blkid/tests/swap0.results1
-rw-r--r--lib/blkid/tests/swap1.img.bz2bin0 -> 120 bytes
-rw-r--r--lib/blkid/tests/swap1.results3
-rw-r--r--lib/blkid/tests/udf.img.bz2bin0 -> 2031 bytes
-rw-r--r--lib/blkid/tests/udf.results2
-rw-r--r--lib/blkid/tests/xfs.img.bz2bin0 -> 443 bytes
-rw-r--r--lib/blkid/tests/xfs.results3
-rw-r--r--lib/blkid/tests/zfs.img.bz2bin0 -> 10869 bytes
-rw-r--r--lib/blkid/tests/zfs.results1
-rw-r--r--lib/blkid/tst_types.c64
-rw-r--r--lib/blkid/version.c50
-rw-r--r--lib/config.h.in665
-rw-r--r--lib/dirpaths.h.in10
-rw-r--r--lib/e2p/Android.bp58
-rw-r--r--lib/e2p/Makefile.in223
-rw-r--r--lib/e2p/crypto_mode.c74
-rw-r--r--lib/e2p/e2p.h92
-rw-r--r--lib/e2p/e2p.pc.in11
-rw-r--r--lib/e2p/encoding.c118
-rw-r--r--lib/e2p/errcode.c48
-rw-r--r--lib/e2p/feature.c449
-rw-r--r--lib/e2p/fgetflags.c117
-rw-r--r--lib/e2p/fgetproject.c63
-rw-r--r--lib/e2p/fgetversion.c74
-rw-r--r--lib/e2p/fsetflags.c118
-rw-r--r--lib/e2p/fsetproject.c69
-rw-r--r--lib/e2p/fsetversion.c71
-rw-r--r--lib/e2p/getflags.c71
-rw-r--r--lib/e2p/getversion.c41
-rw-r--r--lib/e2p/hashstr.c72
-rw-r--r--lib/e2p/iod.c76
-rw-r--r--lib/e2p/ljs.c134
-rw-r--r--lib/e2p/ls.c494
-rw-r--r--lib/e2p/mntopts.c150
-rw-r--r--lib/e2p/ostype.c79
-rw-r--r--lib/e2p/parse_num.c91
-rw-r--r--lib/e2p/pe.c40
-rw-r--r--lib/e2p/percent.c67
-rw-r--r--lib/e2p/pf.c79
-rw-r--r--lib/e2p/project.h27
-rw-r--r--lib/e2p/ps.c32
-rw-r--r--lib/e2p/setflags.c77
-rw-r--r--lib/e2p/setversion.c40
-rw-r--r--lib/e2p/uuid.c85
-rw-r--r--lib/et/Android.bp40
-rw-r--r--lib/et/Makefile.in185
-rw-r--r--lib/et/com_err.387
-rw-r--r--lib/et/com_err.c105
-rw-r--r--lib/et/com_err.h68
-rw-r--r--lib/et/com_err.pc.in12
-rw-r--r--lib/et/com_err.texinfo532
-rw-r--r--lib/et/com_right.c120
-rw-r--r--lib/et/compile_et.182
-rw-r--r--lib/et/compile_et.sh.in60
-rw-r--r--lib/et/error_message.c355
-rw-r--r--lib/et/error_table.h29
-rw-r--r--lib/et/et_c.awk269
-rw-r--r--lib/et/et_h.awk204
-rw-r--r--lib/et/et_name.c43
-rw-r--r--lib/et/init_et.c53
-rw-r--r--lib/et/internal.h19
-rw-r--r--lib/et/test_cases/continuation.c55
-rw-r--r--lib/et/test_cases/continuation.et8
-rw-r--r--lib/et/test_cases/continuation.h19
-rw-r--r--lib/et/test_cases/heimdal.c136
-rw-r--r--lib/et/test_cases/heimdal.et65
-rw-r--r--lib/et/test_cases/heimdal.h64
-rw-r--r--lib/et/test_cases/heimdal2.c122
-rw-r--r--lib/et/test_cases/heimdal2.et65
-rw-r--r--lib/et/test_cases/heimdal2.h58
-rw-r--r--lib/et/test_cases/heimdal3.c56
-rw-r--r--lib/et/test_cases/heimdal3.et5
-rw-r--r--lib/et/test_cases/heimdal3.h20
-rw-r--r--lib/et/test_cases/imap_err.c115
-rw-r--r--lib/et/test_cases/imap_err.et238
-rw-r--r--lib/et/test_cases/imap_err.h79
-rw-r--r--lib/et/test_cases/simple.c76
-rw-r--r--lib/et/test_cases/simple.et69
-rw-r--r--lib/et/test_cases/simple.h40
-rw-r--r--lib/et/texinfo.tex7226
-rw-r--r--lib/et/vfprintf.c50
-rw-r--r--lib/ext2fs/Android.bp129
-rw-r--r--lib/ext2fs/Makefile.in1459
-rw-r--r--lib/ext2fs/alloc.c554
-rw-r--r--lib/ext2fs/alloc_sb.c81
-rw-r--r--lib/ext2fs/alloc_stats.c165
-rw-r--r--lib/ext2fs/alloc_tables.c278
-rw-r--r--lib/ext2fs/atexit.c116
-rw-r--r--lib/ext2fs/badblocks.c328
-rw-r--r--lib/ext2fs/bb_compat.c64
-rw-r--r--lib/ext2fs/bb_inode.c270
-rw-r--r--lib/ext2fs/bitmaps.c320
-rw-r--r--lib/ext2fs/bitops.c148
-rw-r--r--lib/ext2fs/bitops.h606
-rw-r--r--lib/ext2fs/blkmap64_ba.c492
-rw-r--r--lib/ext2fs/blkmap64_rb.c998
-rw-r--r--lib/ext2fs/blknum.c606
-rw-r--r--lib/ext2fs/block.c659
-rw-r--r--lib/ext2fs/bmap.c499
-rw-r--r--lib/ext2fs/bmap64.h106
-rw-r--r--lib/ext2fs/bmove.c167
-rw-r--r--lib/ext2fs/brel.h86
-rw-r--r--lib/ext2fs/brel_ma.c199
-rw-r--r--lib/ext2fs/check_desc.c104
-rw-r--r--lib/ext2fs/closefs.c521
-rw-r--r--lib/ext2fs/compiler.h26
-rw-r--r--lib/ext2fs/crc16.c74
-rw-r--r--lib/ext2fs/crc16.h26
-rw-r--r--lib/ext2fs/crc32c.c938
-rw-r--r--lib/ext2fs/crc32c_defs.h59
-rw-r--r--lib/ext2fs/csum.c1011
-rw-r--r--lib/ext2fs/dblist.c403
-rw-r--r--lib/ext2fs/dblist_dir.c88
-rw-r--r--lib/ext2fs/digest_encode.c187
-rw-r--r--lib/ext2fs/dir_iterate.c315
-rw-r--r--lib/ext2fs/dirblock.c113
-rw-r--r--lib/ext2fs/dirhash.c307
-rw-r--r--lib/ext2fs/dosio.c459
-rw-r--r--lib/ext2fs/dosio.h157
-rw-r--r--lib/ext2fs/dupfs.c122
-rw-r--r--lib/ext2fs/e2image.h37
-rw-r--r--lib/ext2fs/expanddir.c143
-rw-r--r--lib/ext2fs/ext2_err.et.in560
-rw-r--r--lib/ext2fs/ext2_ext_attr.h84
-rw-r--r--lib/ext2fs/ext2_fs.h1201
-rw-r--r--lib/ext2fs/ext2_io.h179
-rw-r--r--lib/ext2fs/ext2_types.h.in196
-rw-r--r--lib/ext2fs/ext2fs.h2224
-rw-r--r--lib/ext2fs/ext2fs.pc.in11
-rw-r--r--lib/ext2fs/ext2fsP.h210
-rw-r--r--lib/ext2fs/ext3_extents.h127
-rw-r--r--lib/ext2fs/ext4_acl.h62
-rw-r--r--lib/ext2fs/ext_attr.c1784
-rw-r--r--lib/ext2fs/extent.c1877
-rw-r--r--lib/ext2fs/fallocate.c873
-rw-r--r--lib/ext2fs/fast_commit.h190
-rw-r--r--lib/ext2fs/fiemap.h93
-rw-r--r--lib/ext2fs/fileio.c666
-rw-r--r--lib/ext2fs/finddev.c218
-rw-r--r--lib/ext2fs/flushb.c88
-rw-r--r--lib/ext2fs/freefs.c108
-rw-r--r--lib/ext2fs/gen_bitmap.c650
-rw-r--r--lib/ext2fs/gen_bitmap64.c981
-rw-r--r--lib/ext2fs/gen_crc32ctable.c117
-rw-r--r--lib/ext2fs/get_num_dirs.c50
-rw-r--r--lib/ext2fs/get_pathname.c171
-rw-r--r--lib/ext2fs/getsectsize.c151
-rw-r--r--lib/ext2fs/getsize.c324
-rw-r--r--lib/ext2fs/hashmap.c109
-rw-r--r--lib/ext2fs/hashmap.h42
-rw-r--r--lib/ext2fs/i_block.c90
-rw-r--r--lib/ext2fs/icount.c921
-rw-r--r--lib/ext2fs/imager.c470
-rw-r--r--lib/ext2fs/ind_block.c67
-rw-r--r--lib/ext2fs/initialize.c671
-rw-r--r--lib/ext2fs/inline.c118
-rw-r--r--lib/ext2fs/inline_data.c842
-rw-r--r--lib/ext2fs/inode.c1122
-rw-r--r--lib/ext2fs/inode_io.c290
-rw-r--r--lib/ext2fs/io_manager.c152
-rw-r--r--lib/ext2fs/irel.h114
-rw-r--r--lib/ext2fs/irel_ma.c377
-rw-r--r--lib/ext2fs/ismounted.c478
-rw-r--r--lib/ext2fs/jfs_compat.h113
-rw-r--r--lib/ext2fs/kernel-jbd.h456
-rw-r--r--lib/ext2fs/kernel-list.h111
-rw-r--r--lib/ext2fs/link.c646
-rw-r--r--lib/ext2fs/llseek.c145
-rw-r--r--lib/ext2fs/lookup.c70
-rw-r--r--lib/ext2fs/mkdir.c200
-rw-r--r--lib/ext2fs/mkjournal.c654
-rw-r--r--lib/ext2fs/mmp.c488
-rw-r--r--lib/ext2fs/namei.c227
-rw-r--r--lib/ext2fs/native.c28
-rw-r--r--lib/ext2fs/newdir.c126
-rw-r--r--lib/ext2fs/nls_utf8.c1000
-rw-r--r--lib/ext2fs/nt_io.c1494
-rw-r--r--lib/ext2fs/openfs.c590
-rw-r--r--lib/ext2fs/orphan.c273
-rw-r--r--lib/ext2fs/progress.c103
-rw-r--r--lib/ext2fs/punch.c513
-rw-r--r--lib/ext2fs/qcow2.c272
-rw-r--r--lib/ext2fs/qcow2.h114
-rw-r--r--lib/ext2fs/rbtree.c383
-rw-r--r--lib/ext2fs/rbtree.h183
-rw-r--r--lib/ext2fs/read_bb.c102
-rw-r--r--lib/ext2fs/read_bb_file.c109
-rw-r--r--lib/ext2fs/res_gdt.c239
-rw-r--r--lib/ext2fs/rw_bitmaps.c682
-rw-r--r--lib/ext2fs/sha256.c254
-rw-r--r--lib/ext2fs/sha512.c302
-rw-r--r--lib/ext2fs/sparse_io.c554
-rw-r--r--lib/ext2fs/swapfs.c491
-rw-r--r--lib/ext2fs/symlink.c212
-rw-r--r--lib/ext2fs/tdb.c4174
-rw-r--r--lib/ext2fs/tdb.h215
-rwxr-xr-xlib/ext2fs/tdb/build-tdb34
-rw-r--r--lib/ext2fs/tdb/patches/copyright20
-rw-r--r--lib/ext2fs/tdb/patches/ext2tdb-rename65
-rw-r--r--lib/ext2fs/tdb/patches/replace-includes92
-rw-r--r--lib/ext2fs/tdb/patches/series6
-rw-r--r--lib/ext2fs/tdb/patches/static-functions13
-rw-r--r--lib/ext2fs/tdb/patches/static-prototypes72
-rw-r--r--lib/ext2fs/tdb/patches/tdbtool-includes30
-rw-r--r--lib/ext2fs/tdbtool.c621
-rw-r--r--lib/ext2fs/test_io.c557
-rw-r--r--lib/ext2fs/tst_badblocks.c369
-rw-r--r--lib/ext2fs/tst_bitmaps.c732
-rw-r--r--lib/ext2fs/tst_bitmaps_cmd.ct51
-rw-r--r--lib/ext2fs/tst_bitmaps_cmds150
-rw-r--r--lib/ext2fs/tst_bitmaps_exp313
-rw-r--r--lib/ext2fs/tst_bitops.c288
-rw-r--r--lib/ext2fs/tst_byteswap.c93
-rw-r--r--lib/ext2fs/tst_cmds.ct6
-rw-r--r--lib/ext2fs/tst_fs_struct.c81
-rw-r--r--lib/ext2fs/tst_getsectsize.c63
-rw-r--r--lib/ext2fs/tst_getsize.c47
-rw-r--r--lib/ext2fs/tst_inode_size.c89
-rw-r--r--lib/ext2fs/tst_iscan.c227
-rw-r--r--lib/ext2fs/tst_libext2fs.c72
-rw-r--r--lib/ext2fs/tst_super_size.c161
-rw-r--r--lib/ext2fs/tst_types.c64
-rw-r--r--lib/ext2fs/undo_io.c1108
-rw-r--r--lib/ext2fs/unix_io.c1510
-rw-r--r--lib/ext2fs/unlink.c100
-rw-r--r--lib/ext2fs/utf8data.h4109
-rw-r--r--lib/ext2fs/utf8n.h120
-rw-r--r--lib/ext2fs/valid_blk.c68
-rw-r--r--lib/ext2fs/version.c57
-rw-r--r--lib/ext2fs/windows_io.c1031
-rw-r--r--lib/ext2fs/write_bb_file.c35
-rw-r--r--lib/fpopen.c116
-rw-r--r--lib/ss/Android.bp39
-rw-r--r--lib/ss/Makefile.in234
-rw-r--r--lib/ss/ct_c.awk75
-rw-r--r--lib/ss/ct_c.sed160
-rw-r--r--lib/ss/data.c20
-rw-r--r--lib/ss/error.c77
-rw-r--r--lib/ss/execute_cmd.c229
-rw-r--r--lib/ss/get_readline.c100
-rw-r--r--lib/ss/help.c185
-rw-r--r--lib/ss/invocation.c132
-rw-r--r--lib/ss/list_rqs.c92
-rw-r--r--lib/ss/listen.c200
-rw-r--r--lib/ss/mit-sipb-copyright.h19
-rw-r--r--lib/ss/mk_cmds.159
-rw-r--r--lib/ss/mk_cmds.sh.in56
-rw-r--r--lib/ss/pager.c152
-rw-r--r--lib/ss/parse.c158
-rw-r--r--lib/ss/prompt.c31
-rw-r--r--lib/ss/request_tbl.c68
-rw-r--r--lib/ss/requests.c66
-rw-r--r--lib/ss/ss.h96
-rw-r--r--lib/ss/ss.pc.in12
-rw-r--r--lib/ss/ss_err.et39
-rw-r--r--lib/ss/ss_internal.h107
-rw-r--r--lib/ss/std_rqs.ct46
-rw-r--r--lib/ss/test_cmd.ct6
-rw-r--r--lib/ss/test_script8
-rw-r--r--lib/ss/test_script_expected22
-rw-r--r--lib/ss/test_ss.c151
-rw-r--r--lib/support/Android.bp78
-rw-r--r--lib/support/Makefile.in184
-rw-r--r--lib/support/argv_parse.c168
-rw-r--r--lib/support/argv_parse.h43
-rw-r--r--lib/support/common.h36
-rw-r--r--lib/support/cstring.c162
-rw-r--r--lib/support/cstring.h6
-rw-r--r--lib/support/devname.c65
-rw-r--r--lib/support/devname.h19
-rw-r--r--lib/support/dict.c1542
-rw-r--r--lib/support/dict.h147
-rw-r--r--lib/support/dqblk_v2.h31
-rw-r--r--lib/support/mkquota.c707
-rw-r--r--lib/support/nls-enable.h21
-rw-r--r--lib/support/parse_qtype.c90
-rw-r--r--lib/support/plausible.c287
-rw-r--r--lib/support/plausible.h29
-rw-r--r--lib/support/print_fs_flags.c75
-rw-r--r--lib/support/print_fs_flags.h5
-rw-r--r--lib/support/prof_err.et66
-rw-r--r--lib/support/profile.c1910
-rw-r--r--lib/support/profile.h107
-rw-r--r--lib/support/profile_helpers.c317
-rw-r--r--lib/support/profile_helpers.h28
-rw-r--r--lib/support/quotaio.c412
-rw-r--r--lib/support/quotaio.h268
-rw-r--r--lib/support/quotaio_tree.c687
-rw-r--r--lib/support/quotaio_tree.h64
-rw-r--r--lib/support/quotaio_v2.c389
-rw-r--r--lib/support/quotaio_v2.h69
-rw-r--r--lib/support/sort_r.h325
-rw-r--r--lib/uuid/Android.bp54
-rw-r--r--lib/uuid/COPYING25
-rw-r--r--lib/uuid/Makefile.in216
-rw-r--r--lib/uuid/clear.c44
-rw-r--r--lib/uuid/compare.c56
-rw-r--r--lib/uuid/configure.in10
-rw-r--r--lib/uuid/copy.c46
-rw-r--r--lib/uuid/gen_uuid.c682
-rw-r--r--lib/uuid/gen_uuid_nt.c93
-rw-r--r--lib/uuid/isnull.c49
-rw-r--r--lib/uuid/pack.c70
-rw-r--r--lib/uuid/parse.c80
-rw-r--r--lib/uuid/tst_uuid.c194
-rw-r--r--lib/uuid/unpack.c64
-rw-r--r--lib/uuid/unparse.c77
-rw-r--r--lib/uuid/uuid.3.in66
-rw-r--r--lib/uuid/uuid.h.in103
-rw-r--r--lib/uuid/uuid.pc.in11
-rw-r--r--lib/uuid/uuidP.h71
-rw-r--r--lib/uuid/uuid_clear.3.in60
-rw-r--r--lib/uuid/uuid_compare.3.in66
-rw-r--r--lib/uuid/uuid_copy.3.in62
-rw-r--r--lib/uuid/uuid_generate.3.in104
-rw-r--r--lib/uuid/uuid_is_null.3.in62
-rw-r--r--lib/uuid/uuid_parse.3.in71
-rw-r--r--lib/uuid/uuid_time.3.in74
-rw-r--r--lib/uuid/uuid_time.c167
-rw-r--r--lib/uuid/uuid_types.h.in50
-rw-r--r--lib/uuid/uuid_unparse.3.in79
-rw-r--r--lib/uuid/uuidd.h54
380 files changed, 96384 insertions, 0 deletions
diff --git a/lib/Android.bp b/lib/Android.bp
new file mode 100644
index 0000000..529fa23
--- /dev/null
+++ b/lib/Android.bp
@@ -0,0 +1,45 @@
+// Copyright 2017 The Android Open Source Project
+
+// All the libraries under this directory export their headers as relative
+// paths to this directory (external/e2fsprogs/lib). This is a helper headers
+// only library to allow exporting
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-0BSD
+ // SPDX-license-identifier-BSD
+ // SPDX-license-identifier-GPL
+ // SPDX-license-identifier-GPL-2.0
+ // SPDX-license-identifier-LGPL
+ // SPDX-license-identifier-LGPL-2.1
+ // SPDX-license-identifier-LGPL-3.0
+ // SPDX-license-identifier-MIT
+ // legacy_unencumbered
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library_headers {
+ name: "libext2-headers",
+ host_supported: true,
+ vendor_available: true,
+ product_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ export_include_dirs: ["."],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
+
+subdirs = [
+ "*",
+]
diff --git a/lib/Makefile.bsd-lib b/lib/Makefile.bsd-lib
new file mode 100644
index 0000000..2792ba3
--- /dev/null
+++ b/lib/Makefile.bsd-lib
@@ -0,0 +1,47 @@
+#
+# This is a Makefile stub which handles the creation of BSD shared
+# libraries.
+#
+# In order to use this stub, the following makefile variables must be defined.
+#
+# BSDLIB_VERSION = 1.0
+# BSDLIB_IMAGE = libce
+# BSDLIB_MYDIR = et
+# BSDLIB_INSTALL_DIR = $(SHLIBDIR)
+#
+
+all:: image
+
+real-subdirs:: Makefile
+ @echo " MKDIR pic"
+ @mkdir -p pic
+
+BSD_LIB = $(BSDLIB_IMAGE).so.$(BSDLIB_VERSION)
+BSDLIB_PIC_FLAG = -fpic
+
+image: $(BSD_LIB)
+
+$(BSD_LIB): $(OBJS)
+ (cd pic; ld -Bshareable -o $(BSD_LIB) $(LDFLAGS_SHLIB) $(OBJS))
+ $(MV) pic/$(BSD_LIB) .
+ $(RM) -f ../$(BSD_LIB)
+ (cd ..; $(LN) $(LINK_BUILD_FLAGS) \
+ `echo $(my_dir) | sed -e 's;lib/;;'`/$(BSD_LIB) $(BSD_LIB))
+
+install-shlibs install:: $(BSD_LIB)
+ @echo " INSTALL_PROGRAM $(BSDLIB_INSTALL_DIR)/$(BSD_LIB)"
+ @$(INSTALL_PROGRAM) $(BSD_LIB) \
+ $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB)
+ @-$(LDCONFIG)
+
+install-strip: install
+
+install-shlibs-strip:: install-shlibs
+
+uninstall-shlibs uninstall::
+ $(RM) -f $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB)
+
+clean::
+ $(RM) -rf pic
+ $(RM) -f $(BSD_LIB)
+ $(RM) -f ../$(BSD_LIB)
diff --git a/lib/Makefile.checker b/lib/Makefile.checker
new file mode 100644
index 0000000..0b5f9a3
--- /dev/null
+++ b/lib/Makefile.checker
@@ -0,0 +1,3 @@
+# This is a placeholder so builds don't break after a "git pull"
+# which removed the checker infrastructure. This file can be deleted
+# after e2fsprogs 1.44 is released
diff --git a/lib/Makefile.darwin-lib b/lib/Makefile.darwin-lib
new file mode 100644
index 0000000..95e8ee0
--- /dev/null
+++ b/lib/Makefile.darwin-lib
@@ -0,0 +1,52 @@
+#
+# This is a Makefile stub which handles the creation of Darwin BSD shared
+# libraries.
+#
+# In order to use this stub, the following makefile variables must be defined.
+#
+# BSDLIB_VERSION = 1.0
+# BSDLIB_IMAGE = libce
+# BSDLIB_MYDIR = et
+# BSDLIB_INSTALL_DIR = $(SHLIBDIR)
+#
+
+all:: image
+
+real-subdirs:: Makefile
+ $(E) " MKDIR pic"
+ $(Q) mkdir -p pic
+
+BSD_LIB = $(BSDLIB_IMAGE).$(BSDLIB_VERSION).dylib
+BSDLIB_PIC_FLAG = -fPIC
+
+image: $(BSD_LIB)
+
+$(BSD_LIB): $(OBJS)
+ $(E) " GEN_BSD_SOLIB $(BSD_LIB)"
+ $(Q) (cd pic; $(CC) -dynamiclib -compatibility_version 1.0 -current_version $(BSDLIB_VERSION) \
+ -install_name $(BSDLIB_INSTALL_DIR)/$(BSD_LIB) \
+ -undefined dynamic_lookup -o $(BSD_LIB) $(OBJS))
+ $(Q) $(MV) pic/$(BSD_LIB) .
+ $(Q) $(RM) -f ../$(BSD_LIB)
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \
+ `echo $(my_dir) | sed -e 's;lib/;;'`/$(BSD_LIB) $(BSD_LIB))
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(BSD_LIB) $(BSDLIB_IMAGE).dylib)
+
+install-shlibs install:: $(BSD_LIB)
+ $(E) " INSTALL_PROGRAM $(BSDLIB_INSTALL_DIR)/$(BSD_LIB)"
+ $(Q) $(INSTALL_PROGRAM) $(BSD_LIB) \
+ $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB)
+ -$(Q) $(LDCONFIG)
+
+install-strip: install
+
+install-shlibs-strip:: install-shlibs
+
+uninstall-shlibs uninstall::
+ $(RM) -f $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB)
+
+clean::
+ $(RM) -rf pic
+ $(RM) -f $(BSD_LIB)
+ $(RM) -f ../$(BSD_LIB)
+ $(RM) -f ../$(BSDLIB_IMAGE).dylib
diff --git a/lib/Makefile.elf-lib b/lib/Makefile.elf-lib
new file mode 100644
index 0000000..f850f3d
--- /dev/null
+++ b/lib/Makefile.elf-lib
@@ -0,0 +1,75 @@
+#
+# This is a Makefile stub which handles the creation of Linux ELF shared
+# libraries.
+#
+# In order to use this stub, the following makefile variables must be defined.
+#
+# ELF_VERSION = 1.0
+# ELF_SO_VERSION = 1
+# ELF_IMAGE = libce
+# ELF_MYDIR = et
+# ELF_INSTALL_DIR = $(SHLIBDIR)
+# ELF_OTHER_LIBS = -lc
+
+all:: image
+
+real-subdirs:: Makefile
+ $(E) " MKDIR elfshared"
+ $(Q) mkdir -p elfshared
+
+ELF_LIB = $(ELF_IMAGE).so.$(ELF_VERSION)
+ELF_SONAME = $(ELF_IMAGE).so.$(ELF_SO_VERSION)
+
+image: $(ELF_LIB)
+
+$(ELF_LIB): $(OBJS)
+ $(E) " GEN_ELF_SOLIB $(ELF_LIB)"
+ $(Q) (cd elfshared; $(CC) -o $(ELF_LIB) \
+ -L$(top_builddir)/../lib $(LDFLAGS_SHLIB) -fPIC -shared \
+ -Wl,-soname,$(ELF_SONAME) $(OBJS) $(ELF_OTHER_LIBS))
+ $(Q) $(MV) elfshared/$(ELF_LIB) .
+ $(Q) $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME)
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \
+ `echo $(my_dir) | sed -e 's;lib/;;'`/$(ELF_LIB) $(ELF_LIB))
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(ELF_LIB) $(ELF_IMAGE).so)
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(ELF_LIB) $(ELF_SONAME))
+
+installdirs-elf-lib::
+ $(E) " MKDIR_P $(ELF_INSTALL_DIR) $(libdir)"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(ELF_INSTALL_DIR) \
+ $(DESTDIR)$(libdir)
+
+installdirs:: installdirs-elf-lib
+
+install-shlibs install:: $(ELF_LIB) installdirs-elf-lib $(DEP_INSTALL_SYMLINK)
+ $(E) " INSTALL-ELF-LIB $(ELF_INSTALL_DIR)/$(ELF_LIB)"
+ $(Q) $(INSTALL_PROGRAM) $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
+ $(E) " SYMLINK $(ELF_INSTALL_DIR)/$(ELF_SONAME)"
+ $(Q) $(INSTALL_SYMLINK) $(ELF_INSTALL_DIR)/$(ELF_LIB) \
+ $(ELF_INSTALL_DIR)/$(ELF_SONAME) $(DESTDIR)
+ $(E) " SYMLINK $(libdir)/$(ELF_IMAGE).so"
+ $(Q) $(INSTALL_SYMLINK) $(ELF_INSTALL_DIR)/$(ELF_SONAME) \
+ $(libdir)/$(ELF_IMAGE).so $(DESTDIR)
+ $(E) " LDCONFIG"
+ $(Q) -$(LDCONFIG)
+
+install-strip: install
+ $(E) " STRIP-LIB $(ELF_INSTALL_DIR)/$(ELF_LIB)"
+ $(Q) $(STRIP) --strip-unneeded --remove-section=.comment \
+ --remove-section=.note $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
+
+install-shlibs-strip:: install-shlibs
+ $(E) " STRIP-LIB $(ELF_INSTALL_DIR)/$(ELF_LIB)"
+ $(Q) $(STRIP) --strip-unneeded --remove-section=.comment \
+ --remove-section=.note $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
+
+uninstall-shlibs uninstall::
+ $(RM) -f $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB) \
+ $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_SONAME) \
+ $(DESTDIR)$(libdir)/$(ELF_IMAGE).so
+ -$(LDCONFIG)
+
+clean::
+ $(RM) -rf elfshared
+ $(RM) -f $(ELF_LIB)
+ $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME)
diff --git a/lib/Makefile.library b/lib/Makefile.library
new file mode 100644
index 0000000..f78467a
--- /dev/null
+++ b/lib/Makefile.library
@@ -0,0 +1,27 @@
+all:: subdirs $(LIBRARY).a
+
+install-shlibs-strip::
+
+install-shlibs::
+
+uninstall-shlibs::
+
+real-subdirs::
+
+subdirs:: Makefile
+ $(Q) $(MAKE) -s real-subdirs
+ $(Q) touch subdirs
+
+clean::
+ $(Q) $(RM) -f subdirs
+
+$(LIBRARY).a: $(OBJS)
+ $(E) " GEN_LIB $@"
+ $(Q) (if test -r $@; then $(RM) -f $@.bak && $(MV) $@ $@.bak; fi)
+ $(Q) $(ARGEN) $@ $(OBJS)
+ -@$(RANLIB) $@
+ $(Q) $(RM) -f ../$@
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \
+ `echo $(my_dir) | sed -e 's;lib/;;'`/$@ $@)
+
+$(LIB)/$(LIBRARY).a: $(LIBRARY).a
diff --git a/lib/Makefile.profile b/lib/Makefile.profile
new file mode 100644
index 0000000..180f5bc
--- /dev/null
+++ b/lib/Makefile.profile
@@ -0,0 +1,27 @@
+all:: $(LIBRARY)_p.a
+
+real-subdirs:: Makefile
+ $(E) " MKDIR profiled"
+ $(Q) mkdir -p profiled
+
+clean::
+ $(RM) -rf profiled
+ $(RM) -f $(LIBRARY)_p.a ../$(LIBRARY)_p.a
+
+$(LIBRARY)_p.a: $(OBJS)
+ $(E) " GEN_PROFILED_LIB $(ELF_LIB)"
+ $(Q) (if test -r $@; then $(RM) -f $@.bak && $(MV) $@ $@.bak; fi)
+ $(Q) (cd profiled; $(ARUPD) ../$@ $(OBJS))
+ -$(Q) $(RANLIB) $@
+ $(Q) $(RM) -f ../$@
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \
+ `echo $(my_dir) | sed -e 's;lib/;;'`/$@ $@)
+
+install:: $(LIBRARY)_p.a installdirs
+ $(E) " INSTALL_DATA $(libdir)/$(LIBRARY)_p.a"
+ $(Q) $(INSTALL_DATA) $(LIBRARY)_p.a $(DESTDIR)$(libdir)/$(LIBRARY)_p.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/$(LIBRARY)_p.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/$(LIBRARY)_p.a
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/$(LIBRARY)_p.a
diff --git a/lib/Makefile.solaris-lib b/lib/Makefile.solaris-lib
new file mode 100644
index 0000000..1e63636
--- /dev/null
+++ b/lib/Makefile.solaris-lib
@@ -0,0 +1,65 @@
+#
+# This is a Makefile stub which handles the creation of Linux ELF shared
+# libraries.
+#
+# In order to use this stub, the following makefile variables must be defined.
+#
+# ELF_VERSION = 1.0
+# ELF_SO_VERSION = 1
+# ELF_IMAGE = libce
+# ELF_MYDIR = et
+# ELF_INSTALL_DIR = $(SHLIBDIR)
+# ELF_OTHER_LIBS = -lc
+
+all:: image
+
+real-subdirs:: Makefile
+ $(E) " MKDIR elfshared"
+ $(Q) mkdir -p elfshared
+
+ELF_LIB = $(ELF_IMAGE).so.$(ELF_VERSION)
+ELF_SONAME = $(ELF_IMAGE).so.$(ELF_SO_VERSION)
+
+image: $(ELF_LIB)
+
+$(ELF_LIB): $(OBJS)
+ $(E) " GEN_ELF_SOLIB $(ELF_LIB)"
+ $(Q) (cd elfshared; $(CC) --shared -o $(ELF_LIB) \
+ -L$(top_builddir)/../lib $(LDFLAGS_SHLIB) \
+ -Wl,-h,$(ELF_SONAME) $(OBJS) $(ELF_OTHER_LIBS))
+ $(Q) $(MV) elfshared/$(ELF_LIB) .
+ $(Q) $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME)
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \
+ `echo $(my_dir) | sed -e 's;lib/;;'`/$(ELF_LIB) $(ELF_LIB))
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(ELF_LIB) $(ELF_IMAGE).so)
+ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(ELF_LIB) $(ELF_SONAME))
+
+installdirs-elf-lib::
+ $(MKDIR_P) $(DESTDIR)$(ELF_INSTALL_DIR) \
+ $(DESTDIR)$(libdir)
+
+installdirs:: installdirs-elf-lib
+
+install-shlibs install:: $(ELF_LIB) installdirs-elf-lib
+ $(INSTALL_PROGRAM) $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
+ $(LN_S) -f $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_SONAME)
+ $(LN_S) -f $(ELF_INSTALL_DIR)/$(ELF_SONAME) \
+ $(DESTDIR)$(libdir)/$(ELF_IMAGE).so
+ -$(LDCONFIG)
+
+install-strip: install
+ $(STRIP) -x $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
+
+install-shlibs-strip:: install-shlibs
+ $(STRIP) -x $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB)
+
+uninstall-shlibs uninstall::
+ $(RM) -f $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB) \
+ $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_SONAME) \
+ $(DESTDIR)$(libdir)/$(ELF_IMAGE).so
+ -$(LDCONFIG)
+
+clean::
+ $(RM) -rf elfshared
+ $(RM) -f $(ELF_LIB)
+ $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME)
diff --git a/lib/blkid/Android.bp b/lib/blkid/Android.bp
new file mode 100644
index 0000000..891c74a
--- /dev/null
+++ b/lib/blkid/Android.bp
@@ -0,0 +1,49 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-GPL
+ // SPDX-license-identifier-LGPL
+ // SPDX-license-identifier-LGPL-2.1
+ // SPDX-license-identifier-LGPL-3.0
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library {
+ name: "libext2_blkid",
+ host_supported: true,
+ ramdisk_available: true,
+ vendor_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "cache.c",
+ "dev.c",
+ "devname.c",
+ "devno.c",
+ "getsize.c",
+ "llseek.c",
+ "probe.c",
+ "read.c",
+ "resolve.c",
+ "save.c",
+ "tag.c",
+ "version.c",
+ ],
+ shared_libs: ["libext2_uuid"],
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
diff --git a/lib/blkid/Makefile.in b/lib/blkid/Makefile.in
new file mode 100644
index 0000000..c5ca920
--- /dev/null
+++ b/lib/blkid/Makefile.in
@@ -0,0 +1,224 @@
+# Makefile for libblkid
+#
+# Copyright (C) 2001 Theodore Ts'o (tytso@mit.edu)
+#
+# This file can be redistributed under the terms of the
+# GNU Lesser General Public License
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/blkid
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+
+@MCONFIG@
+
+all::
+
+SMANPAGES= libblkid.3
+
+OBJS= cache.o dev.o devname.o devno.o getsize.o llseek.o probe.o \
+ read.o resolve.o save.o tag.o version.o
+
+SRCS= $(srcdir)/cache.c $(srcdir)/dev.c $(srcdir)/devname.c $(srcdir)/devno.c \
+ $(srcdir)/getsize.c $(srcdir)/llseek.c $(srcdir)/probe.c \
+ $(srcdir)/read.c $(srcdir)/resolve.c $(srcdir)/save.c $(srcdir)/tag.c \
+ $(srcdir)/version.c
+
+HFILES_IN= blkid.h blkid_types.h
+
+LIBRARY= libblkid
+LIBDIR= blkid
+
+ELF_VERSION = 1.0
+ELF_SO_VERSION = 1
+ELF_IMAGE = libblkid
+ELF_MYDIR = blkid
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS = -luuid
+
+BSDLIB_VERSION = 2.0
+BSDLIB_IMAGE = libblkid
+BSDLIB_MYDIR = blkid
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+
+LIBS_BLKID= $(STATIC_LIBBLKID) $(STATIC_LIBUUID)
+DEPLIBS_BLKID= $(DEPSTATIC_LIBBLKID) $(DEPSTATIC_LIBUUID)
+
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+all:: $(SMANPAGES) blkid.pc
+
+blkid_types.h: $(srcdir)/blkid_types.h.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); \
+ CONFIG_FILES=$(my_dir)/blkid_types.h ./config.status
+
+blkid.h: $(srcdir)/blkid.h.in
+ $(E) " CP $@"
+ $(Q) cp $(srcdir)/blkid.h.in blkid.h
+
+libblkid.3: $(DEP_SUBSTITUTE) $(srcdir)/libblkid.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/libblkid.3.in libblkid.3
+
+tst_cache: $(srcdir)/cache.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_cache -DTEST_PROGRAM $(srcdir)/cache.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_dev: $(srcdir)/dev.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_dev -DTEST_PROGRAM $(srcdir)/dev.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_devname: $(srcdir)/devname.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_devname -DTEST_PROGRAM $(srcdir)/devname.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_devno: $(srcdir)/devno.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_devno -DTEST_PROGRAM $(srcdir)/devno.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_getsize: $(srcdir)/getsize.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_getsize -DTEST_PROGRAM $(srcdir)/getsize.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_probe: $(srcdir)/probe.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_probe -DTEST_PROGRAM $(srcdir)/probe.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_read: $(srcdir)/read.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_read -DTEST_PROGRAM $(srcdir)/read.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_resolve: $(srcdir)/resolve.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_resolve -DTEST_PROGRAM $(srcdir)/resolve.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_save: $(srcdir)/save.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_save -DTEST_PROGRAM $(srcdir)/save.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_tag: $(srcdir)/tag.c $(DEPLIBS_BLKID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_tag -DTEST_PROGRAM $(srcdir)/tag.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_types: tst_types.o blkid_types.h
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_types $(ALL_LDFLAGS) tst_types.o
+
+../../misc/blkid.o: $(top_srcdir)/misc/blkid.c blkid.h
+ $(E) " CC $@"
+ $(Q) $(CC) $(ALL_CFLAGS) -c $(top_srcdir)/misc/blkid.c \
+ -o ../../misc/blkid.o
+
+blkid: ../../misc/blkid.o libblkid.a $(DEPLIBUUID)
+ $(E) " LD $@"
+ $(Q) $(CC) -o blkid ../../misc/blkid.o libblkid.a $(LIBUUID)
+
+test_probe: test_probe.in Makefile
+ $(E) "Creating test_probe..."
+ $(E) "#!/bin/sh" > test_probe
+ $(E) "SRCDIR=@srcdir@" >> test_probe
+ $(Q) cat $(srcdir)/test_probe.in >> test_probe
+ $(Q) chmod +x test_probe
+
+fullcheck check:: all tst_cache tst_dev tst_devname tst_devno \
+ tst_getsize tst_probe tst_read tst_resolve tst_save tst_tag \
+ test_probe tst_types
+ ./test_probe
+ ./tst_types
+
+blkid.pc: $(srcdir)/blkid.pc.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); CONFIG_FILES=lib/blkid/blkid.pc ./config.status
+
+installdirs::
+ $(E) " MKDIR_P $(libdir) $(includedir)/blkid"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(includedir)/blkid $(DESTDIR)$(pkgconfigdir)
+
+install:: all installdirs
+ $(E) " INSTALL_DATA $(libdir)/libblkid.a"
+ $(Q) $(INSTALL_DATA) libblkid.a $(DESTDIR)$(libdir)/libblkid.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libblkid.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libblkid.a
+ $(Q) set -e; for i in $(HFILES_IN); do \
+ echo " INSTALL_DATA $(includedir)/blkid/$$i"; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/blkid/$$i; \
+ done
+ $(Q) for i in $(SMANPAGES); do \
+ echo " INSTALL_DATA $(man3dir)/$$i"; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(man3dir)/$$i; \
+ done
+ $(E) " INSTALL_DATA $(pkgconfigdir)/blkid.pc"
+ $(Q) $(INSTALL_DATA) blkid.pc $(DESTDIR)$(pkgconfigdir)/blkid.pc
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/libblkid.a \
+ $(DESTDIR)$(pkgconfigdir)/blkid.pc
+ $(RM) -rf $(DESTDIR)$(includedir)/blkid
+ for i in $(SMANPAGES); do \
+ $(RM) -f $(DESTDIR)$(man3dir)/$$i; \
+ done
+
+clean::
+ $(RM) -f \#* *.s *.o *.orig *.a *~ *.bak tst_cache tst_dev tst_devname \
+ tst_devno tst_getsize tst_probe tst_read tst_resolve tst_save \
+ tst_tag tst_types tests/*.out tests/*.ok \
+ tests/*.img results test_probe core profiled/* \
+ blkid.h blkid_types.h ../libblkid.a ../libblkid_p.a \
+ $(SMANPAGES) blkid blkid.pc
+ @echo rmdir tests/tmp tests
+ @(rmdir tests/tmp tests 2> /dev/null ; exit 0)
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend Makefile blkid.pc \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+$(OBJS): subdirs $(HFILES_IN)
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+cache.o: $(srcdir)/cache.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+dev.o: $(srcdir)/dev.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+devname.o: $(srcdir)/devname.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+devno.o: $(srcdir)/devno.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+getsize.o: $(srcdir)/getsize.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+llseek.o: $(srcdir)/llseek.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+probe.o: $(srcdir)/probe.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h \
+ $(srcdir)/probe.h
+read.o: $(srcdir)/read.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+resolve.o: $(srcdir)/resolve.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+save.o: $(srcdir)/save.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+tag.o: $(srcdir)/tag.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/blkidP.h $(srcdir)/list.h
+version.o: $(srcdir)/version.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/version.h
diff --git a/lib/blkid/blkid.h.in b/lib/blkid/blkid.h.in
new file mode 100644
index 0000000..81f3098
--- /dev/null
+++ b/lib/blkid/blkid.h.in
@@ -0,0 +1,110 @@
+/*
+ * blkid.h - Interface for libblkid, a library to identify block devices
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKID_H
+#define _BLKID_BLKID_H
+
+#include <sys/types.h>
+#include <blkid/blkid_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION "1.0.0"
+#define BLKID_DATE "12-Feb-2003"
+
+typedef struct blkid_struct_dev *blkid_dev;
+typedef struct blkid_struct_cache *blkid_cache;
+typedef __s64 blkid_loff_t;
+
+typedef struct blkid_struct_tag_iterate *blkid_tag_iterate;
+typedef struct blkid_struct_dev_iterate *blkid_dev_iterate;
+
+/*
+ * Flags for blkid_get_dev
+ *
+ * BLKID_DEV_CREATE Create an empty device structure if not found
+ * in the cache.
+ * BLKID_DEV_VERIFY Make sure the device structure corresponds
+ * with reality.
+ * BLKID_DEV_FIND Just look up a device entry, and return NULL
+ * if it is not found.
+ * BLKID_DEV_NORMAL Get a valid device structure, either from the
+ * cache or by probing the device.
+ */
+#define BLKID_DEV_FIND 0x0000
+#define BLKID_DEV_CREATE 0x0001
+#define BLKID_DEV_VERIFY 0x0002
+#define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY)
+
+/* cache.c */
+extern void blkid_put_cache(blkid_cache cache);
+extern int blkid_get_cache(blkid_cache *cache, const char *filename);
+extern void blkid_gc_cache(blkid_cache cache);
+
+/* dev.c */
+extern const char *blkid_dev_devname(blkid_dev dev);
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache);
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+ char *search_type, char *search_value);
+extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev);
+extern void blkid_dev_iterate_end(blkid_dev_iterate iterate);
+
+/* devno.c */
+extern char *blkid_devno_to_devname(dev_t devno);
+
+/* devname.c */
+extern int blkid_probe_all(blkid_cache cache);
+extern int blkid_probe_all_new(blkid_cache cache);
+extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname,
+ int flags);
+
+/* getsize.c */
+extern blkid_loff_t blkid_get_dev_size(int fd);
+
+/* probe.c */
+int blkid_known_fstype(const char *fstype);
+extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev);
+
+/* read.c */
+
+/* resolve.c */
+extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+ const char *devname);
+extern char *blkid_get_devname(blkid_cache cache, const char *token,
+ const char *value);
+
+/* tag.c */
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev);
+extern int blkid_tag_next(blkid_tag_iterate iterate,
+ const char **type, const char **value);
+extern void blkid_tag_iterate_end(blkid_tag_iterate iterate);
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+ const char *value);
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+ const char *type,
+ const char *value);
+extern int blkid_parse_tag_string(const char *token, char **ret_type,
+ char **ret_val);
+
+/* version.c */
+extern int blkid_parse_version_string(const char *ver_string);
+extern int blkid_get_library_version(const char **ver_string,
+ const char **date_string);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKID_H */
diff --git a/lib/blkid/blkid.pc.in b/lib/blkid/blkid.pc.in
new file mode 100644
index 0000000..808fe22
--- /dev/null
+++ b/lib/blkid/blkid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: blkid
+Description: Block device id library
+Version: @E2FSPROGS_VERSION@
+Requires.private: uuid
+Cflags: -I${includedir}/blkid -I${includedir}
+Libs: -L${libdir} -lblkid
diff --git a/lib/blkid/blkidP.h b/lib/blkid/blkidP.h
new file mode 100644
index 0000000..b3fe4a6
--- /dev/null
+++ b/lib/blkid/blkidP.h
@@ -0,0 +1,204 @@
+/*
+ * blkidP.h - Internal interfaces for libblkid
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKIDP_H
+#define _BLKID_BLKIDP_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <blkid/blkid.h>
+
+#include <blkid/list.h>
+
+#ifdef __GNUC__
+#define __BLKID_ATTR(x) __attribute__(x)
+#else
+#define __BLKID_ATTR(x)
+#endif
+
+
+/*
+ * This describes the attributes of a specific device.
+ * We can traverse all of the tags by bid_tags (linking to the tag bit_names).
+ * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag
+ * values, if they exist.
+ */
+struct blkid_struct_dev
+{
+ struct list_head bid_devs; /* All devices in the cache */
+ struct list_head bid_tags; /* All tags for this device */
+ blkid_cache bid_cache; /* Dev belongs to this cache */
+ char *bid_name; /* Device inode pathname */
+ char *bid_type; /* Preferred device TYPE */
+ int bid_pri; /* Device priority */
+ dev_t bid_devno; /* Device major/minor number */
+ time_t bid_time; /* Last update time of device */
+ unsigned int bid_flags; /* Device status bitflags */
+ char *bid_label; /* Shortcut to device LABEL */
+ char *bid_uuid; /* Shortcut to binary UUID */
+};
+
+#define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */
+#define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */
+
+/*
+ * Each tag defines a NAME=value pair for a particular device. The tags
+ * are linked via bit_names for a single device, so that traversing the
+ * names list will get you a list of all tags associated with a device.
+ * They are also linked via bit_values for all devices, so one can easily
+ * search all tags with a given NAME for a specific value.
+ */
+struct blkid_struct_tag
+{
+ struct list_head bit_tags; /* All tags for this device */
+ struct list_head bit_names; /* All tags with given NAME */
+ char *bit_name; /* NAME of tag (shared) */
+ char *bit_val; /* value of tag */
+ blkid_dev bit_dev; /* pointer to device */
+};
+typedef struct blkid_struct_tag *blkid_tag;
+
+/*
+ * Minimum number of seconds between device probes, even when reading
+ * from the cache. This is to avoid re-probing all devices which were
+ * just probed by another program that does not share the cache.
+ */
+#define BLKID_PROBE_MIN 2
+
+/*
+ * Time in seconds an entry remains verified in the in-memory cache
+ * before being reverified (in case of long-running processes that
+ * keep a cache in memory and continue to use it for a long time).
+ */
+#define BLKID_PROBE_INTERVAL 200
+
+/* This describes an entire blkid cache file and probed devices.
+ * We can traverse all of the found devices via bic_list.
+ * We can traverse all of the tag types by bic_tags, which hold empty tags
+ * for each tag type. Those tags can be used as list_heads for iterating
+ * through all devices with a specific tag type (e.g. LABEL).
+ */
+struct blkid_struct_cache
+{
+ struct list_head bic_devs; /* List head of all devices */
+ struct list_head bic_tags; /* List head of all tag types */
+ time_t bic_time; /* Last probe time */
+ time_t bic_ftime; /* Mod time of the cachefile */
+ unsigned int bic_flags; /* Status flags of the cache */
+ char *bic_filename; /* filename of cache */
+};
+
+#define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */
+#define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */
+
+extern char *blkid_strdup(const char *s);
+extern char *blkid_strndup(const char *s, const int length);
+
+#define BLKID_CACHE_FILE "/etc/blkid.tab"
+
+#define BLKID_ERR_IO 5
+#define BLKID_ERR_PROC 9
+#define BLKID_ERR_MEM 12
+#define BLKID_ERR_CACHE 14
+#define BLKID_ERR_DEV 19
+#define BLKID_ERR_PARAM 22
+#define BLKID_ERR_BIG 27
+
+/*
+ * Priority settings for different types of devices
+ */
+#define BLKID_PRI_DM 40
+#define BLKID_PRI_EVMS 30
+#define BLKID_PRI_LVM 20
+#define BLKID_PRI_MD 10
+
+#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG)
+#define CONFIG_BLKID_DEBUG
+#endif
+
+#define DEBUG_CACHE 0x0001
+#define DEBUG_DUMP 0x0002
+#define DEBUG_DEV 0x0004
+#define DEBUG_DEVNAME 0x0008
+#define DEBUG_DEVNO 0x0010
+#define DEBUG_PROBE 0x0020
+#define DEBUG_READ 0x0040
+#define DEBUG_RESOLVE 0x0080
+#define DEBUG_SAVE 0x0100
+#define DEBUG_TAG 0x0200
+#define DEBUG_INIT 0x8000
+#define DEBUG_ALL 0xFFFF
+
+#ifdef CONFIG_BLKID_DEBUG
+#include <stdio.h>
+extern int blkid_debug_mask;
+#define DBG(m,x) if ((m) & blkid_debug_mask) x;
+#else
+#define DBG(m,x)
+#endif
+
+#ifdef CONFIG_BLKID_DEBUG
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+#endif
+
+static inline int blkidP_is_disk_device(mode_t mode)
+{
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ return S_ISBLK(mode) || S_ISCHR(mode);
+#else
+ return S_ISBLK(mode);
+#endif
+}
+
+/* devno.c */
+struct dir_list {
+ char *name;
+ struct dir_list *next;
+};
+extern void blkid__scan_dir(const char *, dev_t, struct dir_list **, char **);
+
+/* lseek.c */
+extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence);
+
+/* read.c */
+extern void blkid_read_cache(blkid_cache cache);
+
+/* save.c */
+extern int blkid_flush_cache(blkid_cache cache);
+
+/*
+ * Functions to create and find a specific tag type: tag.c
+ */
+extern void blkid_free_tag(blkid_tag tag);
+extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type);
+extern int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength);
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev blkid_new_dev(void);
+extern void blkid_free_dev(blkid_dev dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKIDP_H */
diff --git a/lib/blkid/blkid_types.h.in b/lib/blkid/blkid_types.h.in
new file mode 100644
index 0000000..d4c81d0
--- /dev/null
+++ b/lib/blkid/blkid_types.h.in
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+#if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \
+ !defined(_EXT2_TYPES_H))
+#define _BLKID_TYPES_H
+
+@ASM_TYPES_HEADER@
+
+#ifndef HAVE___U8
+#define HAVE___U8
+#ifdef __U8_TYPEDEF
+typedef __U8_TYPEDEF __u8;
+#else
+typedef unsigned char __u8;
+#endif
+#endif /* HAVE___U8 */
+
+#ifndef HAVE___S8
+#define HAVE___S8
+#ifdef __S8_TYPEDEF
+typedef __S8_TYPEDEF __s8;
+#else
+typedef signed char __s8;
+#endif
+#endif /* HAVE___S8 */
+
+#ifndef HAVE___U16
+#define HAVE___U16
+#ifdef __U16_TYPEDEF
+typedef __U16_TYPEDEF __u16;
+#else
+#if (@SIZEOF_INT@ == 2)
+typedef unsigned int __u16;
+#else
+#if (@SIZEOF_SHORT@ == 2)
+typedef unsigned short __u16;
+#else
+#undef HAVE___U16
+ ?==error: undefined 16 bit type
+#endif /* SIZEOF_SHORT == 2 */
+#endif /* SIZEOF_INT == 2 */
+#endif /* __U16_TYPEDEF */
+#endif /* HAVE___U16 */
+
+#ifndef HAVE___S16
+#define HAVE___S16
+#ifdef __S16_TYPEDEF
+typedef __S16_TYPEDEF __s16;
+#else
+#if (@SIZEOF_INT@ == 2)
+typedef int __s16;
+#else
+#if (@SIZEOF_SHORT@ == 2)
+typedef short __s16;
+#else
+#undef HAVE___S16
+ ?==error: undefined 16 bit type
+#endif /* SIZEOF_SHORT == 2 */
+#endif /* SIZEOF_INT == 2 */
+#endif /* __S16_TYPEDEF */
+#endif /* HAVE___S16 */
+
+#ifndef HAVE___U32
+#define HAVE___U32
+#ifdef __U32_TYPEDEF
+typedef __U32_TYPEDEF __u32;
+#else
+#if (@SIZEOF_INT@ == 4)
+typedef unsigned int __u32;
+#else
+#if (@SIZEOF_LONG@ == 4)
+typedef unsigned long __u32;
+#else
+#if (@SIZEOF_SHORT@ == 4)
+typedef unsigned short __u32;
+#else
+#undef HAVE___U32
+ ?== error: undefined 32 bit type
+#endif /* SIZEOF_SHORT == 4 */
+#endif /* SIZEOF_LONG == 4 */
+#endif /* SIZEOF_INT == 4 */
+#endif /* __U32_TYPEDEF */
+#endif /* HAVE___U32 */
+
+#ifndef HAVE___S32
+#define HAVE___S32
+#ifdef __S32_TYPEDEF
+typedef __S32_TYPEDEF __s32;
+#else
+#if (@SIZEOF_INT@ == 4)
+typedef int __s32;
+#else
+#if (@SIZEOF_LONG@ == 4)
+typedef long __s32;
+#else
+#if (@SIZEOF_SHORT@ == 4)
+typedef short __s32;
+#else
+#undef HAVE___S32
+ ?== error: undefined 32 bit type
+#endif /* SIZEOF_SHORT == 4 */
+#endif /* SIZEOF_LONG == 4 */
+#endif /* SIZEOF_INT == 4 */
+#endif /* __S32_TYPEDEF */
+#endif /* HAVE___S32 */
+
+#ifndef HAVE___U64
+#define HAVE___U64
+#ifdef __U64_TYPEDEF
+typedef __U64_TYPEDEF __u64;
+#else
+#if (@SIZEOF_INT@ == 8)
+typedef unsigned int __u64;
+#else
+#if (@SIZEOF_LONG_LONG@ == 8)
+typedef unsigned long long __u64;
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef unsigned long __u64;
+#else
+#undef HAVE___U64
+ ?== error: undefined 64 bit type
+#endif /* SIZEOF_LONG == 8 */
+#endif /* SIZEOF_LONG_LONG == 8 */
+#endif /* SIZEOF_INT == 8 */
+#endif /* __U64_TYPEDEF */
+#endif /* HAVE___U64 */
+
+#ifndef HAVE___S64
+#define HAVE___S64
+#ifdef __S64_TYPEDEF
+typedef __S64_TYPEDEF __s64;
+#else
+#if (@SIZEOF_INT@ == 8)
+typedef int __s64;
+#else
+#if (@SIZEOF_LONG_LONG@ == 8)
+#if defined(__GNUC__)
+typedef __signed__ long long __s64;
+#else
+typedef signed long long __s64;
+#endif /* __GNUC__ */
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef long __s64;
+#else
+#undef HAVE___S64
+ ?== error: undefined 64 bit type
+#endif /* SIZEOF_LONG == 8 */
+#endif /* SIZEOF_LONG_LONG == 8 */
+#endif /* SIZEOF_INT == 8 */
+#endif /* __S64_TYPEDEF */
+#endif /* HAVE___S64 */
+
+#undef __S8_TYPEDEF
+#undef __U8_TYPEDEF
+#undef __S16_TYPEDEF
+#undef __U16_TYPEDEF
+#undef __S32_TYPEDEF
+#undef __U32_TYPEDEF
+#undef __S64_TYPEDEF
+#undef __U64_TYPEDEF
+
+#endif /* _*_TYPES_H */
diff --git a/lib/blkid/cache.c b/lib/blkid/cache.c
new file mode 100644
index 0000000..80c8089
--- /dev/null
+++ b/lib/blkid/cache.c
@@ -0,0 +1,209 @@
+/*
+ * cache.c - allocation/initialization/free routines for cache
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include "blkidP.h"
+
+int blkid_debug_mask = 0;
+
+
+static char *safe_getenv(const char *arg)
+{
+ if ((getuid() != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#if HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
+
+#if 0 /* ifdef CONFIG_BLKID_DEBUG */
+static blkid_debug_dump_cache(int mask, blkid_cache cache)
+{
+ struct list_head *p;
+
+ if (!cache) {
+ printf("cache: NULL\n");
+ return;
+ }
+
+ printf("cache: time = %lu\n", cache->bic_time);
+ printf("cache: flags = 0x%08X\n", cache->bic_flags);
+
+ list_for_each(p, &cache->bic_devs) {
+ blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+ blkid_debug_dump_dev(dev);
+ }
+}
+#endif
+
+int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
+{
+ blkid_cache cache;
+
+#ifdef CONFIG_BLKID_DEBUG
+ if (!(blkid_debug_mask & DEBUG_INIT)) {
+ char *dstr = getenv("BLKID_DEBUG");
+
+ if (dstr)
+ blkid_debug_mask = strtoul(dstr, 0, 0);
+ blkid_debug_mask |= DEBUG_INIT;
+ }
+#endif
+
+ DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
+ filename ? filename : "default cache"));
+
+ if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
+ return -BLKID_ERR_MEM;
+
+ INIT_LIST_HEAD(&cache->bic_devs);
+ INIT_LIST_HEAD(&cache->bic_tags);
+
+ if (filename && !strlen(filename))
+ filename = 0;
+ if (!filename)
+ filename = safe_getenv("BLKID_FILE");
+ if (!filename)
+ filename = BLKID_CACHE_FILE;
+ cache->bic_filename = blkid_strdup(filename);
+
+ blkid_read_cache(cache);
+
+ *ret_cache = cache;
+ return 0;
+}
+
+void blkid_put_cache(blkid_cache cache)
+{
+ if (!cache)
+ return;
+
+ (void) blkid_flush_cache(cache);
+
+ DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
+
+ /* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
+
+ while (!list_empty(&cache->bic_devs)) {
+ blkid_dev dev = list_entry(cache->bic_devs.next,
+ struct blkid_struct_dev,
+ bid_devs);
+ blkid_free_dev(dev);
+ }
+
+ while (!list_empty(&cache->bic_tags)) {
+ blkid_tag tag = list_entry(cache->bic_tags.next,
+ struct blkid_struct_tag,
+ bit_tags);
+
+ while (!list_empty(&tag->bit_names)) {
+ blkid_tag bad = list_entry(tag->bit_names.next,
+ struct blkid_struct_tag,
+ bit_names);
+
+ DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
+ bad->bit_name, bad->bit_val));
+ blkid_free_tag(bad);
+ }
+ blkid_free_tag(tag);
+ }
+ free(cache->bic_filename);
+
+ free(cache);
+}
+
+void blkid_gc_cache(blkid_cache cache)
+{
+ struct list_head *p, *pnext;
+ struct stat st;
+
+ if (!cache)
+ return;
+
+ list_for_each_safe(p, pnext, &cache->bic_devs) {
+ blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (stat(dev->bid_name, &st) < 0) {
+ DBG(DEBUG_CACHE,
+ printf("freeing %s\n", dev->bid_name));
+ blkid_free_dev(dev);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ } else {
+ DBG(DEBUG_CACHE,
+ printf("Device %s exists\n", dev->bid_name));
+ }
+ }
+}
+
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if ((argc > 2)) {
+ fprintf(stderr, "Usage: %s [filename] \n", argv[0]);
+ exit(1);
+ }
+
+ if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
+ fprintf(stderr, "error %d parsing cache file %s\n", ret,
+ argv[1] ? argv[1] : BLKID_CACHE_FILE);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ if ((ret = blkid_probe_all(cache) < 0))
+ fprintf(stderr, "error probing devices\n");
+
+ blkid_put_cache(cache);
+
+ return ret;
+}
+#endif
diff --git a/lib/blkid/dev.c b/lib/blkid/dev.c
new file mode 100644
index 0000000..1d62dd8
--- /dev/null
+++ b/lib/blkid/dev.c
@@ -0,0 +1,254 @@
+/*
+ * dev.c - allocation/initialization/free routines for dev
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "blkidP.h"
+
+blkid_dev blkid_new_dev(void)
+{
+ blkid_dev dev;
+
+ if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
+ return NULL;
+
+ INIT_LIST_HEAD(&dev->bid_devs);
+ INIT_LIST_HEAD(&dev->bid_tags);
+
+ return dev;
+}
+
+void blkid_free_dev(blkid_dev dev)
+{
+ if (!dev)
+ return;
+
+ DBG(DEBUG_DEV,
+ printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type ?
+ dev->bid_type : "(null)"));
+ DBG(DEBUG_DEV, blkid_debug_dump_dev(dev));
+
+ list_del(&dev->bid_devs);
+ while (!list_empty(&dev->bid_tags)) {
+ blkid_tag tag = list_entry(dev->bid_tags.next,
+ struct blkid_struct_tag,
+ bit_tags);
+ blkid_free_tag(tag);
+ }
+ free(dev->bid_name);
+ free(dev);
+}
+
+/*
+ * Given a blkid device, return its name
+ */
+extern const char *blkid_dev_devname(blkid_dev dev)
+{
+ return dev->bid_name;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+ struct list_head *p;
+
+ if (!dev) {
+ printf(" dev: NULL\n");
+ return;
+ }
+
+ printf(" dev: name = %s\n", dev->bid_name);
+ printf(" dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+ printf(" dev: TIME=\"%ld\"\n", (long)dev->bid_time);
+ printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
+ printf(" dev: flags = 0x%08X\n", dev->bid_flags);
+
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+ if (tag)
+ printf(" tag: %s=\"%s\"\n", tag->bit_name,
+ tag->bit_val);
+ else
+ printf(" tag: NULL\n");
+ }
+ printf("\n");
+}
+#endif
+
+/*
+ * dev iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implementation. I'm not convinced I want
+ * to keep list.h in the long term, anyway. It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application. [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all devices in a blkid cache
+ */
+#define DEV_ITERATE_MAGIC 0x01a5284c
+
+struct blkid_struct_dev_iterate {
+ int magic;
+ blkid_cache cache;
+ char *search_type;
+ char *search_value;
+ struct list_head *p;
+};
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+ blkid_dev_iterate iter;
+
+ iter = malloc(sizeof(struct blkid_struct_dev_iterate));
+ if (iter) {
+ iter->magic = DEV_ITERATE_MAGIC;
+ iter->cache = cache;
+ iter->p = cache->bic_devs.next;
+ iter->search_type = 0;
+ iter->search_value = 0;
+ }
+ return (iter);
+}
+
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+ char *search_type, char *search_value)
+{
+ char *new_type, *new_value;
+
+ if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type ||
+ !search_value)
+ return -1;
+ new_type = malloc(strlen(search_type)+1);
+ new_value = malloc(strlen(search_value)+1);
+ if (!new_type || !new_value) {
+ free(new_type);
+ free(new_value);
+ return -1;
+ }
+ strcpy(new_type, search_type);
+ strcpy(new_value, search_value);
+ free(iter->search_type);
+ free(iter->search_value);
+ iter->search_type = new_type;
+ iter->search_value = new_value;
+ return 0;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_dev_next(blkid_dev_iterate iter,
+ blkid_dev *ret_dev)
+{
+ blkid_dev dev;
+
+ *ret_dev = 0;
+ if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+ return -1;
+ while (iter->p != &iter->cache->bic_devs) {
+ dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
+ iter->p = iter->p->next;
+ if (iter->search_type &&
+ !blkid_dev_has_tag(dev, iter->search_type,
+ iter->search_value))
+ continue;
+ *ret_dev = dev;
+ return 0;
+ }
+ return -1;
+}
+
+extern void blkid_dev_iterate_end(blkid_dev_iterate iter)
+{
+ if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+ return;
+ iter->magic = 0;
+ free(iter);
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog);
+ fprintf(stderr, "\tList all devices and exit\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ blkid_dev_iterate iter;
+ blkid_cache cache = NULL;
+ blkid_dev dev;
+ int c, ret;
+ char *tmp;
+ char *file = NULL;
+ char *search_type = NULL;
+ char *search_value = NULL;
+
+ while ((c = getopt (argc, argv, "m:f:")) != EOF)
+ switch (c) {
+ case 'f':
+ file = optarg;
+ break;
+ case 'm':
+ blkid_debug_mask = strtoul (optarg, &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, "Invalid debug mask: %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ if (argc >= optind+2) {
+ search_type = argv[optind];
+ search_value = argv[optind+1];
+ optind += 2;
+ }
+ if (argc != optind)
+ usage(argv[0]);
+
+ if ((ret = blkid_get_cache(&cache, file)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+
+ iter = blkid_dev_iterate_begin(cache);
+ if (search_type)
+ blkid_dev_set_search(iter, search_type, search_value);
+ while (blkid_dev_next(iter, &dev) == 0) {
+ printf("Device: %s\n", blkid_dev_devname(dev));
+ }
+ blkid_dev_iterate_end(iter);
+
+
+ blkid_put_cache(cache);
+ return (0);
+}
+#endif
diff --git a/lib/blkid/devname.c b/lib/blkid/devname.c
new file mode 100644
index 0000000..6a21963
--- /dev/null
+++ b/lib/blkid/devname.c
@@ -0,0 +1,561 @@
+/*
+ * devname.c - get a dev by its device inode name
+ *
+ * Copyright (C) Andries Brouwer
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _GNU_SOURCE 1
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <dirent.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+#include <time.h>
+
+#include "blkidP.h"
+
+/*
+ * Find a dev struct in the cache by device name, if available.
+ *
+ * If there is no entry with the specified device name, and the create
+ * flag is set, then create an empty device entry.
+ */
+blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+{
+ blkid_dev dev = NULL, tmp;
+ struct list_head *p, *pnext;
+
+ if (!cache || !devname)
+ return NULL;
+
+ list_for_each(p, &cache->bic_devs) {
+ tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (strcmp(tmp->bid_name, devname))
+ continue;
+
+ DBG(DEBUG_DEVNAME,
+ printf("found devname %s in cache\n", tmp->bid_name));
+ dev = tmp;
+ break;
+ }
+
+ if (!dev && (flags & BLKID_DEV_CREATE)) {
+ if (access(devname, F_OK) < 0)
+ return NULL;
+ dev = blkid_new_dev();
+ if (!dev)
+ return NULL;
+ dev->bid_time = INT_MIN;
+ dev->bid_name = blkid_strdup(devname);
+ dev->bid_cache = cache;
+ list_add_tail(&dev->bid_devs, &cache->bic_devs);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ }
+
+ if (flags & BLKID_DEV_VERIFY) {
+ dev = blkid_verify(cache, dev);
+ if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED))
+ return dev;
+ /*
+ * If the device is verified, then search the blkid
+ * cache for any entries that match on the type, uuid,
+ * and label, and verify them; if a cache entry can
+ * not be verified, then it's stale and so we remove
+ * it.
+ */
+ list_for_each_safe(p, pnext, &cache->bic_devs) {
+ blkid_dev dev2;
+ dev2 = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (dev2->bid_flags & BLKID_BID_FL_VERIFIED)
+ continue;
+ if (!dev->bid_type || !dev2->bid_type ||
+ strcmp(dev->bid_type, dev2->bid_type))
+ continue;
+ if (dev->bid_label && dev2->bid_label &&
+ strcmp(dev->bid_label, dev2->bid_label))
+ continue;
+ if (dev->bid_uuid && dev2->bid_uuid &&
+ strcmp(dev->bid_uuid, dev2->bid_uuid))
+ continue;
+ if ((dev->bid_label && !dev2->bid_label) ||
+ (!dev->bid_label && dev2->bid_label) ||
+ (dev->bid_uuid && !dev2->bid_uuid) ||
+ (!dev->bid_uuid && dev2->bid_uuid))
+ continue;
+ dev2 = blkid_verify(cache, dev2);
+ if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED))
+ blkid_free_dev(dev2);
+ }
+ }
+ return dev;
+}
+
+/* Directories where we will try to search for device names */
+static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL };
+
+static int is_dm_leaf(const char *devname)
+{
+ struct dirent *de, *d_de;
+ DIR *dir, *d_dir;
+ char path[300];
+ int ret = 1;
+
+ if ((dir = opendir("/sys/block")) == NULL)
+ return 0;
+ while ((de = readdir(dir)) != NULL) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+ !strcmp(de->d_name, devname) ||
+ strncmp(de->d_name, "dm-", 3) ||
+ strlen(de->d_name) > sizeof(path)-32)
+ continue;
+ sprintf(path, "/sys/block/%s/slaves", de->d_name);
+ if ((d_dir = opendir(path)) == NULL)
+ continue;
+ while ((d_de = readdir(d_dir)) != NULL) {
+ if (!strcmp(d_de->d_name, devname)) {
+ ret = 0;
+ break;
+ }
+ }
+ closedir(d_dir);
+ if (!ret)
+ break;
+ }
+ closedir(dir);
+ return ret;
+}
+
+/*
+ * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
+ * provides the real DM device names in /sys/block/<ptname>/dm/name
+ */
+static char *get_dm_name(const char *ptname)
+{
+ FILE *f;
+ size_t sz;
+ char path[300], name[256], *res = NULL;
+
+ snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
+ if ((f = fopen(path, "r")) == NULL)
+ return NULL;
+
+ /* read "<name>\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 = blkid_strdup(path);
+ }
+ fclose(f);
+ return res;
+}
+
+/*
+ * Probe a single block device to add to the device cache.
+ */
+static void probe_one(blkid_cache cache, const char *ptname,
+ dev_t devno, int pri, int only_if_new)
+{
+ blkid_dev dev = NULL;
+ struct list_head *p, *pnext;
+ const char **dir;
+ char *devname = NULL;
+
+ /* See if we already have this device number in the cache. */
+ list_for_each_safe(p, pnext, &cache->bic_devs) {
+ blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
+ bid_devs);
+ if (tmp->bid_devno == devno) {
+ if (only_if_new && !access(tmp->bid_name, F_OK))
+ return;
+ dev = blkid_verify(cache, tmp);
+ if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
+ break;
+ dev = 0;
+ }
+ }
+ if (dev && dev->bid_devno == devno)
+ goto set_pri;
+
+ /* Try to translate private device-mapper dm-<N> names
+ * to standard /dev/mapper/<name>.
+ */
+ if (!strncmp(ptname, "dm-", 3) && isdigit(ptname[3])) {
+ devname = get_dm_name(ptname);
+ if (!devname)
+ blkid__scan_dir("/dev/mapper", devno, 0, &devname);
+ if (devname)
+ goto get_dev;
+ }
+
+ /*
+ * Take a quick look at /dev/ptname for the device number. We check
+ * all of the likely device directories. If we don't find it, or if
+ * the stat information doesn't check out, use blkid_devno_to_devname()
+ * to find it via an exhaustive search for the device major/minor.
+ */
+ for (dir = dirlist; *dir; dir++) {
+ struct stat st;
+ char device[256];
+
+ sprintf(device, "%s/%s", *dir, ptname);
+ if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
+ dev->bid_devno == devno)
+ goto set_pri;
+
+ if (stat(device, &st) == 0 &&
+ blkidP_is_disk_device(st.st_mode) &&
+ st.st_rdev == devno) {
+ devname = blkid_strdup(device);
+ goto get_dev;
+ }
+ }
+ /* Do a short-cut scan of /dev/mapper first */
+ if (!devname)
+ devname = get_dm_name(ptname);
+ if (!devname)
+ blkid__scan_dir("/dev/mapper", devno, 0, &devname);
+ if (!devname) {
+ devname = blkid_devno_to_devname(devno);
+ if (!devname)
+ return;
+ }
+get_dev:
+ dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
+ free(devname);
+set_pri:
+ if (dev) {
+ if (pri)
+ dev->bid_pri = pri;
+ else if (!strncmp(dev->bid_name, "/dev/mapper/", 11)) {
+ dev->bid_pri = BLKID_PRI_DM;
+ if (is_dm_leaf(ptname))
+ dev->bid_pri += 5;
+ } else if (!strncmp(ptname, "md", 2))
+ dev->bid_pri = BLKID_PRI_MD;
+ }
+ return;
+}
+
+#define PROC_PARTITIONS "/proc/partitions"
+#define VG_DIR "/proc/lvm/VGs"
+
+/*
+ * This function initializes the UUID cache with devices from the LVM
+ * proc hierarchy. We currently depend on the names of the LVM
+ * hierarchy giving us the device structure in /dev. (XXX is this a
+ * safe thing to do?)
+ */
+#ifdef VG_DIR
+static dev_t lvm_get_devno(const char *lvm_device)
+{
+ FILE *lvf;
+ char buf[1024];
+ int ma, mi;
+ dev_t ret = 0;
+
+ DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
+ if ((lvf = fopen(lvm_device, "r")) == NULL) {
+ DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
+ strerror(errno)));
+ return 0;
+ }
+
+ while (fgets(buf, sizeof(buf), lvf)) {
+ if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
+ ret = makedev(ma, mi);
+ break;
+ }
+ }
+ fclose(lvf);
+
+ return ret;
+}
+
+static void lvm_probe_all(blkid_cache cache, int only_if_new)
+{
+ DIR *vg_list;
+ struct dirent *vg_iter;
+ int vg_len = strlen(VG_DIR);
+ dev_t dev;
+
+ if ((vg_list = opendir(VG_DIR)) == NULL)
+ return;
+
+ DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
+
+ while ((vg_iter = readdir(vg_list)) != NULL) {
+ DIR *lv_list;
+ char *vdirname;
+ char *vg_name;
+ struct dirent *lv_iter;
+
+ vg_name = vg_iter->d_name;
+ if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
+ continue;
+ vdirname = malloc(vg_len + strlen(vg_name) + 8);
+ if (!vdirname)
+ goto exit;
+ sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
+
+ lv_list = opendir(vdirname);
+ free(vdirname);
+ if (lv_list == NULL)
+ continue;
+
+ while ((lv_iter = readdir(lv_list)) != NULL) {
+ char *lv_name, *lvm_device;
+
+ lv_name = lv_iter->d_name;
+ if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
+ continue;
+
+ lvm_device = malloc(vg_len + strlen(vg_name) +
+ strlen(lv_name) + 8);
+ if (!lvm_device) {
+ closedir(lv_list);
+ goto exit;
+ }
+ sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+ lv_name);
+ dev = lvm_get_devno(lvm_device);
+ sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+ DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
+ lvm_device,
+ (unsigned int) dev));
+ probe_one(cache, lvm_device, dev, BLKID_PRI_LVM,
+ only_if_new);
+ free(lvm_device);
+ }
+ closedir(lv_list);
+ }
+exit:
+ closedir(vg_list);
+}
+#endif
+
+#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
+
+static int
+evms_probe_all(blkid_cache cache, int only_if_new)
+{
+ char line[100];
+ int ma, mi, sz, num = 0;
+ FILE *procpt;
+ char device[110];
+
+ procpt = fopen(PROC_EVMS_VOLUMES, "r");
+ if (!procpt)
+ return 0;
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
+ &ma, &mi, &sz, device) != 4)
+ continue;
+
+ DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
+ device, ma, mi));
+
+ probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS,
+ only_if_new);
+ num++;
+ }
+ fclose(procpt);
+ return num;
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+static int probe_all(blkid_cache cache, int only_if_new)
+{
+ FILE *proc;
+ char line[1024];
+ char ptname0[129], ptname1[129], *ptname = 0;
+ char *ptnames[2];
+ dev_t devs[2];
+ int ma, mi;
+ unsigned long long sz;
+ int lens[2] = { 0, 0 };
+ int which = 0, last = 0;
+ struct list_head *p, *pnext;
+
+ ptnames[0] = ptname0;
+ ptnames[1] = ptname1;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+ time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
+ return 0;
+
+ blkid_read_cache(cache);
+ evms_probe_all(cache, only_if_new);
+#ifdef VG_DIR
+ lvm_probe_all(cache, only_if_new);
+#endif
+
+ proc = fopen(PROC_PARTITIONS, "r");
+ if (!proc)
+ return -BLKID_ERR_PROC;
+
+ while (fgets(line, sizeof(line), proc)) {
+ last = which;
+ which ^= 1;
+ ptname = ptnames[which];
+
+ if (sscanf(line, " %d %d %llu %128[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+ devs[which] = makedev(ma, mi);
+
+ DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
+
+ /* Skip whole disk devs unless they have no partitions.
+ * If base name of device has changed, also
+ * check previous dev to see if it didn't have a partn.
+ * heuristic: partition name ends in a digit, & partition
+ * names contain whole device name as substring.
+ *
+ * Skip extended partitions.
+ * heuristic: size is 1
+ *
+ * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+ */
+
+ lens[which] = strlen(ptname);
+
+ /* ends in a digit, clearly a partition, so check */
+ if (isdigit(ptname[lens[which] - 1])) {
+ DBG(DEBUG_DEVNAME,
+ printf("partition dev %s, devno 0x%04X\n",
+ ptname, (unsigned int) devs[which]));
+
+ if (sz > 1)
+ probe_one(cache, ptname, devs[which], 0,
+ only_if_new);
+ lens[which] = 0; /* mark as checked */
+ }
+
+ /*
+ * If last was a whole disk and we just found a partition
+ * on it, remove the whole-disk dev from the cache if
+ * it exists.
+ */
+ if (lens[last] && !strncmp(ptnames[last], ptname, lens[last])) {
+ list_for_each_safe(p, pnext, &cache->bic_devs) {
+ blkid_dev tmp;
+
+ /* find blkid dev for the whole-disk devno */
+ tmp = list_entry(p, struct blkid_struct_dev,
+ bid_devs);
+ if (tmp->bid_devno == devs[last]) {
+ DBG(DEBUG_DEVNAME,
+ printf("freeing %s\n",
+ tmp->bid_name));
+ blkid_free_dev(tmp);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ break;
+ }
+ }
+ lens[last] = 0;
+ }
+ /*
+ * If last was not checked because it looked like a whole-disk
+ * dev, and the device's base name has changed,
+ * check last as well.
+ */
+ if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) {
+ DBG(DEBUG_DEVNAME,
+ printf("whole dev %s, devno 0x%04X\n",
+ ptnames[last], (unsigned int) devs[last]));
+ probe_one(cache, ptnames[last], devs[last], 0,
+ only_if_new);
+ lens[last] = 0;
+ }
+ }
+
+ /* Handle the last device if it wasn't partitioned */
+ if (lens[which])
+ probe_one(cache, ptname, devs[which], 0, only_if_new);
+
+ fclose(proc);
+ blkid_flush_cache(cache);
+ return 0;
+}
+
+int blkid_probe_all(blkid_cache cache)
+{
+ int ret;
+
+ DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n"));
+ ret = probe_all(cache, 0);
+ cache->bic_time = time(0);
+ cache->bic_flags |= BLKID_BIC_FL_PROBED;
+ DBG(DEBUG_PROBE, printf("End blkid_probe_all()\n"));
+ return ret;
+}
+
+int blkid_probe_all_new(blkid_cache cache)
+{
+ int ret;
+
+ DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n"));
+ ret = probe_all(cache, 1);
+ DBG(DEBUG_PROBE, printf("End blkid_probe_all_new()\n"));
+ return ret;
+}
+
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s\n"
+ "Probe all devices and exit\n", argv[0]);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ if (blkid_probe_all(cache) < 0)
+ printf("%s: error probing devices\n", argv[0]);
+
+ blkid_put_cache(cache);
+ return (0);
+}
+#endif
diff --git a/lib/blkid/devno.c b/lib/blkid/devno.c
new file mode 100644
index 0000000..b1cadc9
--- /dev/null
+++ b/lib/blkid/devno.c
@@ -0,0 +1,242 @@
+/*
+ * devno.c - find a particular device by its device number (major/minor)
+ *
+ * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+
+#include "blkidP.h"
+
+#if defined(__GNUC__) && __GNUC__ >= 8
+/* gcc incorrectly thinks the destination string is not being null-terminated */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-truncation"
+#endif
+
+char *blkid_strndup(const char *s, int length)
+{
+ char *ret;
+
+ if (!s)
+ return NULL;
+
+ if (!length)
+ length = strlen(s);
+
+ ret = malloc(length + 1);
+ if (ret) {
+ strncpy(ret, s, length);
+ ret[length] = '\0';
+ }
+ return ret;
+}
+
+#if defined(__GNUC__) && __GNUC__ >= 8
+#pragma GCC diagnostic pop
+#endif
+
+char *blkid_strdup(const char *s)
+{
+ return blkid_strndup(s, 0);
+}
+
+/*
+ * 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 = blkid_strdup(name);
+ if (!dp->name) {
+ free(dp);
+ return;
+ }
+ 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 = NULL;
+}
+
+void blkid__scan_dir(const char *dirname, dev_t devno, struct dir_list **list,
+ char **devname)
+{
+ DIR *dir;
+ struct dirent *dp;
+ char path[1024];
+ int dirlen;
+ struct stat st;
+
+ if ((dir = opendir(dirname)) == NULL)
+ return;
+ dirlen = strlen(dirname) + 2;
+ while ((dp = readdir(dir)) != 0) {
+ if (dirlen + strlen(dp->d_name) >= sizeof(path))
+ continue;
+
+ if (dp->d_name[0] == '.' &&
+ ((dp->d_name[1] == 0) ||
+ ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+ continue;
+
+ sprintf(path, "%s/%s", dirname, dp->d_name);
+ if (stat(path, &st) < 0)
+ continue;
+
+ if (blkidP_is_disk_device(st.st_mode) && st.st_rdev == devno) {
+ *devname = blkid_strdup(path);
+ DBG(DEBUG_DEVNO,
+ printf("found 0x%llx at %s (%p)\n", (long long)devno,
+ path, *devname));
+ break;
+ }
+ if (list && S_ISDIR(st.st_mode) && !lstat(path, &st) &&
+ S_ISDIR(st.st_mode))
+ add_to_dirlist(path, list);
+ }
+ closedir(dir);
+ return;
+}
+
+/* Directories where we will try to search for device numbers */
+static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL };
+
+/*
+ * 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 *blkid_devno_to_devname(dev_t devno)
+{
+ struct dir_list *list = NULL, *new_list = NULL;
+ char *devname = NULL;
+ const char **dir;
+
+ /*
+ * Add the starting directories to search in reverse order of
+ * importance, since we are using a stack...
+ */
+ for (dir = devdirs; *dir; dir++)
+ add_to_dirlist(*dir, &list);
+
+ while (list) {
+ struct dir_list *current = list;
+
+ list = list->next;
+ DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
+ blkid__scan_dir(current->name, devno, &new_list, &devname);
+ free(current->name);
+ free(current);
+ if (devname)
+ break;
+ /*
+ * If we're done checking at this level, descend to
+ * the next level of subdirectories. (breadth-first)
+ */
+ if (list == NULL) {
+ list = new_list;
+ new_list = NULL;
+ }
+ }
+ free_dirlist(&list);
+ free_dirlist(&new_list);
+
+ if (!devname) {
+ DBG(DEBUG_DEVNO,
+ printf("blkid: couldn't find devno 0x%04lx\n",
+ (unsigned long) devno));
+ } else {
+ DBG(DEBUG_DEVNO,
+ printf("found devno 0x%04llx as %s\n", (long long)devno, devname));
+ }
+
+
+ return devname;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ char *devname, *tmp;
+ int major, minor;
+ dev_t devno;
+ const char *errmsg = "Couldn't parse %s: %s\n";
+
+ blkid_debug_mask = DEBUG_ALL;
+ if ((argc != 2) && (argc != 3)) {
+ fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
+ "Resolve a device number to a device name\n",
+ argv[0], argv[0]);
+ exit(1);
+ }
+ if (argc == 2) {
+ devno = 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);
+ }
+ devno = makedev(major, minor);
+ }
+ printf("Looking for device 0x%04llx\n", (long long)devno);
+ devname = blkid_devno_to_devname(devno);
+ free(devname);
+ return 0;
+}
+#endif
diff --git a/lib/blkid/getsize.c b/lib/blkid/getsize.c
new file mode 100644
index 0000000..7a6e6fd
--- /dev/null
+++ b/lib/blkid/getsize.c
@@ -0,0 +1,217 @@
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include "blkidP.h"
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+
+#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 */
+
+static int valid_offset(int fd, blkid_loff_t offset)
+{
+ char ch;
+
+ if (blkid_llseek(fd, offset, 0) < 0)
+ return 0;
+ if (read(fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+/*
+ * Returns the number of bytes in a partition
+ */
+blkid_loff_t blkid_get_dev_size(int fd)
+{
+ unsigned long long size64 __BLKID_ATTR((unused));
+ blkid_loff_t high, low;
+
+#if defined DKIOCGETBLOCKCOUNT && defined DKIOCGETBLOCKSIZE /* For Apple Darwin */
+ unsigned int size;
+
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0 &&
+ ioctl(fd, DKIOCGETBLOCKSIZE, &size) >= 0) {
+ if (sizeof(blkid_loff_t) < sizeof(unsigned long long) &&
+ (size64 * size) > 0xFFFFFFFF)
+ return 0; /* EFBIG */
+ return (blkid_loff_t)size64 * size;
+ }
+#endif
+
+#ifdef BLKGETSIZE64
+ {
+ int valid_blkgetsize64 = 1;
+#ifdef __linux__
+ struct utsname ut;
+
+ 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) {
+ if (sizeof(blkid_loff_t) < sizeof(unsigned long long) &&
+ (size64 > 0xFFFFFFFF))
+ return 0; /* EFBIG */
+ return size64;
+ }
+ }
+#endif /* BLKGETSIZE64 */
+
+#ifdef BLKGETSIZE
+ {
+ unsigned long size;
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0)
+ return (blkid_loff_t)size << 9;
+ }
+#endif
+
+/* tested on FreeBSD 6.1-RELEASE i386 */
+#ifdef DIOCGMEDIASIZE
+ if (ioctl(fd, DIOCGMEDIASIZE, &size64) >= 0)
+ return (off_t)size64;
+#endif /* DIOCGMEDIASIZE */
+
+#ifdef FDGETPRM
+ {
+ struct floppy_struct this_floppy;
+
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
+ return (blkid_loff_t)this_floppy.size << 9;
+ }
+#endif
+#if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO)
+ {
+ int part = -1;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+ struct stat st;
+
+ /*
+ * This code works for FreeBSD 4.11 i386, except for the full
+ * device (such as /dev/ad0). It doesn't work properly for
+ * newer FreeBSD though. FreeBSD >= 5.0 should be covered by
+ * the DIOCGMEDIASIZE above however.
+ *
+ * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
+ * character) devices, so we need to check for S_ISCHR, too.
+ */
+ if (fstat(fd, &st) >= 0 &&
+ blkidP_is_disk_device(st.st_mode))
+ part = st.st_rdev & 7;
+
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size)
+ return pp->p_size << 9;
+ }
+ }
+#endif /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */
+ {
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ struct stat64 st;
+ if (fstat64(fd, &st) == 0)
+#else
+ struct stat st;
+ if (fstat(fd, &st) == 0)
+#endif
+ if (S_ISREG(st.st_mode))
+ return st.st_size;
+ }
+
+ /*
+ * 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 blkid_loff_t mid = (low + high) / 2;
+
+ if (valid_offset(fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ return low + 1;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ long long bytes;
+ int fd;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n"
+ "Determine the size of a device\n", argv[0]);
+ return 1;
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0)
+ perror(argv[0]);
+
+ bytes = blkid_get_dev_size(fd);
+ printf("Device %s has %lld 1k blocks.\n", argv[1],
+ (unsigned long long)bytes >> 10);
+
+ return 0;
+}
+#endif
diff --git a/lib/blkid/libblkid.3.in b/lib/blkid/libblkid.3.in
new file mode 100644
index 0000000..18c4a27
--- /dev/null
+++ b/lib/blkid/libblkid.3.in
@@ -0,0 +1,80 @@
+.\" Copyright 2001 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for libblkid.so.1.0 from e2fsprogs-1.24.
+.\"
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.\" Created Wed Sep 14 12:02:12 2001, Andreas Dilger
+.TH LIBBLKID 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+libblkid \- block device identification library
+.SH SYNOPSIS
+.B #include <blkid/blkid.h>
+.sp
+.B cc
+.I file.c
+.B \-lblkid
+.SH DESCRIPTION
+The
+.B libblkid
+library is used to identify block devices (disks) as to their content (e.g.
+file system type) as well as extracting additional information such as
+file system labels/volume names, unique identifiers/serial numbers, etc.
+A common use is to allow use of LABEL= and UUID= tags instead of hard-coding
+specific block device names into configuration files.
+.P
+Block device information is normally kept in a cache file
+.I /etc/blkid.tab
+and is verified to still be valid before being returned to the user
+(if the user has read permission on the raw block device, otherwise not).
+The cache file also allows unprivileged users (normally anyone other
+than root, or those not in the "disk" group) to locate devices by label/id.
+The standard location of the cache file can be overridden by the
+environment variable BLKID_FILE.
+.P
+In situations where one is getting information about a single known device,
+it does not impact performance whether the cache is used or not (unless you
+are not able to read the block device directly). If you are dealing with
+multiple devices, use of the cache is highly recommended (even if empty) as
+devices will be scanned at most one time and the on-disk cache will be
+updated if possible. There is rarely a reason not to use the cache.
+.P
+In some cases (modular kernels), block devices are not even visible until
+after they are accessed the first time, so it is critical that there is
+some way to locate these devices without enumerating only visible devices,
+so the use of the cache file is
+.B required
+in this situation.
+.SH AUTHOR
+.B libblkid
+was written by Andreas Dilger for the ext2 file system utilities, with input
+from Ted Ts'o. The library was subsequently heavily modified by Ted Ts'o.
+.SH FILES
+.TP
+.I /etc/blkid.tab
+Caches data extracted from each recognized block device.
+.SH AVAILABILITY
+.B libblkid
+is part of the e2fsprogs package since version 1.33 and is available from
+http://e2fsprogs.sourceforge.net.
+.SH COPYING
+.B libblkid
+is available under the terms of the GNU Library General Public License (LGPL),
+version 2 (or at your discretion any later version). A copy of the LGPL
+should be included with this library in the file COPYING. If not, write to
+.RS
+Free Software Foundation, Inc.
+.br
+51 Franklin St
+.br
+Fifth Floor
+.br
+Boston, MA 02110-1301 USA
+.RE
+.PP
+or visit
+.UR http://www.gnu.org/licenses/licenses.html#LGPL
+http://www.gnu.org/licenses/licenses.html#LGPL
+.UE
+.SH "SEE ALSO"
+.BR blkid (8)
diff --git a/lib/blkid/list.h b/lib/blkid/list.h
new file mode 100644
index 0000000..eb1c513
--- /dev/null
+++ b/lib/blkid/list.h
@@ -0,0 +1,184 @@
+#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD_INIT)
+#define _BLKID_LIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#endif
+
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+/*
+ * 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 INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_add(struct list_head * add,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = add;
+ add->next = next;
+ add->prev = prev;
+ prev->next = add;
+}
+
+/**
+ * list_add - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+_INLINE_ void list_add(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, 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!
+ */
+_INLINE_ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+_INLINE_ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+_INLINE_ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+_INLINE_ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+_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;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(intptr_t)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ * pos after the body is done (in case it is freed)
+ * @pos: the &struct list_head to use as a loop counter.
+ * @pnext: the &struct list_head to use as a pointer to the next item.
+ * @head: the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+ for (pos = (head)->next, pnext = pos->next; pos != (head); \
+ pos = pnext, pnext = pos->next)
+
+#undef _INLINE_
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_LIST_H */
diff --git a/lib/blkid/llseek.c b/lib/blkid/llseek.c
new file mode 100644
index 0000000..5929864
--- /dev/null
+++ b/lib/blkid/llseek.c
@@ -0,0 +1,147 @@
+/*
+ * 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 Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+
+#include "blkidP.h"
+
+#ifdef __linux__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#elif defined(HAVE_LLSEEK)
+#include <sys/syscall.h>
+
+#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 <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek 140
+#endif
+
+#ifndef __i386__
+static int _llseek(unsigned int, unsigned long, unsigned long,
+ blkid_loff_t *, unsigned int);
+
+static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
+ unsigned long, offset_low, blkid_loff_t *, result,
+ unsigned int, origin)
+#endif
+
+static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
+{
+ blkid_loff_t result;
+ int retval;
+
+#ifndef __i386__
+ retval = _llseek(fd, ((unsigned long long) offset) >> 32,
+ ((unsigned long long)offset) & 0xffffffff,
+ &result, origin);
+#else
+ retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
+ ((unsigned long long)offset) & 0xffffffff,
+ &result, origin);
+#endif
+ return (retval == -1 ? (blkid_loff_t) retval : result);
+}
+
+#endif /* __alpha__ || __ia64__ */
+
+#endif /* HAVE_LLSEEK */
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
+{
+ blkid_loff_t result;
+ static int do_compat = 0;
+
+ if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
+ (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+ return lseek(fd, (off_t) offset, whence);
+
+ if (do_compat) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ result = my_llseek(fd, offset, whence);
+ 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 = EOVERFLOW;
+ }
+ return result;
+}
+
+#else /* !linux */
+
+#ifndef EOVERFLOW
+#ifdef EXT2_ET_INVALID_ARGUMENT
+#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
+#else
+#define EOVERFLOW 112
+#endif
+#endif
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
+{
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+ return lseek64 (fd, offset, origin);
+#else
+ if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
+ (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ return lseek(fd, (off_t) offset, origin);
+#endif
+}
+
+#endif /* linux */
+
+
diff --git a/lib/blkid/probe.c b/lib/blkid/probe.c
new file mode 100644
index 0000000..6a3bb24
--- /dev/null
+++ b/lib/blkid/probe.c
@@ -0,0 +1,1841 @@
+/*
+ * probe.c - identify a block device by its contents, and return a dev
+ * struct with the details
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+#include "uuid/uuid.h"
+#include "probe.h"
+
+static int figure_label_len(const unsigned char *label, int len)
+{
+ const unsigned char *end = label + len - 1;
+
+ while (end >= label && (*end == ' ' || *end == 0))
+ --end;
+ if (end >= label)
+ return end - label + 1;
+ return 0;
+}
+
+static unsigned char *get_buffer(struct blkid_probe *pr,
+ blkid_loff_t off, size_t len)
+{
+ ssize_t ret_read;
+ unsigned char *newbuf;
+
+ if (off + len <= SB_BUFFER_SIZE) {
+ if (!pr->sbbuf) {
+ pr->sbbuf = malloc(SB_BUFFER_SIZE);
+ if (!pr->sbbuf)
+ return NULL;
+ if (lseek(pr->fd, 0, SEEK_SET) < 0)
+ return NULL;
+ ret_read = read(pr->fd, pr->sbbuf, SB_BUFFER_SIZE);
+ if (ret_read < 0)
+ ret_read = 0;
+ pr->sb_valid = ret_read;
+ }
+ if (off+len > pr->sb_valid)
+ return NULL;
+ return pr->sbbuf + off;
+ } else {
+ if (len > pr->buf_max) {
+ newbuf = realloc(pr->buf, len);
+ if (newbuf == NULL)
+ return NULL;
+ pr->buf = newbuf;
+ pr->buf_max = len;
+ }
+ if (blkid_llseek(pr->fd, off, SEEK_SET) < 0)
+ return NULL;
+ ret_read = read(pr->fd, pr->buf, len);
+ if (ret_read != (ssize_t) len)
+ return NULL;
+ return pr->buf;
+ }
+}
+
+
+/*
+ * This is a special case code to check for an MDRAID device. We do
+ * this special since it requires checking for a superblock at the end
+ * of the device.
+ */
+static int check_mdraid(int fd, unsigned char *ret_uuid)
+{
+ struct mdp_superblock_s *md;
+ blkid_loff_t offset;
+ char buf[4096];
+
+ if (fd < 0)
+ return -BLKID_ERR_PARAM;
+
+ offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536;
+
+ if (blkid_llseek(fd, offset, 0) < 0 ||
+ read(fd, buf, 4096) != 4096)
+ return -BLKID_ERR_IO;
+ /* Check for magic number */
+ if (memcmp("\251+N\374", buf, 4) && memcmp("\374N+\251", buf, 4))
+ return -BLKID_ERR_PARAM;
+
+ if (!ret_uuid)
+ return 0;
+ *ret_uuid = 0;
+
+ /* The MD UUID is not contiguous in the superblock, make it so */
+ md = (struct mdp_superblock_s *)buf;
+ if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) {
+ memcpy(ret_uuid, &md->set_uuid0, 4);
+ memcpy(ret_uuid + 4, &md->set_uuid1, 12);
+ }
+ return 0;
+}
+
+static void set_uuid(blkid_dev dev, uuid_t uuid, const char *tag)
+{
+ char str[37];
+
+ if (!uuid_is_null(uuid)) {
+ uuid_unparse(uuid, str);
+ blkid_set_tag(dev, tag ? tag : "UUID", str, sizeof(str));
+ }
+}
+
+static void get_ext2_info(blkid_dev dev, struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct ext2_super_block *es = (struct ext2_super_block *) buf;
+ const char *label = 0;
+
+ DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
+ blkid_le32(es->s_feature_compat),
+ blkid_le32(es->s_feature_incompat),
+ blkid_le32(es->s_feature_ro_compat)));
+
+ if (es->s_volume_name[0])
+ label = es->s_volume_name;
+ blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name));
+
+ set_uuid(dev, es->s_uuid, 0);
+
+ if ((es->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+ !uuid_is_null(es->s_journal_uuid))
+ set_uuid(dev, es->s_journal_uuid, "EXT_JOURNAL");
+
+ if (strcmp(id->bim_type, "ext2") &&
+ ((blkid_le32(es->s_feature_incompat) &
+ EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0))
+ blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2"));
+}
+
+/*
+ * Check to see if a filesystem is in /proc/filesystems.
+ * Returns 1 if found, 0 if not
+ */
+static int fs_proc_check(const char *fs_name)
+{
+ FILE *f;
+ char buf[80], *cp, *t;
+
+ f = fopen("/proc/filesystems", "r");
+ if (!f)
+ return (0);
+ while (!feof(f)) {
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ cp = buf;
+ if (!isspace(*cp)) {
+ while (*cp && !isspace(*cp))
+ cp++;
+ }
+ while (*cp && isspace(*cp))
+ cp++;
+ if ((t = strchr(cp, '\n')) != NULL)
+ *t = 0;
+ if ((t = strchr(cp, '\t')) != NULL)
+ *t = 0;
+ if ((t = strchr(cp, ' ')) != NULL)
+ *t = 0;
+ if (!strcmp(fs_name, cp)) {
+ fclose(f);
+ return (1);
+ }
+ }
+ fclose(f);
+ return (0);
+}
+
+/*
+ * Check to see if a filesystem is available as a module
+ * Returns 1 if found, 0 if not
+ */
+static int check_for_modules(const char *fs_name)
+{
+#ifdef __linux__
+ struct utsname uts;
+ FILE *f;
+ char buf[1024], *cp;
+ int namesz;
+
+ if (uname(&uts))
+ return (0);
+ snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
+
+ f = fopen(buf, "r");
+ if (!f)
+ return (0);
+
+ namesz = strlen(fs_name);
+
+ while (!feof(f)) {
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ if ((cp = strchr(buf, ':')) != NULL)
+ *cp = 0;
+ else
+ continue;
+ if ((cp = strrchr(buf, '/')) != NULL)
+ cp++;
+ else
+ cp = buf;
+ if (!strncmp(cp, fs_name, namesz) &&
+ (!strcmp(cp + namesz, ".ko") ||
+ !strcmp(cp + namesz, ".ko.gz"))) {
+ fclose(f);
+ return (1);
+ }
+ }
+ fclose(f);
+#endif
+ return (0);
+}
+
+static int linux_version_code(void)
+{
+#ifdef __linux__
+ struct utsname ut;
+ static int version_code = -1;
+ int major, minor, rev;
+ char *endptr;
+ const char *cp;
+
+ if (version_code > 0)
+ return version_code;
+
+ if (uname(&ut))
+ return 0;
+ cp = ut.release;
+
+ major = strtol(cp, &endptr, 10);
+ if (cp == endptr || *endptr != '.')
+ return 0;
+ cp = endptr + 1;
+ minor = strtol(cp, &endptr, 10);
+ if (cp == endptr || *endptr != '.')
+ return 0;
+ cp = endptr + 1;
+ rev = strtol(cp, &endptr, 10);
+ if (cp == endptr)
+ return 0;
+ version_code = (((major * 256) + minor) * 256) + rev;
+ return version_code;
+#else
+ return 0;
+#endif
+}
+
+#define EXT4_SUPPORTS_EXT2 (2 * 65536 + 6*256 + 29)
+
+static int system_supports_ext2(void)
+{
+ static time_t last_check = 0;
+ static int ret = -1;
+ time_t now = time(0);
+
+ if (ret != -1 || (now - last_check) < 5)
+ return ret;
+ last_check = now;
+ ret = (fs_proc_check("ext2") || check_for_modules("ext2"));
+ return ret;
+}
+
+static int system_supports_ext4(void)
+{
+ static time_t last_check = 0;
+ static int ret = -1;
+ time_t now = time(0);
+
+ if (ret != -1 || (now - last_check) < 5)
+ return ret;
+ last_check = now;
+ ret = (fs_proc_check("ext4") || check_for_modules("ext4"));
+ return ret;
+}
+
+static int system_supports_ext4dev(void)
+{
+ static time_t last_check = 0;
+ static int ret = -1;
+ time_t now = time(0);
+
+ if (ret != -1 || (now - last_check) < 5)
+ return ret;
+ last_check = now;
+ ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev"));
+ return ret;
+}
+
+static int probe_ext4dev(struct blkid_probe *probe,
+ struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct ext2_super_block *es;
+ es = (struct ext2_super_block *)buf;
+
+ /* Distinguish from jbd */
+ if (blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ return -BLKID_ERR_PARAM;
+
+ /*
+ * If the filesystem does not have a journal and ext2 and ext4
+ * is not present, then force this to be detected as an
+ * ext4dev filesystem.
+ */
+ if (!(blkid_le32(es->s_feature_compat) &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+ !system_supports_ext2() && !system_supports_ext4() &&
+ system_supports_ext4dev() &&
+ linux_version_code() >= EXT4_SUPPORTS_EXT2)
+ goto force_ext4dev;
+
+ /*
+ * If the filesystem is marked as OK for use by in-development
+ * filesystem code, but ext4dev is not supported, and ext4 is,
+ * then don't call ourselves ext4dev, since we should be
+ * detected as ext4 in that case.
+ *
+ * If the filesystem is marked as in use by production
+ * filesystem, then it can only be used by ext4 and NOT by
+ * ext4dev, so always disclaim we are ext4dev in that case.
+ */
+ if (blkid_le32(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
+ if (!system_supports_ext4dev() && system_supports_ext4())
+ return -BLKID_ERR_PARAM;
+ } else
+ return -BLKID_ERR_PARAM;
+
+force_ext4dev:
+ get_ext2_info(probe->dev, id, buf);
+ return 0;
+}
+
+static int probe_ext4(struct blkid_probe *probe, struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct ext2_super_block *es;
+ es = (struct ext2_super_block *)buf;
+
+ /* Distinguish from jbd */
+ if (blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ return -BLKID_ERR_PARAM;
+
+ /*
+ * If the filesystem does not have a journal and ext2 is not
+ * present, then force this to be detected as an ext2
+ * filesystem.
+ */
+ if (!(blkid_le32(es->s_feature_compat) &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+ !system_supports_ext2() && system_supports_ext4() &&
+ linux_version_code() >= EXT4_SUPPORTS_EXT2)
+ goto force_ext4;
+
+ /* Ext4 has at least one feature which ext3 doesn't understand */
+ if (!(blkid_le32(es->s_feature_ro_compat) &
+ EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
+ !(blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+ return -BLKID_ERR_PARAM;
+
+force_ext4:
+ /*
+ * If the filesystem is a OK for use by in-development
+ * filesystem code, and ext4dev is supported or ext4 is not
+ * supported, then don't call ourselves ext4, so we can redo
+ * the detection and mark the filesystem as ext4dev.
+ *
+ * If the filesystem is marked as in use by production
+ * filesystem, then it can only be used by ext4 and NOT by
+ * ext4dev.
+ */
+ if (blkid_le32(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
+ if (system_supports_ext4dev() || !system_supports_ext4())
+ return -BLKID_ERR_PARAM;
+ }
+ get_ext2_info(probe->dev, id, buf);
+ return 0;
+}
+
+static int probe_ext3(struct blkid_probe *probe, struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct ext2_super_block *es;
+ es = (struct ext2_super_block *)buf;
+
+ /* ext3 requires journal */
+ if (!(blkid_le32(es->s_feature_compat) &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ return -BLKID_ERR_PARAM;
+
+ /* Any features which ext3 doesn't understand */
+ if ((blkid_le32(es->s_feature_ro_compat) &
+ EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+ (blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+ return -BLKID_ERR_PARAM;
+
+ get_ext2_info(probe->dev, id, buf);
+ return 0;
+}
+
+static int probe_ext2(struct blkid_probe *probe, struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct ext2_super_block *es;
+
+ es = (struct ext2_super_block *)buf;
+
+ /* Distinguish between ext3 and ext2 */
+ if ((blkid_le32(es->s_feature_compat) &
+ EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ return -BLKID_ERR_PARAM;
+
+ /* Any features which ext2 doesn't understand */
+ if ((blkid_le32(es->s_feature_ro_compat) &
+ EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+ (blkid_le32(es->s_feature_incompat) &
+ EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
+ return -BLKID_ERR_PARAM;
+
+ /*
+ * If ext2 is not present, but ext4 or ext4dev are, then
+ * disclaim we are ext2
+ */
+ if (!system_supports_ext2() &&
+ (system_supports_ext4() || system_supports_ext4dev()) &&
+ linux_version_code() >= EXT4_SUPPORTS_EXT2)
+ return -BLKID_ERR_PARAM;
+
+ get_ext2_info(probe->dev, id, buf);
+ return 0;
+}
+
+static int probe_jbd(struct blkid_probe *probe, struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct ext2_super_block *es = (struct ext2_super_block *) buf;
+
+ if (!(blkid_le32(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+ return -BLKID_ERR_PARAM;
+
+ get_ext2_info(probe->dev, id, buf);
+
+ return 0;
+}
+
+#define FAT_ATTR_VOLUME_ID 0x08
+#define FAT_ATTR_DIR 0x10
+#define FAT_ATTR_LONG_NAME 0x0f
+#define FAT_ATTR_MASK 0x3f
+#define FAT_ENTRY_FREE 0xe5
+
+static const char *no_name = "NO NAME ";
+
+static unsigned char *search_fat_label(struct vfat_dir_entry *dir, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (dir[i].name[0] == 0x00)
+ break;
+
+ if ((dir[i].name[0] == FAT_ENTRY_FREE) ||
+ (dir[i].cluster_high != 0 || dir[i].cluster_low != 0) ||
+ ((dir[i].attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME))
+ continue;
+
+ if ((dir[i].attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) ==
+ FAT_ATTR_VOLUME_ID) {
+ return dir[i].name;
+ }
+ }
+ return 0;
+}
+
+/* FAT label extraction from the root directory taken from Kay
+ * Sievers's volume_id library */
+static int probe_fat(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct vfat_super_block *vs = (struct vfat_super_block *) buf;
+ struct msdos_super_block *ms = (struct msdos_super_block *) buf;
+ struct vfat_dir_entry *dir;
+ char serno[10];
+ const unsigned char *label = 0, *vol_label = 0, *tmp;
+ unsigned char *vol_serno;
+ int label_len = 0, maxloop = 100;
+ __u16 sector_size, dir_entries, reserved;
+ __u32 sect_count, fat_size, dir_size, cluster_count, fat_length;
+ __u32 buf_size, start_data_sect, next, root_start, root_dir_entries;
+
+ /* sector size check */
+ tmp = (unsigned char *)&ms->ms_sector_size;
+ sector_size = tmp[0] + (tmp[1] << 8);
+ if (sector_size != 0x200 && sector_size != 0x400 &&
+ sector_size != 0x800 && sector_size != 0x1000)
+ return 1;
+
+ tmp = (unsigned char *)&ms->ms_dir_entries;
+ dir_entries = tmp[0] + (tmp[1] << 8);
+ reserved = blkid_le16(ms->ms_reserved);
+ tmp = (unsigned char *)&ms->ms_sectors;
+ sect_count = tmp[0] + (tmp[1] << 8);
+ if (sect_count == 0)
+ sect_count = blkid_le32(ms->ms_total_sect);
+
+ fat_length = blkid_le16(ms->ms_fat_length);
+ if (fat_length == 0)
+ fat_length = blkid_le32(vs->vs_fat32_length);
+
+ fat_size = fat_length * ms->ms_fats;
+ dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
+ (sector_size-1)) / sector_size;
+
+ cluster_count = sect_count - (reserved + fat_size + dir_size);
+ if (ms->ms_cluster_size == 0)
+ return 1;
+ cluster_count /= ms->ms_cluster_size;
+
+ if (cluster_count > FAT32_MAX)
+ return 1;
+
+ if (ms->ms_fat_length) {
+ /* the label may be an attribute in the root directory */
+ root_start = (reserved + fat_size) * sector_size;
+ root_dir_entries = vs->vs_dir_entries[0] +
+ (vs->vs_dir_entries[1] << 8);
+
+ buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
+ dir = (struct vfat_dir_entry *) get_buffer(probe, root_start,
+ buf_size);
+ if (dir)
+ vol_label = search_fat_label(dir, root_dir_entries);
+
+ if (!vol_label || !memcmp(vol_label, no_name, 11))
+ vol_label = ms->ms_label;
+ vol_serno = ms->ms_serno;
+
+ blkid_set_tag(probe->dev, "SEC_TYPE", "msdos",
+ sizeof("msdos"));
+ } else {
+ /* Search the FAT32 root dir for the label attribute */
+ buf_size = vs->vs_cluster_size * sector_size;
+ start_data_sect = reserved + fat_size;
+
+ next = blkid_le32(vs->vs_root_cluster);
+ while (next && --maxloop) {
+ __u32 next_sect_off;
+ __u64 next_off, fat_entry_off;
+ int count;
+
+ next_sect_off = (next - 2) * vs->vs_cluster_size;
+ next_off = (__u64) (start_data_sect + next_sect_off) *
+ sector_size;
+
+ dir = (struct vfat_dir_entry *)
+ get_buffer(probe, next_off, buf_size);
+ if (dir == NULL)
+ break;
+
+ count = buf_size / sizeof(struct vfat_dir_entry);
+
+ vol_label = search_fat_label(dir, count);
+ if (vol_label)
+ break;
+
+ /* get FAT entry */
+ fat_entry_off =
+ ((unsigned int) reserved *
+ (unsigned int) sector_size) +
+ (next * sizeof(__u32));
+ buf = get_buffer(probe, fat_entry_off, buf_size);
+ if (buf == NULL)
+ break;
+
+ /* set next cluster */
+ next = blkid_le32(*((__u32 *) buf) & 0x0fffffff);
+ }
+
+ if (!vol_label || !memcmp(vol_label, no_name, 11))
+ vol_label = vs->vs_label;
+ vol_serno = vs->vs_serno;
+ }
+
+ if (vol_label && memcmp(vol_label, no_name, 11)) {
+ if ((label_len = figure_label_len(vol_label, 11)))
+ label = vol_label;
+ }
+
+ /* We can't just print them as %04X, because they are unaligned */
+ sprintf(serno, "%02X%02X-%02X%02X", vol_serno[3], vol_serno[2],
+ vol_serno[1], vol_serno[0]);
+
+ blkid_set_tag(probe->dev, "LABEL", (const char *) label, label_len);
+ blkid_set_tag(probe->dev, "UUID", serno, sizeof(serno)-1);
+
+ return 0;
+}
+
+/*
+ * The FAT filesystem could be without a magic string in superblock
+ * (e.g. old floppies). This heuristic for FAT detection is inspired
+ * by http://vrfy.org/projects/volume_id/ and Linux kernel.
+ * [7-Jul-2005, Karel Zak <kzak@redhat.com>]
+ */
+static int probe_fat_nomagic(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct msdos_super_block *ms;
+
+ ms = (struct msdos_super_block *)buf;
+
+ /* heads check */
+ if (ms->ms_heads == 0)
+ return 1;
+
+ /* cluster size check*/
+ if (ms->ms_cluster_size == 0 ||
+ (ms->ms_cluster_size & (ms->ms_cluster_size-1)))
+ return 1;
+
+ /* media check */
+ if (ms->ms_media < 0xf8 && ms->ms_media != 0xf0)
+ return 1;
+
+ /* fat counts(Linux kernel expects at least 1 FAT table) */
+ if (!ms->ms_fats)
+ return 1;
+
+ /*
+ * OS/2 and apparently DFSee will place a FAT12/16-like
+ * pseudo-superblock in the first 512 bytes of non-FAT
+ * filesystems --- at least JFS and HPFS, and possibly others.
+ * So we explicitly check for those filesystems at the
+ * FAT12/16 filesystem magic field identifier, and if they are
+ * present, we rule this out as a FAT filesystem, despite the
+ * FAT-like pseudo-header.
+ */
+ if ((memcmp(ms->ms_magic, "JFS ", 8) == 0) ||
+ (memcmp(ms->ms_magic, "HPFS ", 8) == 0))
+ return 1;
+
+ return probe_fat(probe, id, buf);
+}
+
+static int probe_ntfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ntfs_super_block *ns;
+ struct master_file_table_record *mft;
+ struct file_attribute *attr;
+ char uuid_str[17], label_str[129], *cp;
+ int bytes_per_sector, sectors_per_cluster;
+ int mft_record_size, attr_off, attr_len;
+ unsigned int i, attr_type, val_len;
+ int val_off;
+ __u64 nr_clusters;
+ blkid_loff_t off;
+ unsigned char *buf_mft, *val;
+
+ ns = (struct ntfs_super_block *) buf;
+
+ bytes_per_sector = ns->bios_parameter_block[0] +
+ (ns->bios_parameter_block[1] << 8);
+ sectors_per_cluster = ns->bios_parameter_block[2];
+
+ if ((bytes_per_sector < 512) || (sectors_per_cluster == 0))
+ return 1;
+
+ if (ns->cluster_per_mft_record < 0)
+ mft_record_size = 1 << (0-ns->cluster_per_mft_record);
+ else
+ mft_record_size = ns->cluster_per_mft_record *
+ sectors_per_cluster * bytes_per_sector;
+ nr_clusters = blkid_le64(ns->number_of_sectors) / sectors_per_cluster;
+
+ if ((blkid_le64(ns->mft_cluster_location) > nr_clusters) ||
+ (blkid_le64(ns->mft_mirror_cluster_location) > nr_clusters))
+ return 1;
+
+ off = blkid_le64(ns->mft_mirror_cluster_location) *
+ bytes_per_sector * sectors_per_cluster;
+
+ buf_mft = get_buffer(probe, off, mft_record_size);
+ if (!buf_mft)
+ return 1;
+
+ if (memcmp(buf_mft, "FILE", 4))
+ return 1;
+
+ off = blkid_le64(ns->mft_cluster_location) * bytes_per_sector *
+ sectors_per_cluster;
+
+ buf_mft = get_buffer(probe, off, mft_record_size);
+ if (!buf_mft)
+ return 1;
+
+ if (memcmp(buf_mft, "FILE", 4))
+ return 1;
+
+ off += MFT_RECORD_VOLUME * mft_record_size;
+
+ buf_mft = get_buffer(probe, off, mft_record_size);
+ if (!buf_mft)
+ return 1;
+
+ if (memcmp(buf_mft, "FILE", 4))
+ return 1;
+
+ mft = (struct master_file_table_record *) buf_mft;
+
+ attr_off = blkid_le16(mft->attrs_offset);
+ label_str[0] = 0;
+
+ while (1) {
+ attr = (struct file_attribute *) (buf_mft + attr_off);
+ attr_len = blkid_le16(attr->len);
+ attr_type = blkid_le32(attr->type);
+ val_off = blkid_le16(attr->value_offset);
+ val_len = blkid_le32(attr->value_len);
+
+ attr_off += attr_len;
+
+ if ((attr_off > mft_record_size) ||
+ (attr_len == 0))
+ break;
+
+ if (attr_type == MFT_RECORD_ATTR_END)
+ break;
+
+ if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
+ if (val_len > sizeof(label_str))
+ val_len = sizeof(label_str)-1;
+
+ for (i=0, cp=label_str; i < val_len; i+=2,cp++) {
+ val = ((__u8 *) attr) + val_off + i;
+ *cp = val[0];
+ if (val[1])
+ *cp = '?';
+ }
+ *cp = 0;
+ }
+ }
+
+ sprintf(uuid_str, "%016llX", blkid_le64(ns->volume_serial));
+ blkid_set_tag(probe->dev, "UUID", uuid_str, 0);
+ if (label_str[0])
+ blkid_set_tag(probe->dev, "LABEL", label_str, 0);
+ return 0;
+}
+
+
+static int probe_xfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct xfs_super_block *xs;
+ const char *label = 0;
+
+ xs = (struct xfs_super_block *)buf;
+
+ if (strlen(xs->xs_fname))
+ label = xs->xs_fname;
+ blkid_set_tag(probe->dev, "LABEL", label, sizeof(xs->xs_fname));
+ set_uuid(probe->dev, xs->xs_uuid, 0);
+ return 0;
+}
+
+static int probe_reiserfs(struct blkid_probe *probe,
+ struct blkid_magic *id, unsigned char *buf)
+{
+ struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf;
+ unsigned int blocksize;
+ const char *label = 0;
+
+ blocksize = blkid_le16(rs->rs_blocksize);
+
+ /* The blocksize must be at least 1k */
+ if ((blocksize >> 10) == 0)
+ return -BLKID_ERR_PARAM;
+
+ /* If the superblock is inside the journal, we have the wrong one */
+ if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block))
+ return -BLKID_ERR_BIG;
+
+ /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
+ if (id->bim_magic[6] == '2' || id->bim_magic[6] == '3') {
+ if (strlen(rs->rs_label))
+ label = rs->rs_label;
+ set_uuid(probe->dev, rs->rs_uuid, 0);
+ }
+ blkid_set_tag(probe->dev, "LABEL", label, sizeof(rs->rs_label));
+
+ return 0;
+}
+
+static int probe_reiserfs4(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct reiser4_super_block *rs4 = (struct reiser4_super_block *) buf;
+ const unsigned char *label = 0;
+
+ if (strlen((char *) rs4->rs4_label))
+ label = rs4->rs4_label;
+ set_uuid(probe->dev, rs4->rs4_uuid, 0);
+ blkid_set_tag(probe->dev, "LABEL", (const char *) label,
+ sizeof(rs4->rs4_label));
+
+ return 0;
+}
+
+static int probe_jfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct jfs_super_block *js;
+ const char *label = 0;
+
+ js = (struct jfs_super_block *)buf;
+
+ if (blkid_le32(js->js_bsize) != (1U << blkid_le16(js->js_l2bsize)))
+ return 1;
+
+ if (blkid_le32(js->js_pbsize) != (1U << blkid_le16(js->js_l2pbsize)))
+ return 1;
+
+ if ((blkid_le16(js->js_l2bsize) - blkid_le16(js->js_l2pbsize)) !=
+ blkid_le16(js->js_l2bfactor))
+ return 1;
+
+ if (strlen((char *) js->js_label))
+ label = (char *) js->js_label;
+ blkid_set_tag(probe->dev, "LABEL", label, sizeof(js->js_label));
+ set_uuid(probe->dev, js->js_uuid, 0);
+ return 0;
+}
+
+static int probe_zfs(struct blkid_probe *probe __BLKID_ATTR((unused)),
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+#if 0
+ char *vdev_label;
+ const char *pool_name = 0;
+
+ /* read nvpair data for pool name, pool GUID (complex) */
+ blkid_set_tag(probe->dev, "LABEL", pool_name, sizeof(pool_name));
+ set_uuid(probe->dev, pool_guid, 0);
+#endif
+ return 0;
+}
+
+static int probe_luks(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ char uuid[41];
+
+ /* 168 is the offset to the 40 character uuid:
+ * http://luks.endorphin.org/LUKS-on-disk-format.pdf */
+ strncpy(uuid, (char *) buf+168, 40);
+ uuid[40] = 0;
+ blkid_set_tag(probe->dev, "UUID", uuid, 40);
+ return 0;
+}
+
+static int probe_romfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct romfs_super_block *ros;
+ const char *label = 0;
+
+ ros = (struct romfs_super_block *)buf;
+
+ if (strlen((char *) ros->ros_volume))
+ label = (char *) ros->ros_volume;
+ blkid_set_tag(probe->dev, "LABEL", label, 0);
+ return 0;
+}
+
+static int probe_cramfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct cramfs_super_block *csb;
+ const char *label = 0;
+
+ csb = (struct cramfs_super_block *)buf;
+
+ if (strlen((char *) csb->name))
+ label = (char *) csb->name;
+ blkid_set_tag(probe->dev, "LABEL", label, 0);
+ return 0;
+}
+
+static int probe_swap0(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+ blkid_set_tag(probe->dev, "UUID", 0, 0);
+ blkid_set_tag(probe->dev, "LABEL", 0, 0);
+ return 0;
+}
+
+static int probe_swap1(struct blkid_probe *probe,
+ struct blkid_magic *id,
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+ struct swap_id_block *sws;
+
+ probe_swap0(probe, id, buf);
+ /*
+ * Version 1 swap headers are always located at offset of 1024
+ * bytes, although the swap signature itself is located at the
+ * end of the page (which may vary depending on hardware
+ * pagesize).
+ */
+ sws = (struct swap_id_block *) get_buffer(probe, 1024, 1024);
+ if (!sws)
+ return 1;
+
+ /* check for wrong version or zeroed pagecount, for sanity */
+ if (!memcmp(id->bim_magic, "SWAPSPACE2", id->bim_len) &&
+ (sws->sws_version != 1 || sws->sws_lastpage == 0))
+ return 1;
+
+ /* arbitrary sanity check.. is there any garbage down there? */
+ if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) {
+ if (sws->sws_volume[0])
+ blkid_set_tag(probe->dev, "LABEL", sws->sws_volume,
+ sizeof(sws->sws_volume));
+ if (sws->sws_uuid[0])
+ set_uuid(probe->dev, sws->sws_uuid, 0);
+ }
+ return 0;
+}
+
+static int probe_iso9660(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct iso_volume_descriptor *iso;
+ const unsigned char *label;
+
+ iso = (struct iso_volume_descriptor *) buf;
+ label = iso->volume_id;
+
+ blkid_set_tag(probe->dev, "LABEL", (const char *) label,
+ figure_label_len(label, 32));
+ return 0;
+}
+
+
+static const char
+*udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02",
+ "NSR03", "TEA01", 0 };
+
+static int probe_udf(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf __BLKID_ATTR((unused)))
+{
+ int j, bs;
+ struct iso_volume_descriptor *isosb;
+ const char ** m;
+
+ /* determine the block size by scanning in 2K increments
+ (block sizes larger than 2K will be null padded) */
+ for (bs = 1; bs < 16; bs++) {
+ isosb = (struct iso_volume_descriptor *)
+ get_buffer(probe, (blkid_loff_t) bs*2048+32768,
+ sizeof(*isosb));
+ if (!isosb)
+ return 1;
+ if (isosb->vd_id[0])
+ break;
+ }
+
+ /* Scan up to another 64 blocks looking for additional VSD's */
+ for (j = 1; j < 64; j++) {
+ if (j > 1) {
+ isosb = (struct iso_volume_descriptor *)
+ get_buffer(probe, j*bs*2048+32768,
+ sizeof(*isosb));
+ if (!isosb)
+ return 1;
+ }
+ /* If we find NSR0x then call it udf:
+ NSR01 for UDF 1.00
+ NSR02 for UDF 1.50
+ NSR03 for UDF 2.00 */
+ if (!memcmp(isosb->vd_id, "NSR0", 4))
+ return probe_iso9660(probe, id, buf);
+ for (m = udf_magic; *m; m++)
+ if (!memcmp(*m, isosb->vd_id, 5))
+ break;
+ if (*m == 0)
+ return 1;
+ }
+ return 1;
+}
+
+static int probe_ocfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ocfs_volume_header ovh;
+ struct ocfs_volume_label ovl;
+ __u32 major;
+
+ memcpy(&ovh, buf, sizeof(ovh));
+ memcpy(&ovl, buf+512, sizeof(ovl));
+
+ major = ocfsmajor(ovh);
+ if (major == 1)
+ blkid_set_tag(probe->dev,"SEC_TYPE","ocfs1",sizeof("ocfs1"));
+ else if (major >= 9)
+ blkid_set_tag(probe->dev,"SEC_TYPE","ntocfs",sizeof("ntocfs"));
+
+ blkid_set_tag(probe->dev, "LABEL", ovl.label, ocfslabellen(ovl));
+ blkid_set_tag(probe->dev, "MOUNT", ovh.mount, ocfsmountlen(ovh));
+ set_uuid(probe->dev, ovl.vol_id, 0);
+ return 0;
+}
+
+static int probe_ocfs2(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct ocfs2_super_block *osb;
+
+ osb = (struct ocfs2_super_block *)buf;
+
+ blkid_set_tag(probe->dev, "LABEL", osb->s_label, sizeof(osb->s_label));
+ set_uuid(probe->dev, osb->s_uuid, 0);
+ return 0;
+}
+
+static int probe_oracleasm(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct oracle_asm_disk_label *dl;
+
+ dl = (struct oracle_asm_disk_label *)buf;
+
+ blkid_set_tag(probe->dev, "LABEL", dl->dl_id, sizeof(dl->dl_id));
+ return 0;
+}
+
+static int probe_gfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct gfs2_sb *sbd;
+ const char *label = 0;
+
+ sbd = (struct gfs2_sb *)buf;
+
+ if (blkid_be32(sbd->sb_fs_format) == GFS_FORMAT_FS &&
+ blkid_be32(sbd->sb_multihost_format) == GFS_FORMAT_MULTI)
+ {
+ blkid_set_tag(probe->dev, "UUID", 0, 0);
+
+ if (strlen(sbd->sb_locktable))
+ label = sbd->sb_locktable;
+ blkid_set_tag(probe->dev, "LABEL", label, sizeof(sbd->sb_locktable));
+ return 0;
+ }
+ return 1;
+}
+
+static int probe_gfs2(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct gfs2_sb *sbd;
+ const char *label = 0;
+
+ sbd = (struct gfs2_sb *)buf;
+
+ if (blkid_be32(sbd->sb_fs_format) == GFS2_FORMAT_FS &&
+ blkid_be32(sbd->sb_multihost_format) == GFS2_FORMAT_MULTI)
+ {
+ blkid_set_tag(probe->dev, "UUID", 0, 0);
+
+ if (strlen(sbd->sb_locktable))
+ label = sbd->sb_locktable;
+ blkid_set_tag(probe->dev, "LABEL", label, sizeof(sbd->sb_locktable));
+ return 0;
+ }
+ return 1;
+}
+
+static void unicode_16be_to_utf8(unsigned char *str, int out_len,
+ const unsigned char *buf, int in_len)
+{
+ int i, j;
+ unsigned int c;
+
+ for (i = j = 0; i + 2 <= in_len; i += 2) {
+ c = (buf[i] << 8) | buf[i+1];
+ if (c == 0) {
+ str[j] = '\0';
+ break;
+ } else if (c < 0x80) {
+ if (j+1 >= out_len)
+ break;
+ str[j++] = (unsigned char) c;
+ } else if (c < 0x800) {
+ if (j+2 >= out_len)
+ break;
+ str[j++] = (unsigned char) (0xc0 | (c >> 6));
+ str[j++] = (unsigned char) (0x80 | (c & 0x3f));
+ } else {
+ if (j+3 >= out_len)
+ break;
+ str[j++] = (unsigned char) (0xe0 | (c >> 12));
+ str[j++] = (unsigned char) (0x80 | ((c >> 6) & 0x3f));
+ str[j++] = (unsigned char) (0x80 | (c & 0x3f));
+ }
+ }
+ str[j] = '\0';
+}
+
+static void unicode_16le_to_utf8(unsigned char *str, int out_len,
+ const unsigned char *buf, int in_len)
+{
+ int i, j;
+ unsigned int c;
+
+ for (i = j = 0; i + 2 <= in_len; i += 2) {
+ c = (buf[i+1] << 8) | buf[i];
+ if (c == 0) {
+ str[j] = '\0';
+ break;
+ } else if (c < 0x80) {
+ if (j+1 >= out_len)
+ break;
+ str[j++] = (unsigned char) c;
+ } else if (c < 0x800) {
+ if (j+2 >= out_len)
+ break;
+ str[j++] = (unsigned char) (0xc0 | (c >> 6));
+ str[j++] = (unsigned char) (0x80 | (c & 0x3f));
+ } else {
+ if (j+3 >= out_len)
+ break;
+ str[j++] = (unsigned char) (0xe0 | (c >> 12));
+ str[j++] = (unsigned char) (0x80 | ((c >> 6) & 0x3f));
+ str[j++] = (unsigned char) (0x80 | (c & 0x3f));
+ }
+ }
+ str[j] = '\0';
+}
+
+static int probe_hfs(struct blkid_probe *probe __BLKID_ATTR((unused)),
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct hfs_mdb *hfs = (struct hfs_mdb *)buf;
+ char uuid_str[17];
+ __u64 uuid;
+
+ if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
+ (memcmp(hfs->embed_sig, "HX", 2) == 0))
+ return 1; /* Not hfs, but an embedded HFS+ */
+
+ memcpy(&uuid, hfs->finder_info.id, 8);
+ uuid = blkid_le64(uuid);
+ if (uuid) {
+ sprintf(uuid_str, "%016llX", uuid);
+ blkid_set_tag(probe->dev, "UUID", uuid_str, 0);
+ }
+ blkid_set_tag(probe->dev, "LABEL", (char *)hfs->label, hfs->label_len);
+ return 0;
+}
+
+
+#define HFSPLUS_SECTOR_SIZE 512
+
+static int probe_hfsplus(struct blkid_probe *probe,
+ struct blkid_magic *id,
+ unsigned char *buf)
+{
+ struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+ struct hfsplus_bnode_descriptor *descr;
+ struct hfsplus_bheader_record *bnode;
+ struct hfsplus_catalog_key *key;
+ struct hfsplus_vol_header *hfsplus;
+ struct hfs_mdb *sbd = (struct hfs_mdb *) buf;
+ unsigned int alloc_block_size;
+ unsigned int alloc_first_block;
+ unsigned int embed_first_block;
+ unsigned int off = 0;
+ unsigned int blocksize;
+ unsigned int cat_block;
+ unsigned int ext_block_start;
+ unsigned int ext_block_count;
+ unsigned int record_count;
+ unsigned int leaf_node_head;
+ unsigned int leaf_node_count;
+ unsigned int leaf_node_size;
+ unsigned int leaf_block;
+ unsigned int label_len;
+ __u64 leaf_off, uuid;
+ char uuid_str[17], label[512];
+ int ext;
+
+ /* Check for a HFS+ volume embedded in a HFS volume */
+ if (memcmp(sbd->signature, "BD", 2) == 0) {
+ if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
+ (memcmp(sbd->embed_sig, "HX", 2) != 0))
+ /* This must be an HFS volume, so fail */
+ return 1;
+
+ alloc_block_size = blkid_be32(sbd->al_blk_size);
+ alloc_first_block = blkid_be16(sbd->al_bl_st);
+ embed_first_block = blkid_be16(sbd->embed_startblock);
+ off = (alloc_first_block * 512) +
+ (embed_first_block * alloc_block_size);
+ buf = get_buffer(probe, off + (id->bim_kboff * 1024),
+ sizeof(*sbd));
+ if (!buf)
+ return 1;
+
+ hfsplus = (struct hfsplus_vol_header *) buf;
+ }
+
+ hfsplus = (struct hfsplus_vol_header *) buf;
+
+ if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
+ (memcmp(hfsplus->signature, "HX", 2) != 0))
+ return 1;
+
+ memcpy(&uuid, hfsplus->finder_info.id, 8);
+ uuid = blkid_le64(uuid);
+ if (uuid) {
+ sprintf(uuid_str, "%016llX", uuid);
+ blkid_set_tag(probe->dev, "UUID", uuid_str, 0);
+ }
+
+ blocksize = blkid_be32(hfsplus->blocksize);
+ if (blocksize < HFSPLUS_SECTOR_SIZE)
+ return 1;
+
+ memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+ cat_block = blkid_be32(extents[0].start_block);
+
+ buf = get_buffer(probe, off + ((__u64) cat_block * blocksize), 0x2000);
+ if (!buf)
+ return 0;
+
+ bnode = (struct hfsplus_bheader_record *)
+ &buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+ leaf_node_head = blkid_be32(bnode->leaf_head);
+ leaf_node_size = blkid_be16(bnode->node_size);
+ leaf_node_count = blkid_be32(bnode->leaf_count);
+ if (leaf_node_count == 0)
+ return 0;
+
+ leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
+
+ /* get physical location */
+ for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
+ ext_block_start = blkid_be32(extents[ext].start_block);
+ ext_block_count = blkid_be32(extents[ext].block_count);
+ if (ext_block_count == 0)
+ return 0;
+
+ /* this is our extent */
+ if (leaf_block < ext_block_count)
+ break;
+
+ leaf_block -= ext_block_count;
+ }
+ if (ext == HFSPLUS_EXTENT_COUNT)
+ return 0;
+
+ leaf_off = (__u64) (ext_block_start + leaf_block) * blocksize;
+
+ buf = get_buffer(probe, off + leaf_off, leaf_node_size);
+ if (!buf)
+ return 0;
+
+ descr = (struct hfsplus_bnode_descriptor *) buf;
+ record_count = blkid_be16(descr->num_recs);
+ if (record_count == 0)
+ return 0;
+
+ if (descr->type != HFS_NODE_LEAF)
+ return 0;
+
+ key = (struct hfsplus_catalog_key *)
+ &buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+ if (blkid_be32(key->parent_id) != HFSPLUS_POR_CNID)
+ return 0;
+
+ label_len = blkid_be16(key->unicode_len) * 2;
+ unicode_16be_to_utf8((unsigned char *)label, sizeof(label),
+ key->unicode, label_len);
+ blkid_set_tag(probe->dev, "LABEL", label, 0);
+ return 0;
+}
+
+#define LVM2_LABEL_SIZE 512
+static unsigned int lvm2_calc_crc(const void *buf, unsigned int size)
+{
+ static const unsigned int crctab[] = {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+ };
+ unsigned int i, crc = 0xf597a6cf;
+ const __u8 *data = (const __u8 *) buf;
+
+ for (i = 0; i < size; i++) {
+ crc ^= *data++;
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ }
+ return crc;
+}
+
+static int probe_lvm2(struct blkid_probe *probe,
+ struct blkid_magic *id,
+ unsigned char *buf)
+{
+ int sector = (id->bim_kboff) << 1;
+ struct lvm2_pv_label_header *label= (struct lvm2_pv_label_header *)buf;
+ char *p, *q, uuid[40];
+ unsigned int i, b;
+
+ /* buf is at 0k or 1k offset; find label inside */
+ if (memcmp(buf, "LABELONE", 8) == 0) {
+ label = (struct lvm2_pv_label_header *)buf;
+ } else if (memcmp(buf + 512, "LABELONE", 8) == 0) {
+ label = (struct lvm2_pv_label_header *)(buf + 512);
+ sector++;
+ } else {
+ return 1;
+ }
+
+ if (blkid_le64(label->sector_xl) != (unsigned) sector) {
+ DBG(DEBUG_PROBE,
+ printf("LVM2: label for sector %llu found at sector %d\n",
+ blkid_le64(label->sector_xl), sector));
+ return 1;
+ }
+
+ if (lvm2_calc_crc(&label->offset_xl, LVM2_LABEL_SIZE -
+ ((char *)&label->offset_xl - (char *)label)) !=
+ blkid_le32(label->crc_xl)) {
+ DBG(DEBUG_PROBE,
+ printf("LVM2: label checksum incorrect at sector %d\n",
+ sector));
+ return 1;
+ }
+
+ for (i=0, b=1, p=uuid, q= (char *) label->pv_uuid; i < LVM2_ID_LEN;
+ i++, b <<= 1) {
+ if (b & 0x4444440)
+ *p++ = '-';
+ *p++ = *q++;
+ }
+
+ blkid_set_tag(probe->dev, "UUID", uuid, LVM2_ID_LEN+6);
+
+ return 0;
+}
+
+static int probe_btrfs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct btrfs_super_block *bs;
+ const char *label = 0;
+
+ bs = (struct btrfs_super_block *)buf;
+
+ if (strlen(bs->label))
+ label = bs->label;
+ blkid_set_tag(probe->dev, "LABEL", label, sizeof(bs->label));
+ set_uuid(probe->dev, bs->fsid, 0);
+ return 0;
+}
+
+static int probe_f2fs(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct f2fs_super_block *bs;
+
+ bs = (struct f2fs_super_block *)buf;
+ set_uuid(probe->dev, bs->uuid, 0);
+ return 0;
+}
+
+static uint64_t exfat_block_to_offset(const struct exfat_super_block *sb,
+ uint64_t block)
+{
+ return block << sb->block_bits;
+}
+
+static uint64_t exfat_cluster_to_block(const struct exfat_super_block *sb,
+ uint32_t cluster)
+{
+ return sb->cluster_block_start +
+ ((uint64_t)(cluster - EXFAT_FIRST_DATA_CLUSTER) << sb->bpc_bits);
+}
+
+static uint64_t exfat_cluster_to_offset(const struct exfat_super_block *sb,
+ uint32_t cluster)
+{
+ return exfat_block_to_offset(sb, exfat_cluster_to_block(sb, cluster));
+}
+
+static uint32_t exfat_next_cluster(struct blkid_probe *probe,
+ const struct exfat_super_block *sb,
+ uint32_t cluster)
+{
+ uint32_t *next;
+ uint64_t offset;
+
+ offset = exfat_block_to_offset(sb, sb->fat_block_start)
+ + (uint64_t) cluster * sizeof (cluster);
+ next = (uint32_t *)get_buffer(probe, offset, sizeof (uint32_t));
+
+ return next ? *next : 0;
+}
+
+static struct exfat_entry_label *find_exfat_entry_label(
+ struct blkid_probe *probe, const struct exfat_super_block *sb)
+{
+ uint32_t cluster = sb->rootdir_cluster;
+ uint64_t offset = exfat_cluster_to_offset(sb, cluster);
+ uint8_t *entry;
+ const size_t max_iter = 10000;
+ size_t i = 0;
+
+ for (; i < max_iter; ++i) {
+ entry = (uint8_t *)get_buffer(probe, offset, EXFAT_ENTRY_SIZE);
+ if (!entry)
+ return NULL;
+ if (entry[0] == EXFAT_ENTRY_EOD)
+ return NULL;
+ if (entry[0] == EXFAT_ENTRY_LABEL)
+ return (struct exfat_entry_label*) entry;
+
+ offset += EXFAT_ENTRY_SIZE;
+ if (offset % CLUSTER_SIZE(sb) == 0) {
+ cluster = exfat_next_cluster(probe, sb, cluster);
+ if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+ return NULL;
+ if (cluster > EXFAT_LAST_DATA_CLUSTER)
+ return NULL;
+ offset = exfat_cluster_to_offset(sb, cluster);
+ }
+ }
+
+ return NULL;
+}
+
+static int probe_exfat(struct blkid_probe *probe,
+ struct blkid_magic *id __BLKID_ATTR((unused)),
+ unsigned char *buf)
+{
+ struct exfat_super_block *sb;
+ struct exfat_entry_label *label;
+ char uuid[40];
+
+ sb = (struct exfat_super_block *)buf;
+ if (!sb || CLUSTER_SIZE(sb) == 0) {
+ DBG(DEBUG_PROBE, printf("bad exfat superblock.\n"));
+ return errno ? - errno : 1;
+ }
+
+ label = find_exfat_entry_label(probe, sb);
+ if (label) {
+ unsigned char utf8_label[128];
+ unicode_16le_to_utf8(utf8_label, sizeof(utf8_label), label->name, label->length * 2);
+ blkid_set_tag(probe->dev, "LABEL", (char *) utf8_label, 0);
+ } else {
+ blkid_set_tag(probe->dev, "LABEL", "disk", 4);
+ }
+
+ memset(uuid, 0, sizeof (uuid));
+ snprintf(uuid, sizeof (uuid), "%02hhX%02hhX-%02hhX%02hhX",
+ sb->volume_serial[3], sb->volume_serial[2],
+ sb->volume_serial[1], sb->volume_serial[0]);
+ blkid_set_tag(probe->dev, "UUID", uuid, strlen(uuid));
+
+ return 0;
+}
+
+/*
+ * Various filesystem magics that we can check for. Note that kboff and
+ * sboff are in kilobytes and bytes respectively. All magics are in
+ * byte strings so we don't worry about endian issues.
+ */
+static struct blkid_magic type_array[] = {
+/* type kboff sboff len magic probe */
+ { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm },
+ { "ntfs", 0, 3, 8, "NTFS ", probe_ntfs },
+ { "jbd", 1, 0x38, 2, "\123\357", probe_jbd },
+ { "ext4dev", 1, 0x38, 2, "\123\357", probe_ext4dev },
+ { "ext4", 1, 0x38, 2, "\123\357", probe_ext4 },
+ { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 },
+ { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 },
+ { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs },
+ { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs },
+ { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs },
+ { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs },
+ { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs },
+ { "reiser4", 64, 0, 7, "ReIsEr4", probe_reiserfs4 },
+ { "gfs2", 64, 0, 4, "\x01\x16\x19\x70", probe_gfs2 },
+ { "gfs", 64, 0, 4, "\x01\x16\x19\x70", probe_gfs },
+ { "vfat", 0, 0x52, 5, "MSWIN", probe_fat },
+ { "vfat", 0, 0x52, 8, "FAT32 ", probe_fat },
+ { "vfat", 0, 0x36, 5, "MSDOS", probe_fat },
+ { "vfat", 0, 0x36, 8, "FAT16 ", probe_fat },
+ { "vfat", 0, 0x36, 8, "FAT12 ", probe_fat },
+ { "vfat", 0, 0, 1, "\353", probe_fat_nomagic },
+ { "vfat", 0, 0, 1, "\351", probe_fat_nomagic },
+ { "vfat", 0, 0x1fe, 2, "\125\252", probe_fat_nomagic },
+ { "minix", 1, 0x10, 2, "\177\023", 0 },
+ { "minix", 1, 0x10, 2, "\217\023", 0 },
+ { "minix", 1, 0x10, 2, "\150\044", 0 },
+ { "minix", 1, 0x10, 2, "\170\044", 0 },
+ { "vxfs", 1, 0, 4, "\365\374\001\245", 0 },
+ { "xfs", 0, 0, 4, "XFSB", probe_xfs },
+ { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs },
+ { "bfs", 0, 0, 4, "\316\372\173\033", 0 },
+ { "cramfs", 0, 0, 4, "E=\315\050", probe_cramfs },
+ { "qnx4", 0, 4, 6, "QNX4FS", 0 },
+ { "udf", 32, 1, 5, "BEA01", probe_udf },
+ { "udf", 32, 1, 5, "BOOT2", probe_udf },
+ { "udf", 32, 1, 5, "CD001", probe_udf },
+ { "udf", 32, 1, 5, "CDW02", probe_udf },
+ { "udf", 32, 1, 5, "NSR02", probe_udf },
+ { "udf", 32, 1, 5, "NSR03", probe_udf },
+ { "udf", 32, 1, 5, "TEA01", probe_udf },
+ { "iso9660", 32, 1, 5, "CD001", probe_iso9660 },
+ { "iso9660", 32, 9, 5, "CDROM", probe_iso9660 },
+ { "jfs", 32, 0, 4, "JFS1", probe_jfs },
+ /* ZFS has 128 root blocks (#4 is the first used), check only 6 of them */
+ { "zfs", 128, 0, 8, "\0\0\0\0\0\xba\xb1\x0c", probe_zfs },
+ { "zfs", 128, 0, 8, "\x0c\xb1\xba\0\0\0\0\0", probe_zfs },
+ { "zfs", 132, 0, 8, "\0\0\0\0\0\xba\xb1\x0c", probe_zfs },
+ { "zfs", 132, 0, 8, "\x0c\xb1\xba\0\0\0\0\0", probe_zfs },
+ { "zfs", 136, 0, 8, "\0\0\0\0\0\xba\xb1\x0c", probe_zfs },
+ { "zfs", 136, 0, 8, "\x0c\xb1\xba\0\0\0\0\0", probe_zfs },
+ { "zfs", 384, 0, 8, "\0\0\0\0\0\xba\xb1\x0c", probe_zfs },
+ { "zfs", 384, 0, 8, "\x0c\xb1\xba\0\0\0\0\0", probe_zfs },
+ { "zfs", 388, 0, 8, "\0\0\0\0\0\xba\xb1\x0c", probe_zfs },
+ { "zfs", 388, 0, 8, "\x0c\xb1\xba\0\0\0\0\0", probe_zfs },
+ { "zfs", 392, 0, 8, "\0\0\0\0\0\xba\xb1\x0c", probe_zfs },
+ { "zfs", 392, 0, 8, "\x0c\xb1\xba\0\0\0\0\0", probe_zfs },
+ { "hfsplus", 1, 0, 2, "BD", probe_hfsplus },
+ { "hfsplus", 1, 0, 2, "H+", probe_hfsplus },
+ { "hfsplus", 1, 0, 2, "HX", probe_hfsplus },
+ { "hfs", 1, 0, 2, "BD", probe_hfs },
+ { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 },
+ { "hpfs", 8, 0, 4, "I\350\225\371", 0 },
+ { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 },
+ { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swsuspend", 0, 0xff6, 9, "S1SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0xff6, 9, "S2SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0xff6, 9, "ULSUSPEND", probe_swap1 },
+ { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swsuspend", 0, 0x1ff6, 9, "S1SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0x1ff6, 9, "S2SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0x1ff6, 9, "ULSUSPEND", probe_swap1 },
+ { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swsuspend", 0, 0x3ff6, 9, "S1SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0x3ff6, 9, "S2SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0x3ff6, 9, "ULSUSPEND", probe_swap1 },
+ { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swsuspend", 0, 0x7ff6, 9, "S1SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0x7ff6, 9, "S2SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0x7ff6, 9, "ULSUSPEND", probe_swap1 },
+ { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 },
+ { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 },
+ { "swsuspend", 0, 0xfff6, 9, "S1SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0xfff6, 9, "S2SUSPEND", probe_swap1 },
+ { "swsuspend", 0, 0xfff6, 9, "ULSUSPEND", probe_swap1 },
+ { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs },
+ { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 },
+ { "crypt_LUKS", 0, 0, 6, "LUKS\xba\xbe", probe_luks },
+ { "squashfs", 0, 0, 4, "sqsh", 0 },
+ { "squashfs", 0, 0, 4, "hsqs", 0 },
+ { "lvm2pv", 0, 0x218, 8, "LVM2 001", probe_lvm2 },
+ { "lvm2pv", 0, 0x018, 8, "LVM2 001", probe_lvm2 },
+ { "lvm2pv", 1, 0x018, 8, "LVM2 001", probe_lvm2 },
+ { "lvm2pv", 1, 0x218, 8, "LVM2 001", probe_lvm2 },
+ { "btrfs", 64, 0x40, 8, "_BHRfS_M", probe_btrfs },
+ { "f2fs", 1, 0, 4, "\x10\x20\xf5\xf2", probe_f2fs },
+ { "exfat", 0, 3, 8, "EXFAT ", probe_exfat },
+ { NULL, 0, 0, 0, NULL, NULL }
+};
+
+/*
+ * Verify that the data in dev is consistent with what is on the actual
+ * block device (using the devname field only). Normally this will be
+ * called when finding items in the cache, but for long running processes
+ * is also desirable to revalidate an item before use.
+ *
+ * If we are unable to revalidate the data, we return the old data and
+ * do not set the BLKID_BID_FL_VERIFIED flag on it.
+ */
+blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
+{
+ struct blkid_magic *id;
+ struct blkid_probe probe;
+ blkid_tag_iterate iter;
+ unsigned char *buf;
+ const char *type, *value;
+ struct stat st;
+ time_t now;
+ double diff;
+ int idx;
+
+ if (!dev)
+ return NULL;
+
+ now = time(0);
+ diff = difftime(now, dev->bid_time);
+
+ if (stat(dev->bid_name, &st) < 0) {
+ DBG(DEBUG_PROBE,
+ printf("blkid_verify: error %s (%d) while "
+ "trying to stat %s\n", strerror(errno), errno,
+ dev->bid_name));
+ open_err:
+ if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) {
+ /* We don't have read permission, just return cache data. */
+ DBG(DEBUG_PROBE, printf("returning unverified data for %s\n",
+ dev->bid_name));
+ return dev;
+ }
+ blkid_free_dev(dev);
+ return NULL;
+ }
+
+ if ((now >= dev->bid_time) &&
+ (st.st_mtime <= dev->bid_time) &&
+ ((diff < BLKID_PROBE_MIN) ||
+ (dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+ diff < BLKID_PROBE_INTERVAL)))
+ return dev;
+
+ DBG(DEBUG_PROBE,
+ printf("need to revalidate %s (cache time %lu, stat time %lu,\n\t"
+ "time since last check %lu)\n",
+ dev->bid_name, (unsigned long)dev->bid_time,
+ (unsigned long)st.st_mtime, (unsigned long)diff));
+
+ if ((probe.fd = open(dev->bid_name, O_RDONLY)) < 0) {
+ DBG(DEBUG_PROBE, printf("blkid_verify: error %s (%d) while "
+ "opening %s\n", strerror(errno), errno,
+ dev->bid_name));
+ goto open_err;
+ }
+
+ probe.cache = cache;
+ probe.dev = dev;
+ probe.sbbuf = 0;
+ probe.buf = 0;
+ probe.buf_max = 0;
+
+ /*
+ * Iterate over the type array. If we already know the type,
+ * then try that first. If it doesn't work, then blow away
+ * the type information, and try again.
+ *
+ */
+try_again:
+ type = 0;
+ if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) {
+ uuid_t uuid;
+
+ if (check_mdraid(probe.fd, uuid) == 0) {
+ set_uuid(dev, uuid, 0);
+ type = "mdraid";
+ goto found_type;
+ }
+ }
+ for (id = type_array; id->bim_type; id++) {
+ if (dev->bid_type &&
+ strcmp(id->bim_type, dev->bid_type))
+ continue;
+
+ idx = id->bim_kboff + (id->bim_sboff >> 10);
+ buf = get_buffer(&probe, (__u64) idx << 10, 1024);
+ if (!buf)
+ continue;
+
+ if (memcmp(id->bim_magic, buf + (id->bim_sboff & 0x3ff),
+ id->bim_len))
+ continue;
+
+ if ((id->bim_probe == NULL) ||
+ (id->bim_probe(&probe, id, buf) == 0)) {
+ type = id->bim_type;
+ goto found_type;
+ }
+ }
+
+ if (!id->bim_type && dev->bid_type) {
+ /*
+ * Zap the device filesystem information and try again
+ */
+ DBG(DEBUG_PROBE,
+ printf("previous fs type %s not valid, "
+ "trying full probe\n", dev->bid_type));
+ iter = blkid_tag_iterate_begin(dev);
+ while (blkid_tag_next(iter, &type, &value) == 0)
+ blkid_set_tag(dev, type, 0, 0);
+ blkid_tag_iterate_end(iter);
+ goto try_again;
+ }
+
+ if (!dev->bid_type) {
+ blkid_free_dev(dev);
+ dev = 0;
+ goto found_type;
+ }
+
+found_type:
+ if (dev && type) {
+ dev->bid_devno = st.st_rdev;
+ dev->bid_time = time(0);
+ dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+
+ blkid_set_tag(dev, "TYPE", type, 0);
+
+ DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n",
+ dev->bid_name, (long long)st.st_rdev, type));
+ }
+
+ free(probe.sbbuf);
+ free(probe.buf);
+ if (probe.fd >= 0)
+ close(probe.fd);
+
+ return dev;
+}
+
+int blkid_known_fstype(const char *fstype)
+{
+ struct blkid_magic *id;
+
+ for (id = type_array; id->bim_type; id++) {
+ if (strcmp(fstype, id->bim_type) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_dev dev;
+ blkid_cache cache;
+ int ret;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s device\n"
+ "Probe a single device to determine type\n", argv[0]);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL);
+ if (!dev) {
+ printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
+ return (1);
+ }
+ printf("TYPE='%s'\n", dev->bid_type ? dev->bid_type : "(null)");
+ if (dev->bid_label)
+ printf("LABEL='%s'\n", dev->bid_label);
+ if (dev->bid_uuid)
+ printf("UUID='%s'\n", dev->bid_uuid);
+
+ blkid_free_dev(dev);
+ return (0);
+}
+#endif
diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h
new file mode 100644
index 0000000..063a5b5
--- /dev/null
+++ b/lib/blkid/probe.h
@@ -0,0 +1,852 @@
+/*
+ * probe.h - constants and on-disk structures for extracting device data
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_PROBE_H
+#define _BLKID_PROBE_H
+
+#include <stdint.h>
+
+#include <blkid/blkid_types.h>
+
+struct blkid_magic;
+
+#define SB_BUFFER_SIZE 0x11000
+
+struct blkid_probe {
+ int fd;
+ blkid_cache cache;
+ blkid_dev dev;
+ unsigned char *sbbuf;
+ size_t sb_valid;
+ unsigned char *buf;
+ size_t buf_max;
+};
+
+typedef int (*blkid_probe_t)(struct blkid_probe *probe,
+ struct blkid_magic *id, unsigned char *buf);
+
+struct blkid_magic {
+ const char *bim_type; /* type name for this magic */
+ long bim_kboff; /* kilobyte offset of superblock */
+ unsigned bim_sboff; /* byte offset within superblock */
+ unsigned bim_len; /* length of magic */
+ const char *bim_magic; /* magic string */
+ blkid_probe_t bim_probe; /* probe function */
+};
+
+/*
+ * Structures for each of the content types we want to extract information
+ * from. We do not necessarily need the magic field here, because we have
+ * already identified the content type before we get this far. It may still
+ * be useful if there are probe functions which handle multiple content types.
+ */
+struct ext2_super_block {
+ __u32 s_inodes_count;
+ __u32 s_blocks_count;
+ __u32 s_r_blocks_count;
+ __u32 s_free_blocks_count;
+ __u32 s_free_inodes_count;
+ __u32 s_first_data_block;
+ __u32 s_log_block_size;
+ __u32 s_dummy3[7];
+ unsigned char s_magic[2];
+ __u16 s_state;
+ __u32 s_dummy5[8];
+ __u32 s_feature_compat;
+ __u32 s_feature_incompat;
+ __u32 s_feature_ro_compat;
+ unsigned char s_uuid[16];
+ char s_volume_name[16];
+ char s_last_mounted[64];
+ __u32 s_algorithm_usage_bitmap;
+ __u8 s_prealloc_blocks;
+ __u8 s_prealloc_dir_blocks;
+ __u16 s_reserved_gdt_blocks;
+ __u8 s_journal_uuid[16];
+ __u32 s_journal_inum;
+ __u32 s_journal_dev;
+ __u32 s_last_orphan;
+ __u32 s_hash_seed[4];
+ __u8 s_def_hash_version;
+ __u8 s_jnl_backup_type;
+ __u16 s_reserved_word_pad;
+ __u32 s_default_mount_opts;
+ __u32 s_first_meta_bg;
+ __u32 s_mkfs_time;
+ __u32 s_jnl_blocks[17];
+ __u32 s_blocks_count_hi;
+ __u32 s_r_blocks_count_hi;
+ __u32 s_free_blocks_hi;
+ __u16 s_min_extra_isize;
+ __u16 s_want_extra_isize;
+ __u32 s_flags;
+ __u16 s_raid_stride;
+ __u16 s_mmp_interval;
+ __u64 s_mmp_block;
+ __u32 s_raid_stripe_width;
+ __u32 s_reserved[163];
+};
+
+/* for s_flags */
+#define EXT2_FLAGS_TEST_FILESYS 0x0004
+
+/* for s_feature_compat */
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
+
+/* for s_feature_ro_compat */
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
+#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_QUOTA 0x0100
+#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
+
+/* for s_feature_incompat */
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
+#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
+#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */
+#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
+
+#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
+ EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP
+#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP
+
+#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+ EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
+ EXT3_FEATURE_INCOMPAT_RECOVER| \
+ EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP
+#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP
+
+
+struct xfs_super_block {
+ unsigned char xs_magic[4];
+ __u32 xs_blocksize;
+ __u64 xs_dblocks;
+ __u64 xs_rblocks;
+ __u32 xs_dummy1[2];
+ unsigned char xs_uuid[16];
+ __u32 xs_dummy2[15];
+ char xs_fname[12];
+ __u32 xs_dummy3[2];
+ __u64 xs_icount;
+ __u64 xs_ifree;
+ __u64 xs_fdblocks;
+};
+
+struct reiserfs_super_block {
+ __u32 rs_blocks_count;
+ __u32 rs_free_blocks;
+ __u32 rs_root_block;
+ __u32 rs_journal_block;
+ __u32 rs_journal_dev;
+ __u32 rs_orig_journal_size;
+ __u32 rs_dummy2[5];
+ __u16 rs_blocksize;
+ __u16 rs_dummy3[3];
+ unsigned char rs_magic[12];
+ __u32 rs_dummy4[5];
+ unsigned char rs_uuid[16];
+ char rs_label[16];
+};
+
+struct reiser4_super_block {
+ unsigned char rs4_magic[16];
+ __u16 rs4_dummy[2];
+ unsigned char rs4_uuid[16];
+ unsigned char rs4_label[16];
+ __u64 rs4_dummy2;
+};
+
+struct jfs_super_block {
+ unsigned char js_magic[4];
+ __u32 js_version;
+ __u64 js_size;
+ __u32 js_bsize; /* 4: aggregate block size in bytes */
+ __u16 js_l2bsize; /* 2: log2 of s_bsize */
+ __u16 js_l2bfactor; /* 2: log2(s_bsize/hardware block size) */
+ __u32 js_pbsize; /* 4: hardware/LVM block size in bytes */
+ __u16 js_l2pbsize; /* 2: log2 of s_pbsize */
+ __u16 js_pad; /* 2: padding necessary for alignment */
+ __u32 js_dummy2[26];
+ unsigned char js_uuid[16];
+ unsigned char js_label[16];
+ unsigned char js_loguuid[16];
+};
+
+struct romfs_super_block {
+ unsigned char ros_magic[8];
+ __u32 ros_dummy1[2];
+ unsigned char ros_volume[16];
+};
+
+struct cramfs_super_block {
+ __u8 magic[4];
+ __u32 size;
+ __u32 flags;
+ __u32 future;
+ __u8 signature[16];
+ struct cramfs_info {
+ __u32 crc;
+ __u32 edition;
+ __u32 blocks;
+ __u32 files;
+ } info;
+ __u8 name[16];
+};
+
+struct swap_id_block {
+/* unsigned char sws_boot[1024]; */
+ __u32 sws_version;
+ __u32 sws_lastpage;
+ __u32 sws_nrbad;
+ unsigned char sws_uuid[16];
+ char sws_volume[16];
+ unsigned char sws_pad[117];
+ __u32 sws_badpg;
+};
+
+/* Yucky misaligned values */
+struct vfat_super_block {
+/* 00*/ unsigned char vs_ignored[3];
+/* 03*/ unsigned char vs_sysid[8];
+/* 0b*/ unsigned char vs_sector_size[2];
+/* 0d*/ __u8 vs_cluster_size;
+/* 0e*/ __u16 vs_reserved;
+/* 10*/ __u8 vs_fats;
+/* 11*/ unsigned char vs_dir_entries[2];
+/* 13*/ unsigned char vs_sectors[2];
+/* 15*/ unsigned char vs_media;
+/* 16*/ __u16 vs_fat_length;
+/* 18*/ __u16 vs_secs_track;
+/* 1a*/ __u16 vs_heads;
+/* 1c*/ __u32 vs_hidden;
+/* 20*/ __u32 vs_total_sect;
+/* 24*/ __u32 vs_fat32_length;
+/* 28*/ __u16 vs_flags;
+/* 2a*/ __u8 vs_version[2];
+/* 2c*/ __u32 vs_root_cluster;
+/* 30*/ __u16 vs_insfo_sector;
+/* 32*/ __u16 vs_backup_boot;
+/* 34*/ __u16 vs_reserved2[6];
+/* 40*/ unsigned char vs_unknown[3];
+/* 43*/ unsigned char vs_serno[4];
+/* 47*/ unsigned char vs_label[11];
+/* 52*/ unsigned char vs_magic[8];
+/* 5a*/ unsigned char vs_dummy2[164];
+/*1fe*/ unsigned char vs_pmagic[2];
+};
+
+/* Yucky misaligned values */
+struct msdos_super_block {
+/* 00*/ unsigned char ms_ignored[3];
+/* 03*/ unsigned char ms_sysid[8];
+/* 0b*/ unsigned char ms_sector_size[2];
+/* 0d*/ __u8 ms_cluster_size;
+/* 0e*/ __u16 ms_reserved;
+/* 10*/ __u8 ms_fats;
+/* 11*/ unsigned char ms_dir_entries[2];
+/* 13*/ unsigned char ms_sectors[2];
+/* 15*/ unsigned char ms_media;
+/* 16*/ __u16 ms_fat_length;
+/* 18*/ __u16 ms_secs_track;
+/* 1a*/ __u16 ms_heads;
+/* 1c*/ __u32 ms_hidden;
+/* 20*/ __u32 ms_total_sect;
+/* 24*/ unsigned char ms_unknown[3];
+/* 27*/ unsigned char ms_serno[4];
+/* 2b*/ unsigned char ms_label[11];
+/* 36*/ unsigned char ms_magic[8];
+/* 3d*/ unsigned char ms_dummy2[192];
+/*1fe*/ unsigned char ms_pmagic[2];
+};
+
+struct vfat_dir_entry {
+ __u8 name[11];
+ __u8 attr;
+ __u16 time_creat;
+ __u16 date_creat;
+ __u16 time_acc;
+ __u16 date_acc;
+ __u16 cluster_high;
+ __u16 time_write;
+ __u16 date_write;
+ __u16 cluster_low;
+ __u32 size;
+};
+
+/* maximum number of clusters */
+#define FAT12_MAX 0xFF4
+#define FAT16_MAX 0xFFF4
+#define FAT32_MAX 0x0FFFFFF6
+
+struct minix_super_block {
+ __u16 ms_ninodes;
+ __u16 ms_nzones;
+ __u16 ms_imap_blocks;
+ __u16 ms_zmap_blocks;
+ __u16 ms_firstdatazone;
+ __u16 ms_log_zone_size;
+ __u32 ms_max_size;
+ unsigned char ms_magic[2];
+ __u16 ms_state;
+ __u32 ms_zones;
+};
+
+struct mdp_superblock_s {
+ __u32 md_magic;
+ __u32 major_version;
+ __u32 minor_version;
+ __u32 patch_version;
+ __u32 gvalid_words;
+ __u32 set_uuid0;
+ __u32 ctime;
+ __u32 level;
+ __u32 size;
+ __u32 nr_disks;
+ __u32 raid_disks;
+ __u32 md_minor;
+ __u32 not_persistent;
+ __u32 set_uuid1;
+ __u32 set_uuid2;
+ __u32 set_uuid3;
+};
+
+struct hfs_super_block {
+ char h_magic[2];
+ char h_dummy[18];
+ __u32 h_blksize;
+};
+
+struct ocfs_volume_header {
+ unsigned char minor_version[4];
+ unsigned char major_version[4];
+ unsigned char signature[128];
+ char mount[128];
+ unsigned char mount_len[2];
+};
+
+struct ocfs_volume_label {
+ unsigned char disk_lock[48];
+ char label[64];
+ unsigned char label_len[2];
+ unsigned char vol_id[16];
+ unsigned char vol_id_len[2];
+};
+
+#define ocfsmajor(o) ((__u32)o.major_version[0] \
+ + (((__u32) o.major_version[1]) << 8) \
+ + (((__u32) o.major_version[2]) << 16) \
+ + (((__u32) o.major_version[3]) << 24))
+#define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8))
+#define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8))
+
+#define OCFS_MAGIC "OracleCFS"
+
+struct ocfs2_super_block {
+ unsigned char signature[8];
+ unsigned char s_dummy1[184];
+ unsigned char s_dummy2[80];
+ char s_label[64];
+ unsigned char s_uuid[16];
+};
+
+#define OCFS2_MIN_BLOCKSIZE 512
+#define OCFS2_MAX_BLOCKSIZE 4096
+
+#define OCFS2_SUPER_BLOCK_BLKNO 2
+
+#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2"
+
+struct oracle_asm_disk_label {
+ char dummy[32];
+ char dl_tag[8];
+ char dl_id[24];
+};
+
+#define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK"
+#define ORACLE_ASM_DISK_LABEL_OFFSET 32
+
+struct iso_volume_descriptor {
+ unsigned char vd_type;
+ unsigned char vd_id[5];
+ unsigned char vd_version;
+ unsigned char flags;
+ unsigned char system_id[32];
+ unsigned char volume_id[32];
+ unsigned char unused[8];
+ unsigned char space_size[8];
+ unsigned char escape_sequences[8];
+};
+
+/* Common gfs/gfs2 constants: */
+#define GFS_MAGIC 0x01161970
+#define GFS_DEFAULT_BSIZE 4096
+#define GFS_SUPERBLOCK_OFFSET (0x10 * GFS_DEFAULT_BSIZE)
+#define GFS_METATYPE_SB 1
+#define GFS_FORMAT_SB 100
+#define GFS_LOCKNAME_LEN 64
+
+/* gfs1 constants: */
+#define GFS_FORMAT_FS 1309
+#define GFS_FORMAT_MULTI 1401
+/* gfs2 constants: */
+#define GFS2_FORMAT_FS 1801
+#define GFS2_FORMAT_MULTI 1900
+
+struct gfs2_meta_header {
+ __u32 mh_magic;
+ __u32 mh_type;
+ __u64 __pad0; /* Was generation number in gfs1 */
+ __u32 mh_format;
+ __u32 __pad1; /* Was incarnation number in gfs1 */
+};
+
+struct gfs2_inum {
+ __u64 no_formal_ino;
+ __u64 no_addr;
+};
+
+struct gfs2_sb {
+ struct gfs2_meta_header sb_header;
+
+ __u32 sb_fs_format;
+ __u32 sb_multihost_format;
+ __u32 __pad0; /* Was superblock flags in gfs1 */
+
+ __u32 sb_bsize;
+ __u32 sb_bsize_shift;
+ __u32 __pad1; /* Was journal segment size in gfs1 */
+
+ struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */
+ struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */
+ struct gfs2_inum sb_root_dir;
+
+ char sb_lockproto[GFS_LOCKNAME_LEN];
+ char sb_locktable[GFS_LOCKNAME_LEN];
+ /* In gfs1, quota and license dinodes followed */
+};
+
+struct ntfs_super_block {
+ __u8 jump[3];
+ __u8 oem_id[8];
+ __u8 bios_parameter_block[25];
+ __u16 unused[2];
+ __u64 number_of_sectors;
+ __u64 mft_cluster_location;
+ __u64 mft_mirror_cluster_location;
+ __s8 cluster_per_mft_record;
+ __u8 reserved1[3];
+ __s8 cluster_per_index_record;
+ __u8 reserved2[3];
+ __u64 volume_serial;
+ __u16 checksum;
+};
+
+struct master_file_table_record {
+ __u32 magic;
+ __u16 usa_ofs;
+ __u16 usa_count;
+ __u64 lsn;
+ __u16 sequence_number;
+ __u16 link_count;
+ __u16 attrs_offset;
+ __u16 flags;
+ __u32 bytes_in_use;
+ __u32 bytes_allocated;
+} __attribute__((__packed__));
+
+struct file_attribute {
+ __u32 type;
+ __u32 len;
+ __u8 non_resident;
+ __u8 name_len;
+ __u16 name_offset;
+ __u16 flags;
+ __u16 instance;
+ __u32 value_len;
+ __u16 value_offset;
+} __attribute__((__packed__));
+
+#define MFT_RECORD_VOLUME 3
+#define MFT_RECORD_ATTR_VOLUME_NAME 0x60
+#define MFT_RECORD_ATTR_VOLUME_INFO 0x70
+#define MFT_RECORD_ATTR_OBJECT_ID 0x40
+#define MFT_RECORD_ATTR_END 0xffffffffu
+
+/* HFS / HFS+ */
+struct hfs_finder_info {
+ __u32 boot_folder;
+ __u32 start_app;
+ __u32 open_folder;
+ __u32 os9_folder;
+ __u32 reserved;
+ __u32 osx_folder;
+ __u8 id[8];
+} __attribute__((packed));
+
+struct hfs_mdb {
+ __u8 signature[2];
+ __u32 cr_date;
+ __u32 ls_Mod;
+ __u16 atrb;
+ __u16 nm_fls;
+ __u16 vbm_st;
+ __u16 alloc_ptr;
+ __u16 nm_al_blks;
+ __u32 al_blk_size;
+ __u32 clp_size;
+ __u16 al_bl_st;
+ __u32 nxt_cnid;
+ __u16 free_bks;
+ __u8 label_len;
+ __u8 label[27];
+ __u32 vol_bkup;
+ __u16 vol_seq_num;
+ __u32 wr_cnt;
+ __u32 xt_clump_size;
+ __u32 ct_clump_size;
+ __u16 num_root_dirs;
+ __u32 file_count;
+ __u32 dir_count;
+ struct hfs_finder_info finder_info;
+ __u8 embed_sig[2];
+ __u16 embed_startblock;
+ __u16 embed_blockcount;
+} __attribute__((packed));
+
+
+#define HFS_NODE_LEAF 0xff
+#define HFSPLUS_POR_CNID 1
+
+struct hfsplus_bnode_descriptor {
+ __u32 next;
+ __u32 prev;
+ __u8 type;
+ __u8 height;
+ __u16 num_recs;
+ __u16 reserved;
+} __attribute__((packed));
+
+struct hfsplus_bheader_record {
+ __u16 depth;
+ __u32 root;
+ __u32 leaf_count;
+ __u32 leaf_head;
+ __u32 leaf_tail;
+ __u16 node_size;
+} __attribute__((packed));
+
+struct hfsplus_catalog_key {
+ __u16 key_len;
+ __u32 parent_id;
+ __u16 unicode_len;
+ __u8 unicode[255 * 2];
+} __attribute__((packed));
+
+struct hfsplus_extent {
+ __u32 start_block;
+ __u32 block_count;
+} __attribute__((packed));
+
+#define HFSPLUS_EXTENT_COUNT 8
+struct hfsplus_fork {
+ __u64 total_size;
+ __u32 clump_size;
+ __u32 total_blocks;
+ struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} __attribute__((packed));
+
+struct hfsplus_vol_header {
+ __u8 signature[2];
+ __u16 version;
+ __u32 attributes;
+ __u32 last_mount_vers;
+ __u32 reserved;
+ __u32 create_date;
+ __u32 modify_date;
+ __u32 backup_date;
+ __u32 checked_date;
+ __u32 file_count;
+ __u32 folder_count;
+ __u32 blocksize;
+ __u32 total_blocks;
+ __u32 free_blocks;
+ __u32 next_alloc;
+ __u32 rsrc_clump_sz;
+ __u32 data_clump_sz;
+ __u32 next_cnid;
+ __u32 write_count;
+ __u64 encodings_bmp;
+ struct hfs_finder_info finder_info;
+ struct hfsplus_fork alloc_file;
+ struct hfsplus_fork ext_file;
+ struct hfsplus_fork cat_file;
+ struct hfsplus_fork attr_file;
+ struct hfsplus_fork start_file;
+} __attribute__((packed));
+
+
+/* this is lvm's label_header & pv_header combined. */
+
+#define LVM2_ID_LEN 32
+
+struct lvm2_pv_label_header {
+ /* label_header */
+ __u8 id[8]; /* LABELONE */
+ __u64 sector_xl; /* Sector number of this label */
+ __u32 crc_xl; /* From next field to end of sector */
+ __u32 offset_xl; /* Offset from start of struct to contents */
+ __u8 type[8]; /* LVM2 001 */
+ /* pv_header */
+ __u8 pv_uuid[LVM2_ID_LEN];
+} __attribute__ ((packed));
+
+
+/*
+ * this is a very generous portion of the super block, giving us
+ * room to translate 14 chunks with 3 stripes each.
+ */
+#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_UUID_SIZE 16
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_CSUM_SIZE 32
+
+struct btrfs_dev_item {
+ /* the internal btrfs device id */
+ __u64 devid;
+
+ /* size of the device */
+ __u64 total_bytes;
+
+ /* bytes used */
+ __u64 bytes_used;
+
+ /* optimal io alignment for this device */
+ __u32 io_align;
+
+ /* optimal io width for this device */
+ __u32 io_width;
+
+ /* minimal io size for this device */
+ __u32 sector_size;
+
+ /* type and info about this device */
+ __u64 type;
+
+ /* expected generation for this device */
+ __u64 generation;
+
+ /*
+ * starting byte of this partition on the device,
+ * to allow for stripe alignment in the future
+ */
+ __u64 start_offset;
+
+ /* grouping information for allocation decisions */
+ __u32 dev_group;
+
+ /* seek speed 0-100 where 100 is fastest */
+ __u8 seek_speed;
+
+ /* bandwidth 0-100 where 100 is fastest */
+ __u8 bandwidth;
+
+ /* btrfs generated uuid for this device */
+ __u8 uuid[BTRFS_UUID_SIZE];
+
+ /* uuid of FS who owns this device */
+ __u8 fsid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+/*
+ * the super block basically lists the main trees of the FS
+ * it currently lacks any block count etc etc
+ */
+struct btrfs_super_block {
+ __u8 csum[BTRFS_CSUM_SIZE];
+ /* the first 3 fields must match struct btrfs_header */
+ __u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __u64 bytenr; /* this block number */
+ __u64 flags;
+
+ /* allowed to be different from the btrfs_header from here own down */
+ __u64 magic;
+ __u64 generation;
+ __u64 root;
+ __u64 chunk_root;
+ __u64 log_root;
+
+ /* this will help find the new super based on the log root */
+ __u64 log_root_transid;
+ __u64 total_bytes;
+ __u64 bytes_used;
+ __u64 root_dir_objectid;
+ __u64 num_devices;
+ __u32 sectorsize;
+ __u32 nodesize;
+ __u32 leafsize;
+ __u32 stripesize;
+ __u32 sys_chunk_array_size;
+ __u64 chunk_root_generation;
+ __u64 compat_flags;
+ __u64 compat_ro_flags;
+ __u64 incompat_flags;
+ __u16 csum_type;
+ __u8 root_level;
+ __u8 chunk_root_level;
+ __u8 log_root_level;
+ struct btrfs_dev_item dev_item;
+
+ char label[BTRFS_LABEL_SIZE];
+
+ /* future expansion */
+ __u64 reserved[32];
+ __u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
+} __attribute__ ((__packed__));
+
+#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
+
+struct f2fs_super_block {
+ __u32 magic; /* Magic Number */
+ __u16 major_ver; /* Major Version */
+ __u16 minor_ver; /* Minor Version */
+ __u32 log_sectorsize; /* log2 sector size in bytes */
+ __u32 log_sectors_per_block; /* log2 # of sectors per block */
+ __u32 log_blocksize; /* log2 block size in bytes */
+ __u32 log_blocks_per_seg; /* log2 # of blocks per segment */
+ __u32 segs_per_sec; /* # of segments per section */
+ __u32 secs_per_zone; /* # of sections per zone */
+ __u32 checksum_offset; /* checksum offset inside super block */
+ __u64 block_count; /* total # of user blocks */
+ __u32 section_count; /* total # of sections */
+ __u32 segment_count; /* total # of segments */
+ __u32 segment_count_ckpt; /* # of segments for checkpoint */
+ __u32 segment_count_sit; /* # of segments for SIT */
+ __u32 segment_count_nat; /* # of segments for NAT */
+ __u32 segment_count_ssa; /* # of segments for SSA */
+ __u32 segment_count_main; /* # of segments for main area */
+ __u32 segment0_blkaddr; /* start block address of segment 0 */
+ __u32 cp_blkaddr; /* start block address of checkpoint */
+ __u32 sit_blkaddr; /* start block address of SIT */
+ __u32 nat_blkaddr; /* start block address of NAT */
+ __u32 ssa_blkaddr; /* start block address of SSA */
+ __u32 main_blkaddr; /* start block address of main area */
+ __u32 root_ino; /* root inode number */
+ __u32 node_ino; /* node inode number */
+ __u32 meta_ino; /* meta inode number */
+ __u8 uuid[16]; /* 128-bit uuid for volume */
+ __u16 volume_name[512]; /* volume name */
+ __u32 extension_count; /* # of extensions below */
+ __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */
+} __attribute__((__packed__));
+
+struct exfat_super_block {
+ uint8_t jump[3];
+ uint8_t oem_name[8];
+ uint8_t __unused1[53];
+ uint64_t block_start;
+ uint64_t block_count;
+ uint32_t fat_block_start;
+ uint32_t fat_block_count;
+ uint32_t cluster_block_start;
+ uint32_t cluster_count;
+ uint32_t rootdir_cluster;
+ uint8_t volume_serial[4];
+ struct {
+ uint8_t vermin;
+ uint8_t vermaj;
+ } version;
+ uint16_t volume_state;
+ uint8_t block_bits;
+ uint8_t bpc_bits;
+ uint8_t fat_count;
+ uint8_t drive_no;
+ uint8_t allocated_percent;
+} __attribute__((__packed__));
+
+struct exfat_entry_label {
+ uint8_t type;
+ uint8_t length;
+ uint8_t name[30];
+} __attribute__((__packed__));
+
+#define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
+#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
+
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER 0xffffff6
+#define EXFAT_ENTRY_SIZE 32
+
+#define EXFAT_ENTRY_EOD 0x00
+#define EXFAT_ENTRY_LABEL 0x83
+
+/*
+ * Byte swap functions
+ */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+_INLINE_ __u16 blkid_swab16(__u16 val)
+{
+ return (val >> 8) | (val << 8);
+}
+
+_INLINE_ __u32 blkid_swab32(__u32 val)
+{
+ return ((val>>24) | ((val>>8)&0xFF00) |
+ ((val<<8)&0xFF0000) | (val<<24));
+}
+
+_INLINE_ __u64 blkid_swab64(__u64 val)
+{
+ return (blkid_swab32(val >> 32) |
+ (((__u64) blkid_swab32(val & 0xFFFFFFFFUL)) << 32));
+}
+
+#ifdef WORDS_BIGENDIAN
+#define blkid_le16(x) blkid_swab16(x)
+#define blkid_le32(x) blkid_swab32(x)
+#define blkid_le64(x) blkid_swab64(x)
+#define blkid_be16(x) (x)
+#define blkid_be32(x) (x)
+#define blkid_be64(x) (x)
+#else
+#define blkid_le16(x) (x)
+#define blkid_le32(x) (x)
+#define blkid_le64(x) (x)
+#define blkid_be16(x) blkid_swab16(x)
+#define blkid_be32(x) blkid_swab32(x)
+#define blkid_be64(x) blkid_swab64(x)
+#endif
+
+#undef _INLINE_
+
+#endif /* _BLKID_PROBE_H */
diff --git a/lib/blkid/read.c b/lib/blkid/read.c
new file mode 100644
index 0000000..b894856
--- /dev/null
+++ b/lib/blkid/read.c
@@ -0,0 +1,494 @@
+/*
+ * read.c - read the blkid cache from disk, to avoid scanning all devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Y. Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _XOPEN_SOURCE 600 /* for inclusion of strtoull */
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "blkidP.h"
+#include "uuid/uuid.h"
+
+#ifdef HAVE_STRTOULL
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev) (debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#endif
+
+/*
+ * File format:
+ *
+ * <device [<NAME="value"> ...]>device_name</device>
+ *
+ * The following tags are required for each entry:
+ * <ID="id"> unique (within this file) ID number of this device
+ * <TIME="time"> (ascii time_t) time this entry was last read from disk
+ * <TYPE="type"> (detected) type of filesystem/data for this partition
+ *
+ * The following tags may be present, depending on the device contents
+ * <LABEL="label"> (user supplied) label (volume name, etc)
+ * <UUID="uuid"> (generated) universally unique identifier (serial no)
+ */
+
+static char *skip_over_blank(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ char ch;
+
+ while ((ch = *cp)) {
+ /* If we see a backslash, skip the next character */
+ if (ch == '\\') {
+ cp++;
+ if (*cp == '\0')
+ break;
+ cp++;
+ continue;
+ }
+ if (isspace(ch) || ch == '<' || ch == '>')
+ break;
+ cp++;
+ }
+ return cp;
+}
+
+static char *strip_line(char *line)
+{
+ char *p;
+
+ line = skip_over_blank(line);
+
+ p = line + strlen(line) - 1;
+
+ while (*line) {
+ if (isspace(*p))
+ *p-- = '\0';
+ else
+ break;
+ }
+
+ return line;
+}
+
+#if 0
+static char *parse_word(char **buf)
+{
+ char *word, *next;
+
+ word = *buf;
+ if (*word == '\0')
+ return NULL;
+
+ word = skip_over_blank(word);
+ next = skip_over_word(word);
+ if (*next) {
+ char *end = next - 1;
+ if (*end == '"' || *end == '\'')
+ *end = '\0';
+ *next++ = '\0';
+ }
+ *buf = next;
+
+ if (*word == '"' || *word == '\'')
+ word++;
+ return word;
+}
+#endif
+
+/*
+ * Start parsing a new line from the cache.
+ *
+ * line starts with "<device" return 1 -> continue parsing line
+ * line starts with "<foo", empty, or # return 0 -> skip line
+ * line starts with other, return -BLKID_ERR_CACHE -> error
+ */
+static int parse_start(char **cp)
+{
+ char *p;
+
+ p = strip_line(*cp);
+
+ /* Skip comment or blank lines. We can't just NUL the first '#' char,
+ * in case it is inside quotes, or escaped.
+ */
+ if (*p == '\0' || *p == '#')
+ return 0;
+
+ if (!strncmp(p, "<device", 7)) {
+ DBG(DEBUG_READ, printf("found device header: %8s\n", p));
+ p += 7;
+
+ *cp = p;
+ return 1;
+ }
+
+ if (*p == '<')
+ return 0;
+
+ return -BLKID_ERR_CACHE;
+}
+
+/* Consume the remaining XML on the line (cosmetic only) */
+static int parse_end(char **cp)
+{
+ *cp = skip_over_blank(*cp);
+
+ if (!strncmp(*cp, "</device>", 9)) {
+ DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
+ *cp += 9;
+ return 0;
+ }
+
+ return -BLKID_ERR_CACHE;
+}
+
+/*
+ * Allocate a new device struct with device name filled in. Will handle
+ * finding the device on lines of the form:
+ * <device foo=bar>devname</device>
+ * <device>devname<foo>bar</foo></device>
+ */
+static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
+{
+ char *start, *tmp, *end, *name;
+ int ret;
+
+ if ((ret = parse_start(cp)) <= 0)
+ return ret;
+
+ start = tmp = strchr(*cp, '>');
+ if (!start) {
+ DBG(DEBUG_READ,
+ printf("blkid: short line parsing dev: %s\n", *cp));
+ return -BLKID_ERR_CACHE;
+ }
+ start = skip_over_blank(start + 1);
+ end = skip_over_word(start);
+
+ DBG(DEBUG_READ, printf("device should be %.*s\n",
+ (int)(end - start), start));
+
+ if (**cp == '>')
+ *cp = end;
+ else
+ (*cp)++;
+
+ *tmp = '\0';
+
+ if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+ DBG(DEBUG_READ,
+ printf("blkid: missing </device> ending: %s\n", end));
+ } else if (tmp)
+ *tmp = '\0';
+
+ if (end - start <= 1) {
+ DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
+ return -BLKID_ERR_CACHE;
+ }
+
+ name = blkid_strndup(start, end-start);
+ if (name == NULL)
+ return -BLKID_ERR_MEM;
+
+ DBG(DEBUG_READ, printf("found dev %s\n", name));
+
+ if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) {
+ free(name);
+ return -BLKID_ERR_MEM;
+ }
+
+ free(name);
+ return 1;
+}
+
+/*
+ * Extract a tag of the form NAME="value" from the line.
+ */
+static int parse_token(char **name, char **value, char **cp)
+{
+ char *end;
+
+ if (!name || !value || !cp)
+ return -BLKID_ERR_PARAM;
+
+ if (!(*value = strchr(*cp, '=')))
+ return 0;
+
+ **value = '\0';
+ *name = strip_line(*cp);
+ *value = skip_over_blank(*value + 1);
+
+ if (**value == '"') {
+ end = strchr(*value + 1, '"');
+ if (!end) {
+ DBG(DEBUG_READ,
+ printf("unbalanced quotes at: %s\n", *value));
+ *cp = *value;
+ return -BLKID_ERR_CACHE;
+ }
+ (*value)++;
+ *end = '\0';
+ end++;
+ } else {
+ end = skip_over_word(*value);
+ if (*end) {
+ *end = '\0';
+ end++;
+ }
+ }
+ *cp = end;
+
+ return 1;
+}
+
+/*
+ * Extract a tag of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+ char *end;
+
+ if (!name || !value || !cp)
+ return -BLKID_ERR_PARAM;
+
+ *name = strip_line(*cp);
+
+ if ((*name)[0] != '<' || (*name)[1] == '/')
+ return 0;
+
+ FIXME: finish this.
+}
+*/
+
+/*
+ * Extract a tag from the line.
+ *
+ * Return 1 if a valid tag was found.
+ * Return 0 if no tag found.
+ * Return -ve error code.
+ */
+static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
+{
+ char *name;
+ char *value;
+ int ret;
+
+ if (!cache || !dev)
+ return -BLKID_ERR_PARAM;
+
+ if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
+ (ret = parse_xml(&name, &value, cp)) <= 0 */)
+ return ret;
+
+ /* Some tags are stored directly in the device struct */
+ if (!strcmp(name, "DEVNO"))
+ dev->bid_devno = STRTOULL(value, 0, 0);
+ else if (!strcmp(name, "PRI"))
+ dev->bid_pri = strtol(value, 0, 0);
+ else if (!strcmp(name, "TIME"))
+ dev->bid_time = STRTOULL(value, 0, 0);
+ else
+ ret = blkid_set_tag(dev, name, value, strlen(value));
+
+ DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value));
+
+ return ret < 0 ? ret : 1;
+}
+
+/*
+ * Parse a single line of data, and return a newly allocated dev struct.
+ * Add the new device to the cache struct, if one was read.
+ *
+ * Lines are of the form <device [TAG="value" ...]>/dev/foo</device>
+ *
+ * Returns -ve value on error.
+ * Returns 0 otherwise.
+ * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL
+ * (e.g. comment lines, unknown XML content, etc).
+ */
+static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
+{
+ blkid_dev dev;
+ int ret;
+
+ if (!cache || !dev_p)
+ return -BLKID_ERR_PARAM;
+
+ *dev_p = NULL;
+
+ DBG(DEBUG_READ, printf("line: %s\n", cp));
+
+ if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
+ return ret;
+
+ dev = *dev_p;
+
+ while ((ret = parse_tag(cache, dev, &cp)) > 0) {
+ ;
+ }
+
+ if (dev->bid_type == NULL) {
+ DBG(DEBUG_READ,
+ printf("blkid: device %s has no TYPE\n",dev->bid_name));
+ blkid_free_dev(dev);
+ }
+
+ DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
+
+ return ret;
+}
+
+/*
+ * Parse the specified filename, and return the data in the supplied or
+ * a newly allocated cache struct. If the file doesn't exist, return a
+ * new empty cache struct.
+ */
+void blkid_read_cache(blkid_cache cache)
+{
+ FILE *file;
+ char buf[4096];
+ int fd, lineno = 0;
+ struct stat st;
+
+ if (!cache)
+ return;
+
+ /*
+ * If the file doesn't exist, then we just return an empty
+ * struct so that the cache can be populated.
+ */
+ if ((fd = open(cache->bic_filename, O_RDONLY)) < 0)
+ return;
+ if (fstat(fd, &st) < 0)
+ goto errout;
+ if ((st.st_mtime == cache->bic_ftime) ||
+ (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+ DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
+ cache->bic_filename));
+ goto errout;
+ }
+
+ DBG(DEBUG_CACHE, printf("reading cache file %s\n",
+ cache->bic_filename));
+
+ file = fdopen(fd, "r");
+ if (!file)
+ goto errout;
+
+ while (fgets(buf, sizeof(buf), file)) {
+ blkid_dev dev;
+ unsigned int end;
+
+ lineno++;
+ if (buf[0] == 0)
+ continue;
+ end = strlen(buf) - 1;
+ /* Continue reading next line if it ends with a backslash */
+ while (buf[end] == '\\' && end < sizeof(buf) - 2 &&
+ fgets(buf + end, sizeof(buf) - end, file)) {
+ end = strlen(buf) - 1;
+ lineno++;
+ }
+
+ if (blkid_parse_line(cache, &dev, buf) < 0) {
+ DBG(DEBUG_READ,
+ printf("blkid: bad format on line %d\n", lineno));
+ continue;
+ }
+ }
+ fclose(file);
+
+ /*
+ * Initially we do not need to write out the cache file.
+ */
+ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+ cache->bic_ftime = st.st_mtime;
+
+ return;
+errout:
+ close(fd);
+ return;
+}
+
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
+{
+ struct list_head *p;
+
+ if (!dev) {
+ printf(" dev: NULL\n");
+ return;
+ }
+
+ printf(" dev: name = %s\n", dev->bid_name);
+ printf(" dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+ printf(" dev: TIME=\"%lld\"\n", (long long)dev->bid_time);
+ printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
+ printf(" dev: flags = 0x%08X\n", dev->bid_flags);
+
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+ if (tag)
+ printf(" tag: %s=\"%s\"\n", tag->bit_name,
+ tag->bit_val);
+ else
+ printf(" tag: NULL\n");
+ }
+ printf("\n");
+}
+
+int main(int argc, char**argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [filename]\n"
+ "Test parsing of the cache (filename)\n", argv[0]);
+ exit(1);
+ }
+ if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
+ fprintf(stderr, "error %d reading cache file %s\n", ret,
+ argv[1] ? argv[1] : BLKID_CACHE_FILE);
+
+ blkid_put_cache(cache);
+
+ return ret;
+}
+#endif
diff --git a/lib/blkid/resolve.c b/lib/blkid/resolve.c
new file mode 100644
index 0000000..3bc37b0
--- /dev/null
+++ b/lib/blkid/resolve.c
@@ -0,0 +1,140 @@
+/*
+ * resolve.c - resolve names and tags into specific devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Ts'o.
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "blkidP.h"
+
+/*
+ * Find a tagname (e.g. LABEL or UUID) on a specific device.
+ */
+char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+ const char *devname)
+{
+ blkid_tag found;
+ blkid_dev dev;
+ blkid_cache c = cache;
+ char *ret = NULL;
+
+ DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname));
+
+ if (!devname)
+ return NULL;
+
+ if (!cache) {
+ if (blkid_get_cache(&c, NULL) < 0)
+ return NULL;
+ }
+
+ if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) &&
+ (found = blkid_find_tag_dev(dev, tagname)))
+ ret = blkid_strdup(found->bit_val);
+
+ if (!cache)
+ blkid_put_cache(c);
+
+ return ret;
+}
+
+/*
+ * Locate a device name from a token (NAME=value string), or (name, value)
+ * pair. In the case of a token, value is ignored. If the "token" is not
+ * of the form "NAME=value" and there is no value given, then it is assumed
+ * to be the actual devname and a copy is returned.
+ */
+char *blkid_get_devname(blkid_cache cache, const char *token,
+ const char *value)
+{
+ blkid_dev dev;
+ blkid_cache c = cache;
+ char *t = 0, *v = 0;
+ char *ret = NULL;
+
+ if (!token)
+ return NULL;
+
+ if (!cache) {
+ if (blkid_get_cache(&c, NULL) < 0)
+ return NULL;
+ }
+
+ DBG(DEBUG_RESOLVE,
+ printf("looking for %s%s%s %s\n", token, value ? "=" : "",
+ value ? value : "", cache ? "in cache" : "from disk"));
+
+ if (!value) {
+ if (!strchr(token, '=')) {
+ ret = blkid_strdup(token);
+ goto out;
+ }
+ blkid_parse_tag_string(token, &t, &v);
+ if (!t || !v)
+ goto out;
+ token = t;
+ value = v;
+ }
+
+ dev = blkid_find_dev_with_tag(c, token, value);
+ if (!dev)
+ goto out;
+
+ ret = blkid_strdup(blkid_dev_devname(dev));
+
+out:
+ free(t);
+ free(v);
+ if (!cache) {
+ blkid_put_cache(c);
+ }
+ return (ret);
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ char *value;
+ blkid_cache cache;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Usage:\t%s tagname=value\n"
+ "\t%s tagname devname\n"
+ "Find which device holds a given token or\n"
+ "Find what the value of a tag is in a device\n",
+ argv[0], argv[0]);
+ exit(1);
+ }
+ if (blkid_get_cache(&cache, "/dev/null") < 0) {
+ fprintf(stderr, "Couldn't get blkid cache\n");
+ exit(1);
+ }
+
+ if (argv[2]) {
+ value = blkid_get_tag_value(cache, argv[1], argv[2]);
+ printf("%s has tag %s=%s\n", argv[2], argv[1],
+ value ? value : "<missing>");
+ } else {
+ value = blkid_get_devname(cache, argv[1], NULL);
+ printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
+ }
+ blkid_put_cache(cache);
+ return value ? 0 : 1;
+}
+#endif
diff --git a/lib/blkid/save.c b/lib/blkid/save.c
new file mode 100644
index 0000000..6f4499c
--- /dev/null
+++ b/lib/blkid/save.c
@@ -0,0 +1,213 @@
+/*
+ * save.c - write the cache struct to disk
+ *
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+#ifdef _WIN32
+#include "windows.h"
+#endif
+
+static int save_dev(blkid_dev dev, FILE *file)
+{
+ struct list_head *p;
+
+ if (!dev || dev->bid_name[0] != '/')
+ return 0;
+
+ DBG(DEBUG_SAVE,
+ printf("device %s, type %s\n", dev->bid_name, dev->bid_type ?
+ dev->bid_type : "(null)"));
+
+ fprintf(file,
+ "<device DEVNO=\"0x%04lx\" TIME=\"%ld\"",
+ (unsigned long) dev->bid_devno, (long) dev->bid_time);
+ if (dev->bid_pri)
+ fprintf(file, " PRI=\"%d\"", dev->bid_pri);
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+ fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
+ }
+ fprintf(file, ">%s</device>\n", dev->bid_name);
+
+ return 0;
+}
+
+/*
+ * Write out the cache struct to the cache file on disk.
+ */
+int blkid_flush_cache(blkid_cache cache)
+{
+ struct list_head *p;
+ char *tmp = NULL;
+ const char *opened = NULL;
+ const char *filename;
+ FILE *file = NULL;
+ int fd, ret = 0;
+ struct stat st;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ if (list_empty(&cache->bic_devs) ||
+ !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+ DBG(DEBUG_SAVE, printf("skipping cache file write\n"));
+ return 0;
+ }
+
+ filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE;
+
+ /* If we can't write to the cache file, then don't even try */
+ if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
+ (ret == 0 && access(filename, W_OK) < 0)) {
+ DBG(DEBUG_SAVE,
+ printf("can't write to cache file %s\n", filename));
+ return 0;
+ }
+
+ /*
+ * Try and create a temporary file in the same directory so
+ * that in case of error we don't overwrite the cache file.
+ * If the cache file doesn't yet exist, it isn't a regular
+ * file (e.g. /dev/null or a socket), or we couldn't create
+ * a temporary file then we open it directly.
+ */
+ if (ret == 0 && S_ISREG(st.st_mode)) {
+ tmp = malloc(strlen(filename) + 8);
+ if (tmp) {
+ mode_t save_umask = umask(022);
+ sprintf(tmp, "%s-XXXXXX", filename);
+ fd = mkstemp(tmp);
+ umask(save_umask);
+ if (fd >= 0) {
+ file = fdopen(fd, "w");
+ opened = tmp;
+ }
+#ifndef _WIN32
+ fchmod(fd, 0644);
+#else
+ chmod(tmp, 0644);
+#endif
+ }
+ }
+
+ if (!file) {
+ file = fopen(filename, "w");
+ opened = filename;
+ }
+
+ DBG(DEBUG_SAVE,
+ printf("writing cache file %s (really %s)\n",
+ filename, opened));
+
+ if (!file) {
+ ret = errno;
+ goto errout;
+ }
+
+ list_for_each(p, &cache->bic_devs) {
+ blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (!dev->bid_type)
+ continue;
+ if ((ret = save_dev(dev, file)) < 0)
+ break;
+ }
+
+ if (ret >= 0) {
+ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+ ret = 1;
+ }
+
+ fclose(file);
+ if (opened != filename) {
+ if (ret < 0) {
+ (void) unlink(opened);
+ DBG(DEBUG_SAVE,
+ printf("unlinked temp cache %s\n", opened));
+ } else {
+ char *backup;
+
+ backup = malloc(strlen(filename) + 5);
+ if (backup) {
+ sprintf(backup, "%s.old", filename);
+ unlink(backup);
+#if defined(__GNUC__) && __GNUC__ >= 5
+/* explicit (void) cast is not enough with glibc and _FORTIFY_SOURCE */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-result"
+#endif
+ (void) link(filename, backup);
+#if defined(__GNUC__) && __GNUC__ >= 5
+#pragma GCC diagnostic pop
+#endif
+ free(backup);
+ }
+ if (rename(opened, filename) < 0)
+ (void) unlink(opened);
+ DBG(DEBUG_SAVE,
+ printf("moved temp cache %s\n", opened));
+ }
+ }
+
+errout:
+ free(tmp);
+ return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_debug_mask = DEBUG_ALL;
+ if (argc > 2) {
+ fprintf(stderr, "Usage: %s [filename]\n"
+ "Test loading/saving a cache (filename)\n", argv[0]);
+ exit(1);
+ }
+
+ if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+ if ((ret = blkid_probe_all(cache)) < 0) {
+ fprintf(stderr, "error (%d) probing devices\n", ret);
+ exit(1);
+ }
+ cache->bic_filename = blkid_strdup(argv[1]);
+
+ if ((ret = blkid_flush_cache(cache)) < 0) {
+ fprintf(stderr, "error (%d) saving cache\n", ret);
+ exit(1);
+ }
+
+ blkid_put_cache(cache);
+
+ return ret;
+}
+#endif
diff --git a/lib/blkid/tag.c b/lib/blkid/tag.c
new file mode 100644
index 0000000..e88ebc4
--- /dev/null
+++ b/lib/blkid/tag.c
@@ -0,0 +1,471 @@
+/*
+ * tag.c - allocation/initialization/free routines for tag structs
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blkidP.h"
+
+static blkid_tag blkid_new_tag(void)
+{
+ blkid_tag tag;
+
+ if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag))))
+ return NULL;
+
+ INIT_LIST_HEAD(&tag->bit_tags);
+ INIT_LIST_HEAD(&tag->bit_names);
+
+ return tag;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+ if (!tag) {
+ printf(" tag: NULL\n");
+ return;
+ }
+
+ printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+#endif
+
+void blkid_free_tag(blkid_tag tag)
+{
+ if (!tag)
+ return;
+
+ DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name,
+ tag->bit_val ? tag->bit_val : "(NULL)"));
+ DBG(DEBUG_TAG, blkid_debug_dump_tag(tag));
+
+ list_del(&tag->bit_tags); /* list of tags for this device */
+ list_del(&tag->bit_names); /* list of tags with this type */
+
+ free(tag->bit_name);
+ free(tag->bit_val);
+
+ free(tag);
+}
+
+/*
+ * Find the desired tag on a device. If value is NULL, then the
+ * first such tag is returned, otherwise return only exact tag if found.
+ */
+blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
+{
+ struct list_head *p;
+
+ if (!dev || !type)
+ return NULL;
+
+ list_for_each(p, &dev->bid_tags) {
+ blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+ bit_tags);
+
+ if (!strcmp(tmp->bit_name, type))
+ return tmp;
+ }
+ return NULL;
+}
+
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+ const char *value)
+{
+ blkid_tag tag;
+
+ if (!dev || !type)
+ return -1;
+
+ tag = blkid_find_tag_dev(dev, type);
+ if (!value)
+ return (tag != NULL);
+ if (!tag || strcmp(tag->bit_val, value))
+ return 0;
+ return 1;
+}
+
+/*
+ * Find the desired tag type in the cache.
+ * We return the head tag for this tag type.
+ */
+static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type)
+{
+ blkid_tag head = NULL, tmp;
+ struct list_head *p;
+
+ if (!cache || !type)
+ return NULL;
+
+ list_for_each(p, &cache->bic_tags) {
+ tmp = list_entry(p, struct blkid_struct_tag, bit_tags);
+ if (!strcmp(tmp->bit_name, type)) {
+ DBG(DEBUG_TAG,
+ printf(" found cache tag head %s\n", type));
+ head = tmp;
+ break;
+ }
+ }
+ return head;
+}
+
+/*
+ * Set a tag on an existing device.
+ *
+ * If value is NULL, then delete the tagsfrom the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength)
+{
+ blkid_tag t = 0, head = 0;
+ char *val = 0;
+ char **dev_var = 0;
+
+ if (!dev || !name)
+ return -BLKID_ERR_PARAM;
+
+ if (!(val = blkid_strndup(value, vlength)) && value)
+ return -BLKID_ERR_MEM;
+
+ /*
+ * Certain common tags are linked directly to the device struct
+ * We need to know what they are before we do anything else because
+ * the function name parameter might get freed later on.
+ */
+ if (!strcmp(name, "TYPE"))
+ dev_var = &dev->bid_type;
+ else if (!strcmp(name, "LABEL"))
+ dev_var = &dev->bid_label;
+ else if (!strcmp(name, "UUID"))
+ dev_var = &dev->bid_uuid;
+
+ t = blkid_find_tag_dev(dev, name);
+ if (!value) {
+ if (t)
+ blkid_free_tag(t);
+ } else if (t) {
+ if (!strcmp(t->bit_val, val)) {
+ /* Same thing, exit */
+ free(val);
+ return 0;
+ }
+ free(t->bit_val);
+ t->bit_val = val;
+ } else {
+ /* Existing tag not present, add to device */
+ if (!(t = blkid_new_tag()))
+ goto errout;
+ t->bit_name = blkid_strdup(name);
+ t->bit_val = val;
+ t->bit_dev = dev;
+
+ list_add_tail(&t->bit_tags, &dev->bid_tags);
+
+ if (dev->bid_cache) {
+ head = blkid_find_head_cache(dev->bid_cache,
+ t->bit_name);
+ if (!head) {
+ head = blkid_new_tag();
+ if (!head)
+ goto errout;
+
+ DBG(DEBUG_TAG,
+ printf(" creating new cache tag head %s\n", name));
+ head->bit_name = blkid_strdup(name);
+ if (!head->bit_name)
+ goto errout;
+ list_add_tail(&head->bit_tags,
+ &dev->bid_cache->bic_tags);
+ }
+ list_add_tail(&t->bit_names, &head->bit_names);
+ }
+ }
+
+ /* Link common tags directly to the device struct */
+ if (dev_var)
+ *dev_var = val;
+
+ if (dev->bid_cache)
+ dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ return 0;
+
+errout:
+ if (t)
+ blkid_free_tag(t);
+ else free(val);
+ if (head)
+ blkid_free_tag(head);
+ return -BLKID_ERR_MEM;
+}
+
+
+/*
+ * Parse a "NAME=value" string. This is slightly different than
+ * parse_token, because that will end an unquoted value at a space, while
+ * this will assume that an unquoted value is the rest of the token (e.g.
+ * if we are passed an already quoted string from the command-line we don't
+ * have to both quote and escape quote so that the quotes make it to
+ * us).
+ *
+ * Returns 0 on success, and -1 on failure.
+ */
+int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
+{
+ char *name, *value, *cp;
+
+ DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token));
+
+ if (!token || !(cp = strchr(token, '=')))
+ return -1;
+
+ name = blkid_strdup(token);
+ if (!name)
+ return -1;
+ value = name + (cp - token);
+ *value++ = '\0';
+ if (*value == '"' || *value == '\'') {
+ char c = *value++;
+ if (!(cp = strrchr(value, c)))
+ goto errout; /* missing closing quote */
+ *cp = '\0';
+ }
+ value = blkid_strdup(value);
+ if (!value)
+ goto errout;
+
+ *ret_type = name;
+ *ret_val = value;
+
+ return 0;
+
+errout:
+ free(name);
+ return -1;
+}
+
+/*
+ * Tag iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implementation. I'm not convinced I want
+ * to keep list.h in the long term, anyway. It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application. [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all tags in a device
+ */
+#define TAG_ITERATE_MAGIC 0x01a5284c
+
+struct blkid_struct_tag_iterate {
+ int magic;
+ blkid_dev dev;
+ struct list_head *p;
+};
+
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+ blkid_tag_iterate iter;
+
+ iter = malloc(sizeof(struct blkid_struct_tag_iterate));
+ if (iter) {
+ iter->magic = TAG_ITERATE_MAGIC;
+ iter->dev = dev;
+ iter->p = dev->bid_tags.next;
+ }
+ return (iter);
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_tag_next(blkid_tag_iterate iter,
+ const char **type, const char **value)
+{
+ blkid_tag tag;
+
+ *type = 0;
+ *value = 0;
+ if (!iter || iter->magic != TAG_ITERATE_MAGIC ||
+ iter->p == &iter->dev->bid_tags)
+ return -1;
+ tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags);
+ *type = tag->bit_name;
+ *value = tag->bit_val;
+ iter->p = iter->p->next;
+ return 0;
+}
+
+extern void blkid_tag_iterate_end(blkid_tag_iterate iter)
+{
+ if (!iter || iter->magic != TAG_ITERATE_MAGIC)
+ return;
+ iter->magic = 0;
+ free(iter);
+}
+
+/*
+ * This function returns a device which matches a particular
+ * type/value pair. If there is more than one device that matches the
+ * search specification, it returns the one with the highest priority
+ * value. This allows us to give preference to EVMS or LVM devices.
+ */
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+ const char *type,
+ const char *value)
+{
+ blkid_tag head;
+ blkid_dev dev;
+ int pri;
+ struct list_head *p;
+ int probe_new = 0;
+
+ if (!cache || !type || !value)
+ return NULL;
+
+ blkid_read_cache(cache);
+
+ DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value));
+
+try_again:
+ pri = -1;
+ dev = 0;
+ head = blkid_find_head_cache(cache, type);
+
+ if (head) {
+ list_for_each(p, &head->bit_names) {
+ blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+ bit_names);
+
+ if (!strcmp(tmp->bit_val, value) &&
+ (tmp->bit_dev->bid_pri > pri) &&
+ !access(tmp->bit_dev->bid_name, F_OK)) {
+ dev = tmp->bit_dev;
+ pri = dev->bid_pri;
+ }
+ }
+ }
+ if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+ dev = blkid_verify(cache, dev);
+ if (!dev || (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)))
+ goto try_again;
+ }
+
+ if (!dev && !probe_new) {
+ if (blkid_probe_all_new(cache) < 0)
+ return NULL;
+ probe_new++;
+ goto try_again;
+ }
+
+ if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+ if (blkid_probe_all(cache) < 0)
+ return NULL;
+ goto try_again;
+ }
+ return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device "
+ "[type value]\n",
+ prog);
+ fprintf(stderr, "\tList all tags for a device and exit\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ blkid_tag_iterate iter;
+ blkid_cache cache = NULL;
+ blkid_dev dev;
+ int c, ret, found;
+ int flags = BLKID_DEV_FIND;
+ char *tmp;
+ char *file = NULL;
+ char *devname = NULL;
+ char *search_type = NULL;
+ char *search_value = NULL;
+ const char *type, *value;
+
+ while ((c = getopt (argc, argv, "m:f:")) != EOF)
+ switch (c) {
+ case 'f':
+ file = optarg;
+ break;
+ case 'm':
+ blkid_debug_mask = strtoul (optarg, &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, "Invalid debug mask: %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ if (argc > optind)
+ devname = argv[optind++];
+ if (argc > optind)
+ search_type = argv[optind++];
+ if (argc > optind)
+ search_value = argv[optind++];
+ if (!devname || (argc != optind))
+ usage(argv[0]);
+
+ if ((ret = blkid_get_cache(&cache, file)) != 0) {
+ fprintf(stderr, "%s: error creating cache (%d)\n",
+ argv[0], ret);
+ exit(1);
+ }
+
+ dev = blkid_get_dev(cache, devname, flags);
+ if (!dev) {
+ fprintf(stderr, "%s: Can not find device in blkid cache\n",
+ devname);
+ exit(1);
+ }
+ if (search_type) {
+ found = blkid_dev_has_tag(dev, search_type, search_value);
+ printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev),
+ search_type, search_value ? search_value : "NULL",
+ found ? "FOUND" : "NOT FOUND");
+ return(!found);
+ }
+ printf("Device %s...\n", blkid_dev_devname(dev));
+
+ iter = blkid_tag_iterate_begin(dev);
+ while (blkid_tag_next(iter, &type, &value) == 0) {
+ printf("\tTag %s has value %s\n", type, value);
+ }
+ blkid_tag_iterate_end(iter);
+
+ blkid_put_cache(cache);
+ return (0);
+}
+#endif
diff --git a/lib/blkid/test_probe.in b/lib/blkid/test_probe.in
new file mode 100644
index 0000000..a7b29a2
--- /dev/null
+++ b/lib/blkid/test_probe.in
@@ -0,0 +1,62 @@
+
+TESTS=$*
+
+if test "$TESTS"x = x ; then
+ for i in $SRCDIR/tests/*.img.bz2
+ do
+ TESTS="$TESTS `basename $i .img.bz2`"
+ done
+fi
+
+mkdir -p tests/tmp
+
+for i in $TESTS
+do
+ printf "%s: " $i
+ RESULTS=$SRCDIR/tests/$i.results
+ IMAGE_BZ2=$SRCDIR/tests/$i.img.bz2
+ IMAGE=tests/tmp/$i.img.$$
+ if test ! -f $IMAGE_BZ2 -a ! -f $RESULTS ;
+ then
+ echo "non-existent"
+ continue
+ fi
+ if [ "$i" = "swap0" ] && which mkswap > /dev/null; then
+ # swap is native-endian, so regenerate before testing
+ dd if=/dev/zero of=$IMAGE bs=16k count=64 2> /dev/null
+ mkswap -v0 $IMAGE > /dev/null
+ elif [ "$i" = "swap1" ] && which mkswap > /dev/null; then
+ # swap is native-endian, so regenerate before testing
+ dd if=/dev/zero of=$IMAGE bs=16k count=64 2> /dev/null
+ # check if mkswap supports the "-U" option
+ if mkswap -h 2>&1 | grep -q -- '-U'; then
+ UUID="-U 8ff8e77f-8553-485e-8656-58be67a81666"
+ else
+ RMUUID="| grep -v UUID"
+ RES_TMP=$SRCDIR/tests/tmp/$i.results
+ grep -v UUID $RESULTS > $RES_TMP
+ RESULTS=$RES_TMP
+ fi
+ mkswap -v1 -L SWAP-TEST $UUID $IMAGE >/dev/null
+ else
+ bunzip2 < $IMAGE_BZ2 > $IMAGE
+ fi
+ eval ./tst_probe $IMAGE $RMUUID > tests/$i.out
+ rm -f $IMAGE tests/$i.ok tests/$i.failed
+ cmp -s tests/$i.out $RESULTS
+ unset RMUUID
+ if [ $? = 0 ]; then
+ echo ok
+ touch tests/$i.ok
+ else
+ echo failed
+ diff -c tests/$i.out $RESULTS > tests/$i.failed
+ fi
+done
+
+num_ok=`ls tests/*.ok 2>/dev/null | wc -l`
+num_failed=`ls tests/*.failed 2>/dev/null | wc -l`
+
+echo "$num_ok tests succeeded $num_failed tests failed"
+
+test "$num_failed" -eq 0 || exit 1
diff --git a/lib/blkid/tests/cramfs.img.bz2 b/lib/blkid/tests/cramfs.img.bz2
new file mode 100644
index 0000000..d638116
--- /dev/null
+++ b/lib/blkid/tests/cramfs.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/cramfs.results b/lib/blkid/tests/cramfs.results
new file mode 100644
index 0000000..984d9e2
--- /dev/null
+++ b/lib/blkid/tests/cramfs.results
@@ -0,0 +1,2 @@
+TYPE='cramfs'
+LABEL='test-cram'
diff --git a/lib/blkid/tests/ext2.img.bz2 b/lib/blkid/tests/ext2.img.bz2
new file mode 100644
index 0000000..d1811ce
--- /dev/null
+++ b/lib/blkid/tests/ext2.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/ext2.results b/lib/blkid/tests/ext2.results
new file mode 100644
index 0000000..4fa7a2a
--- /dev/null
+++ b/lib/blkid/tests/ext2.results
@@ -0,0 +1,3 @@
+TYPE='ext2'
+LABEL='test-ext2'
+UUID='22f0eac3-5c89-4ec1-9076-60799119aaea'
diff --git a/lib/blkid/tests/ext3.img.bz2 b/lib/blkid/tests/ext3.img.bz2
new file mode 100644
index 0000000..5394598
--- /dev/null
+++ b/lib/blkid/tests/ext3.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/ext3.results b/lib/blkid/tests/ext3.results
new file mode 100644
index 0000000..4dc2252
--- /dev/null
+++ b/lib/blkid/tests/ext3.results
@@ -0,0 +1,3 @@
+TYPE='ext3'
+LABEL='test-ext3'
+UUID='35f66dab-477e-4090-a872-95ee0e493ad6'
diff --git a/lib/blkid/tests/fat.img.bz2 b/lib/blkid/tests/fat.img.bz2
new file mode 100644
index 0000000..1fa7f2d
--- /dev/null
+++ b/lib/blkid/tests/fat.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/fat.results b/lib/blkid/tests/fat.results
new file mode 100644
index 0000000..7441872
--- /dev/null
+++ b/lib/blkid/tests/fat.results
@@ -0,0 +1,3 @@
+TYPE='vfat'
+LABEL='TEST-FAT'
+UUID='DEAD-BEEF'
diff --git a/lib/blkid/tests/fat32_label_64MB.img.bz2 b/lib/blkid/tests/fat32_label_64MB.img.bz2
new file mode 100644
index 0000000..ca76293
--- /dev/null
+++ b/lib/blkid/tests/fat32_label_64MB.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/fat32_label_64MB.results b/lib/blkid/tests/fat32_label_64MB.results
new file mode 100644
index 0000000..f2f56c2
--- /dev/null
+++ b/lib/blkid/tests/fat32_label_64MB.results
@@ -0,0 +1,3 @@
+TYPE='vfat'
+LABEL='BINGO'
+UUID='8CB5-BA49'
diff --git a/lib/blkid/tests/iso.img.bz2 b/lib/blkid/tests/iso.img.bz2
new file mode 100644
index 0000000..4efb6ad
--- /dev/null
+++ b/lib/blkid/tests/iso.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/iso.results b/lib/blkid/tests/iso.results
new file mode 100644
index 0000000..6a5d1dc
--- /dev/null
+++ b/lib/blkid/tests/iso.results
@@ -0,0 +1,2 @@
+TYPE='iso9660'
+LABEL='test-iso'
diff --git a/lib/blkid/tests/jbd.img.bz2 b/lib/blkid/tests/jbd.img.bz2
new file mode 100644
index 0000000..f0d7f91
--- /dev/null
+++ b/lib/blkid/tests/jbd.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/jbd.results b/lib/blkid/tests/jbd.results
new file mode 100644
index 0000000..2a43089
--- /dev/null
+++ b/lib/blkid/tests/jbd.results
@@ -0,0 +1,2 @@
+TYPE='jbd'
+UUID='0d7a07df-7b06-4829-bce7-3b9c3ece570c'
diff --git a/lib/blkid/tests/jfs.img.bz2 b/lib/blkid/tests/jfs.img.bz2
new file mode 100644
index 0000000..1d4d249
--- /dev/null
+++ b/lib/blkid/tests/jfs.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/jfs.results b/lib/blkid/tests/jfs.results
new file mode 100644
index 0000000..5d752a3
--- /dev/null
+++ b/lib/blkid/tests/jfs.results
@@ -0,0 +1,3 @@
+TYPE='jfs'
+LABEL='test-jfs'
+UUID='9bf7b82e-7583-4c74-99a4-189a691f27b5'
diff --git a/lib/blkid/tests/minix.img.bz2 b/lib/blkid/tests/minix.img.bz2
new file mode 100644
index 0000000..d11f3a8
--- /dev/null
+++ b/lib/blkid/tests/minix.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/minix.results b/lib/blkid/tests/minix.results
new file mode 100644
index 0000000..d0b448f
--- /dev/null
+++ b/lib/blkid/tests/minix.results
@@ -0,0 +1 @@
+TYPE='minix'
diff --git a/lib/blkid/tests/ocfs2.img.bz2 b/lib/blkid/tests/ocfs2.img.bz2
new file mode 100644
index 0000000..0bad915
--- /dev/null
+++ b/lib/blkid/tests/ocfs2.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/ocfs2.results b/lib/blkid/tests/ocfs2.results
new file mode 100644
index 0000000..918afa3
--- /dev/null
+++ b/lib/blkid/tests/ocfs2.results
@@ -0,0 +1,3 @@
+TYPE='ocfs2'
+LABEL='test-ocfs2'
+UUID='6b6bfbea-3a79-4f0c-b166-a20776102445'
diff --git a/lib/blkid/tests/reiser3.img.bz2 b/lib/blkid/tests/reiser3.img.bz2
new file mode 100644
index 0000000..1802bc2
--- /dev/null
+++ b/lib/blkid/tests/reiser3.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/reiser3.results b/lib/blkid/tests/reiser3.results
new file mode 100644
index 0000000..8c3bb7c
--- /dev/null
+++ b/lib/blkid/tests/reiser3.results
@@ -0,0 +1,3 @@
+TYPE='reiserfs'
+LABEL='TESTREISER'
+UUID='9efe7863-b124-46dc-ad68-8ecd04230a7b'
diff --git a/lib/blkid/tests/reiser4.img.bz2 b/lib/blkid/tests/reiser4.img.bz2
new file mode 100644
index 0000000..b50d12e
--- /dev/null
+++ b/lib/blkid/tests/reiser4.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/reiser4.results b/lib/blkid/tests/reiser4.results
new file mode 100644
index 0000000..b312289
--- /dev/null
+++ b/lib/blkid/tests/reiser4.results
@@ -0,0 +1,3 @@
+TYPE='reiser4'
+LABEL='TESTR4'
+UUID='9722633c-d69a-4881-b1c8-bedecbbf39d2'
diff --git a/lib/blkid/tests/romfs.img.bz2 b/lib/blkid/tests/romfs.img.bz2
new file mode 100644
index 0000000..b5e3109
--- /dev/null
+++ b/lib/blkid/tests/romfs.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/romfs.results b/lib/blkid/tests/romfs.results
new file mode 100644
index 0000000..03e8622
--- /dev/null
+++ b/lib/blkid/tests/romfs.results
@@ -0,0 +1,2 @@
+TYPE='romfs'
+LABEL='test-romfs'
diff --git a/lib/blkid/tests/small-fat32.img.bz2 b/lib/blkid/tests/small-fat32.img.bz2
new file mode 100644
index 0000000..8b90f9c
--- /dev/null
+++ b/lib/blkid/tests/small-fat32.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/small-fat32.results b/lib/blkid/tests/small-fat32.results
new file mode 100644
index 0000000..bd5ebf4
--- /dev/null
+++ b/lib/blkid/tests/small-fat32.results
@@ -0,0 +1,3 @@
+TYPE='vfat'
+LABEL='TESTVFAT'
+UUID='1423-AAE1'
diff --git a/lib/blkid/tests/swap0.img.bz2 b/lib/blkid/tests/swap0.img.bz2
new file mode 100644
index 0000000..e61e375
--- /dev/null
+++ b/lib/blkid/tests/swap0.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/swap0.results b/lib/blkid/tests/swap0.results
new file mode 100644
index 0000000..8742a60
--- /dev/null
+++ b/lib/blkid/tests/swap0.results
@@ -0,0 +1 @@
+TYPE='swap'
diff --git a/lib/blkid/tests/swap1.img.bz2 b/lib/blkid/tests/swap1.img.bz2
new file mode 100644
index 0000000..fbab9ed
--- /dev/null
+++ b/lib/blkid/tests/swap1.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/swap1.results b/lib/blkid/tests/swap1.results
new file mode 100644
index 0000000..ea04e5d
--- /dev/null
+++ b/lib/blkid/tests/swap1.results
@@ -0,0 +1,3 @@
+TYPE='swap'
+LABEL='SWAP-TEST'
+UUID='8ff8e77f-8553-485e-8656-58be67a81666'
diff --git a/lib/blkid/tests/udf.img.bz2 b/lib/blkid/tests/udf.img.bz2
new file mode 100644
index 0000000..bd2deb0
--- /dev/null
+++ b/lib/blkid/tests/udf.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/udf.results b/lib/blkid/tests/udf.results
new file mode 100644
index 0000000..1f7aa48
--- /dev/null
+++ b/lib/blkid/tests/udf.results
@@ -0,0 +1,2 @@
+TYPE='udf'
+LABEL='test-udf'
diff --git a/lib/blkid/tests/xfs.img.bz2 b/lib/blkid/tests/xfs.img.bz2
new file mode 100644
index 0000000..cf6982b
--- /dev/null
+++ b/lib/blkid/tests/xfs.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/xfs.results b/lib/blkid/tests/xfs.results
new file mode 100644
index 0000000..18e8d88
--- /dev/null
+++ b/lib/blkid/tests/xfs.results
@@ -0,0 +1,3 @@
+TYPE='xfs'
+LABEL='test-xfs'
+UUID='8c8a0a5a-9f57-492e-9610-45a61f38f58a'
diff --git a/lib/blkid/tests/zfs.img.bz2 b/lib/blkid/tests/zfs.img.bz2
new file mode 100644
index 0000000..92f445a
--- /dev/null
+++ b/lib/blkid/tests/zfs.img.bz2
Binary files differ
diff --git a/lib/blkid/tests/zfs.results b/lib/blkid/tests/zfs.results
new file mode 100644
index 0000000..c0f8fb9
--- /dev/null
+++ b/lib/blkid/tests/zfs.results
@@ -0,0 +1 @@
+TYPE='zfs'
diff --git a/lib/blkid/tst_types.c b/lib/blkid/tst_types.c
new file mode 100644
index 0000000..cb612ae
--- /dev/null
+++ b/lib/blkid/tst_types.c
@@ -0,0 +1,64 @@
+/*
+ * This testing program makes sure the blkid_types header file
+ *
+ * Copyright (C) 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include "blkid/blkid_types.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ if (sizeof(__u8) != 1) {
+ printf("Sizeof(__u8) is %d should be 1\n",
+ (int)sizeof(__u8));
+ exit(1);
+ }
+ if (sizeof(__s8) != 1) {
+ printf("Sizeof(_s8) is %d should be 1\n",
+ (int)sizeof(__s8));
+ exit(1);
+ }
+ if (sizeof(__u16) != 2) {
+ printf("Sizeof(__u16) is %d should be 2\n",
+ (int)sizeof(__u16));
+ exit(1);
+ }
+ if (sizeof(__s16) != 2) {
+ printf("Sizeof(__s16) is %d should be 2\n",
+ (int)sizeof(__s16));
+ exit(1);
+ }
+ if (sizeof(__u32) != 4) {
+ printf("Sizeof(__u32) is %d should be 4\n",
+ (int)sizeof(__u32));
+ exit(1);
+ }
+ if (sizeof(__s32) != 4) {
+ printf("Sizeof(__s32) is %d should be 4\n",
+ (int)sizeof(__s32));
+ exit(1);
+ }
+ if (sizeof(__u64) != 8) {
+ printf("Sizeof(__u64) is %d should be 8\n",
+ (int)sizeof(__u64));
+ exit(1);
+ }
+ if (sizeof(__s64) != 8) {
+ printf("Sizeof(__s64) is %d should be 8\n",
+ (int)sizeof(__s64));
+ exit(1);
+ }
+ printf("The blkid_types.h types are correct.\n");
+ exit(0);
+}
+
diff --git a/lib/blkid/version.c b/lib/blkid/version.c
new file mode 100644
index 0000000..72124f4
--- /dev/null
+++ b/lib/blkid/version.c
@@ -0,0 +1,50 @@
+/*
+ * version.c --- Return the version of the blkid library
+ *
+ * Copyright (C) 2004 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <blkid/blkid.h>
+#include "../../version.h"
+
+static const char *lib_version = E2FSPROGS_VERSION;
+static const char *lib_date = E2FSPROGS_DATE;
+
+int blkid_parse_version_string(const char *ver_string)
+{
+ const char *cp;
+ int version = 0;
+
+ for (cp = ver_string; *cp; cp++) {
+ if (*cp == '.')
+ continue;
+ if (!isdigit(*cp))
+ break;
+ version = (version * 10) + (*cp - '0');
+ }
+ return version;
+}
+
+int blkid_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 blkid_parse_version_string(lib_version);
+}
diff --git a/lib/config.h.in b/lib/config.h.in
new file mode 100644
index 0000000..ab38266
--- /dev/null
+++ b/lib/config.h.in
@@ -0,0 +1,665 @@
+/* lib/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if debugging the blkid library */
+#undef CONFIG_BLKID_DEBUG
+
+/* Define to 1 to compile findfs */
+#undef CONFIG_BUILD_FINDFS
+
+/* Define to 1 for features for use by ext4 developers */
+#undef CONFIG_DEVELOPER_FEATURES
+
+/* Define to 1 if debugging ext3/4 journal code */
+#undef CONFIG_JBD_DEBUG
+
+/* Define to 1 to enable mmp support */
+#undef CONFIG_MMP
+
+/* Define to 1 to enable tdb support */
+#undef CONFIG_TDB
+
+/* Define to 1 if the testio I/O manager should be enabled */
+#undef CONFIG_TESTIO_DEBUG
+
+/* Define to 1 to disable use of backtrace */
+#undef DISABLE_BACKTRACE
+
+/* Define to 1 to enable bitmap stats. */
+#undef ENABLE_BMAP_STATS
+
+/* Define to 1 to enable bitmap stats. */
+#undef ENABLE_BMAP_STATS_OPS
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#undef ENABLE_NLS
+
+/* Define to 1 if you have the `add_key' function. */
+#undef HAVE_ADD_KEY
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+#undef HAVE_ATTR_XATTR_H
+
+/* Define to 1 if you have the `backtrace' function. */
+#undef HAVE_BACKTRACE
+
+/* Define to 1 if blkid has blkid_probe_enable_partitions */
+#undef HAVE_BLKID_PROBE_ENABLE_PARTITIONS
+
+/* Define to 1 if blkid has blkid_probe_get_topology */
+#undef HAVE_BLKID_PROBE_GET_TOPOLOGY
+
+/* Define to 1 if blkid has blkid_topology_get_dax */
+#undef HAVE_BLKID_TOPOLOGY_GET_DAX
+
+/* Define to 1 if you have the BSD-style 'qsort_r' function. */
+#undef HAVE_BSD_QSORT_R
+
+/* Define to 1 if you have the Mac OS X function
+ CFLocaleCopyPreferredLanguages in the CoreFoundation framework. */
+#undef HAVE_CFLOCALECOPYPREFERREDLANGUAGES
+
+/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+#undef HAVE_CFPREFERENCESCOPYAPPVALUE
+
+/* Define to 1 if you have the `chflags' function. */
+#undef HAVE_CHFLAGS
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+#undef HAVE_DCGETTEXT
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the `dlopen' function. */
+#undef HAVE_DLOPEN
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <execinfo.h> header file. */
+#undef HAVE_EXECINFO_H
+
+/* Define to 1 if Ext2 ioctls present */
+#undef HAVE_EXT2_IOCTLS
+
+/* Define to 1 if you have the `fadvise64' function. */
+#undef HAVE_FADVISE64
+
+/* Define to 1 if you have the `fallocate' function. */
+#undef HAVE_FALLOCATE
+
+/* Define to 1 if you have the `fallocate64' function. */
+#undef HAVE_FALLOCATE64
+
+/* Define to 1 if you have the `fchown' function. */
+#undef HAVE_FCHOWN
+
+/* Define to 1 if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
+/* Define to 1 if you have the `fdatasync' function. */
+#undef HAVE_FDATASYNC
+
+/* Define to 1 if you have the `fstat64' function. */
+#undef HAVE_FSTAT64
+
+/* Define to 1 if you have the `fsync' function. */
+#undef HAVE_FSYNC
+
+/* Define to 1 if you have the `ftruncate64' function. */
+#undef HAVE_FTRUNCATE64
+
+/* Define to 1 if you have the <fuse.h> header file. */
+#undef HAVE_FUSE_H
+
+/* Define to 1 if you have the `futimes' function. */
+#undef HAVE_FUTIMES
+
+/* Define to 1 if you have the `getcwd' function. */
+#undef HAVE_GETCWD
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#undef HAVE_GETDTABLESIZE
+
+/* Define to 1 if you have the `getentropy' function. */
+#undef HAVE_GETENTROPY
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if you have the `getmntinfo' function. */
+#undef HAVE_GETMNTINFO
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#undef HAVE_GETPWUID_R
+
+/* Define to 1 if you have the `getrandom' function. */
+#undef HAVE_GETRANDOM
+
+/* Define to 1 if you have the `getrlimit' function. */
+#undef HAVE_GETRLIMIT
+
+/* Define to 1 if you have the `getrusage' function. */
+#undef HAVE_GETRUSAGE
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#undef HAVE_GETTEXT
+
+/* Define to 1 if you have the GNU-style 'qsort_r' function. */
+#undef HAVE_GNU_QSORT_R
+
+/* Define if you have the iconv() function and it works. */
+#undef HAVE_ICONV
+
+/* Define to 1 if the system has the type `intptr_t'. */
+#undef HAVE_INTPTR_T
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `jrand48' function. */
+#undef HAVE_JRAND48
+
+/* Define to 1 if you have the `keyctl' function. */
+#undef HAVE_KEYCTL
+
+/* Define to 1 if you have the <linux/falloc.h> header file. */
+#undef HAVE_LINUX_FALLOC_H
+
+/* Define to 1 if you have the <linux/fd.h> header file. */
+#undef HAVE_LINUX_FD_H
+
+/* Define to 1 if you have the <linux/fsmap.h> header file. */
+#undef HAVE_LINUX_FSMAP_H
+
+/* Define to 1 if you have the <linux/loop.h> header file. */
+#undef HAVE_LINUX_LOOP_H
+
+/* Define to 1 if you have the <linux/major.h> header file. */
+#undef HAVE_LINUX_MAJOR_H
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#undef HAVE_LINUX_TYPES_H
+
+/* Define to 1 if you have the `llistxattr' function. */
+#undef HAVE_LLISTXATTR
+
+/* Define to 1 if you have the `llseek' function. */
+#undef HAVE_LLSEEK
+
+/* Define to 1 if llseek declared in unistd.h */
+#undef HAVE_LLSEEK_PROTOTYPE
+
+/* Define to 1 if you have the `lseek64' function. */
+#undef HAVE_LSEEK64
+
+/* Define to 1 if lseek64 declared in unistd.h */
+#undef HAVE_LSEEK64_PROTOTYPE
+
+/* Define to 1 if you have the <magic.h> header file. */
+#undef HAVE_MAGIC_H
+
+/* Define to 1 if you have the `mallinfo' function. */
+#undef HAVE_MALLINFO
+
+/* Define to 1 if you have the `mallinfo2' function. */
+#undef HAVE_MALLINFO2
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `mbstowcs' function. */
+#undef HAVE_MBSTOWCS
+
+/* Define to 1 if you have the `memalign' function. */
+#undef HAVE_MEMALIGN
+
+/* Define to 1 if you have the `mempcpy' function. */
+#undef HAVE_MEMPCPY
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+#undef HAVE_MINIX_CONFIG_H
+
+/* Define to 1 if you have the `mmap' function. */
+#undef HAVE_MMAP
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#undef HAVE_MNTENT_H
+
+/* Define to 1 if mount supports nodev. */
+#undef HAVE_MOUNT_NODEV
+
+/* Define to 1 if mount supports nosuid. */
+#undef HAVE_MOUNT_NOSUID
+
+/* Define to 1 if you have the `msync' function. */
+#undef HAVE_MSYNC
+
+/* Define to 1 if you have the `nanosleep' function. */
+#undef HAVE_NANOSLEEP
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the <net/if_dl.h> header file. */
+#undef HAVE_NET_IF_DL_H
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
+/* Define to 1 if you have the `open64' function. */
+#undef HAVE_OPEN64
+
+/* Define to 1 if optreset for getopt is present */
+#undef HAVE_OPTRESET
+
+/* Define to 1 if you have the `pathconf' function. */
+#undef HAVE_PATHCONF
+
+/* Define to 1 if you have the <paths.h> header file. */
+#undef HAVE_PATHS_H
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#undef HAVE_POSIX_FADVISE
+
+/* Define to 1 if you have the `posix_fadvise64' function. */
+#undef HAVE_POSIX_FADVISE64
+
+/* Define to 1 if you have the `posix_memalign' function. */
+#undef HAVE_POSIX_MEMALIGN
+
+/* Define to 1 if you have the `prctl' function. */
+#undef HAVE_PRCTL
+
+/* Define to 1 if you have the `pread' function. */
+#undef HAVE_PREAD
+
+/* Define to 1 if you have the `pread64' function. */
+#undef HAVE_PREAD64
+
+/* Define if you have POSIX threads libraries and header files. */
+#undef HAVE_PTHREAD
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#undef HAVE_PTHREAD_PRIO_INHERIT
+
+/* Define to 1 if you have the `pwrite' function. */
+#undef HAVE_PWRITE
+
+/* Define to 1 if you have the `pwrite64' function. */
+#undef HAVE_PWRITE64
+
+/* Define to 1 if you have the `qsort_r' function. */
+#undef HAVE_QSORT_R
+
+/* Define to 1 if dirent has d_reclen */
+#undef HAVE_RECLEN_DIRENT
+
+/* Define to 1 if if struct sockaddr contains sa_len */
+#undef HAVE_SA_LEN
+
+/* Define to 1 if you have the `secure_getenv' function. */
+#undef HAVE_SECURE_GETENV
+
+/* Define to 1 if you have the <semaphore.h> header file. */
+#undef HAVE_SEMAPHORE_H
+
+/* Define to 1 if sem_init() exists */
+#undef HAVE_SEM_INIT
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#undef HAVE_SETJMP_H
+
+/* Define to 1 if you have the `setmntent' function. */
+#undef HAVE_SETMNTENT
+
+/* Define to 1 if you have the `setresgid' function. */
+#undef HAVE_SETRESGID
+
+/* Define to 1 if you have the `setresuid' function. */
+#undef HAVE_SETRESUID
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Define to 1 if you have the `srandom' function. */
+#undef HAVE_SRANDOM
+
+/* Define to 1 if struct stat has st_flags */
+#undef HAVE_STAT_FLAGS
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `stpcpy' function. */
+#undef HAVE_STPCPY
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strnlen' function. */
+#undef HAVE_STRNLEN
+
+/* Define to 1 if you have the `strptime' function. */
+#undef HAVE_STRPTIME
+
+/* Define to 1 if you have the `strtoull' function. */
+#undef HAVE_STRTOULL
+
+/* Define to 1 if `st_atim' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_ATIM
+
+/* Define to 1 if you have the `sync_file_range' function. */
+#undef HAVE_SYNC_FILE_RANGE
+
+/* Define to 1 if you have the `sysconf' function. */
+#undef HAVE_SYSCONF
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+#undef HAVE_SYS_ACL_H
+
+/* Define to 1 if you have the <sys/disklabel.h> header file. */
+#undef HAVE_SYS_DISKLABEL_H
+
+/* Define to 1 if you have the <sys/disk.h> header file. */
+#undef HAVE_SYS_DISK_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/key.h> header file. */
+#undef HAVE_SYS_KEY_H
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+#undef HAVE_SYS_MKDEV_H
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#undef HAVE_SYS_MMAN_H
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#undef HAVE_SYS_MOUNT_H
+
+/* Define to 1 if you have the <sys/prctl.h> header file. */
+#undef HAVE_SYS_PRCTL_H
+
+/* Define to 1 if you have the <sys/random.h> header file. */
+#undef HAVE_SYS_RANDOM_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+#undef HAVE_SYS_SOCKIO_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+#undef HAVE_SYS_SYSCALL_H
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#undef HAVE_SYS_SYSMACROS_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#undef HAVE_SYS_XATTR_H
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define to 1 if you have the <termio.h> header file. */
+#undef HAVE_TERMIO_H
+
+/* Define to 1 if ssize_t declared */
+#undef HAVE_TYPE_SSIZE_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `usleep' function. */
+#undef HAVE_USLEEP
+
+/* Define to 1 if you have the `utime' function. */
+#undef HAVE_UTIME
+
+/* Define to 1 if you have the `utimes' function. */
+#undef HAVE_UTIMES
+
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
+/* Define to 1 if you have the `valloc' function. */
+#undef HAVE_VALLOC
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
+/* Define to 1 if you have the `__secure_getenv' function. */
+#undef HAVE___SECURE_GETENV
+
+/* package name for gettext */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
+/* The size of `off_t', as computed by sizeof. */
+#undef SIZEOF_OFF_T
+
+/* The size of `short', as computed by sizeof. */
+#undef SIZEOF_SHORT
+
+/* The size of `time_t', as computed by sizeof. */
+#undef SIZEOF_TIME_T
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* If the compiler supports a TLS storage class define it to that here */
+#undef TLS
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# undef _DARWIN_C_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# undef _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+# undef _MINIX
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# undef _NETBSD_SOURCE
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# undef _OPENBSD_SOURCE
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+# undef _POSIX_SOURCE
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+# undef _POSIX_1_SOURCE
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# undef __STDC_WANT_IEC_60559_BFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# undef __STDC_WANT_IEC_60559_DFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# undef __STDC_WANT_IEC_60559_TYPES_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# undef __STDC_WANT_LIB_EXT2__
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# undef __STDC_WANT_MATH_SPEC_FUNCS__
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+
+
+/* Define to 1 to build uuidd */
+#undef USE_UUIDD
+
+/* version for gettext */
+#undef VERSION
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define to 1 if Apple Darwin libintl workaround is needed */
+#undef _INTL_REDIRECT_MACROS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+#include <dirpaths.h>
diff --git a/lib/dirpaths.h.in b/lib/dirpaths.h.in
new file mode 100644
index 0000000..6ccb55b
--- /dev/null
+++ b/lib/dirpaths.h.in
@@ -0,0 +1,10 @@
+/*
+ * This file contains the path names for various directories as
+ * controlled by the configure script.
+ */
+
+/* Where to put the messages file for internationalization support */
+#define LOCALEDIR "@datadir@/locale"
+
+/* Where to find the mke2fs.conf and e2fsck.conf files */
+#define ROOT_SYSCONFDIR "@root_sysconfdir@"
diff --git a/lib/e2p/Android.bp b/lib/e2p/Android.bp
new file mode 100644
index 0000000..bed92c1
--- /dev/null
+++ b/lib/e2p/Android.bp
@@ -0,0 +1,58 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-GPL
+ // SPDX-license-identifier-LGPL
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library {
+ name: "libext2_e2p",
+ host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "encoding.c",
+ "errcode.c",
+ "feature.c",
+ "fgetflags.c",
+ "fsetflags.c",
+ "fgetproject.c",
+ "fsetproject.c",
+ "fgetversion.c",
+ "fsetversion.c",
+ "getflags.c",
+ "getversion.c",
+ "hashstr.c",
+ "iod.c",
+ "ljs.c",
+ "ls.c",
+ "mntopts.c",
+ "parse_num.c",
+ "pe.c",
+ "pf.c",
+ "ps.c",
+ "setflags.c",
+ "setversion.c",
+ "uuid.c",
+ "ostype.c",
+ "percent.c",
+ ],
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
diff --git a/lib/e2p/Makefile.in b/lib/e2p/Makefile.in
new file mode 100644
index 0000000..92d9c01
--- /dev/null
+++ b/lib/e2p/Makefile.in
@@ -0,0 +1,223 @@
+# Makefile for the second extended file system utility functions
+#
+# Copyright (C) 1993 Remy Card (card@masi.ibp.fr)
+#
+# This file can be redistributed under the terms of the GNU General
+# Public License
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/e2p
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+
+@MCONFIG@
+
+all:: e2p.pc
+
+OBJS= feature.o fgetflags.o fsetflags.o fgetversion.o fsetversion.o \
+ getflags.o getversion.o hashstr.o iod.o ls.o ljs.o mntopts.o \
+ parse_num.o pe.o pf.o ps.o setflags.o setversion.o uuid.o \
+ ostype.o percent.o crypto_mode.o fgetproject.o fsetproject.o \
+ encoding.o errcode.o
+
+SRCS= $(srcdir)/feature.c $(srcdir)/fgetflags.c \
+ $(srcdir)/fsetflags.c $(srcdir)/fgetversion.c \
+ $(srcdir)/fsetversion.c $(srcdir)/getflags.c \
+ $(srcdir)/getversion.c $(srcdir)/hashstr.c $(srcdir)/iod.c \
+ $(srcdir)/ls.c $(srcdir)/ljs.c $(srcdir)/mntopts.c \
+ $(srcdir)/parse_num.c $(srcdir)/pe.c $(srcdir)/pf.c \
+ $(srcdir)/ps.c $(srcdir)/setflags.c $(srcdir)/setversion.c \
+ $(srcdir)/uuid.c $(srcdir)/ostype.c $(srcdir)/percent.c \
+ $(srcdir)/crypto_mode.c $(srcdir)/fgetproject.c \
+ $(srcdir)/fsetproject.c $(srcdir)/encoding.c \
+ $(srcdir)/errcode.c
+
+HFILES= e2p.h
+
+LIBRARY= libe2p
+LIBDIR= e2p
+
+ELF_VERSION = 2.3
+ELF_SO_VERSION = 2
+ELF_IMAGE = libe2p
+ELF_MYDIR = e2p
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS =
+
+BSDLIB_VERSION = 2.1
+BSDLIB_IMAGE = libe2p
+BSDLIB_MYDIR = e2p
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+e2p.pc: $(srcdir)/e2p.pc.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); CONFIG_FILES=lib/e2p/e2p.pc ./config.status
+
+tst_ostype: $(srcdir)/ostype.c
+ $(E) " LD $@"
+ $(Q) $(CC) -DTEST_PROGRAM -I$(top_srcdir)/lib -o tst_ostype \
+ $(srcdir)/ostype.c $(ALL_CFLAGS) $(ALL_LDFLAGS)
+
+tst_feature: $(srcdir)/feature.c
+ $(E) " LD $@"
+ $(Q) $(CC) -DTEST_PROGRAM -I$(top_srcdir)/lib -o tst_feature \
+ $(srcdir)/feature.c $(ALL_CFLAGS) $(ALL_LDFLAGS)
+
+fullcheck check:: tst_ostype tst_feature
+ ./tst_ostype
+ ./tst_feature
+
+installdirs::
+ $(E) " MKDIR_P $(libdir) $(includedir)/e2p"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(includedir)/e2p $(DESTDIR)$(pkgconfigdir)
+
+install:: all installdirs
+ $(E) " INSTALL_DATA $(libdir)/libe2p.a"
+ $(Q) $(INSTALL_DATA) libe2p.a $(DESTDIR)$(libdir)/libe2p.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libe2p.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libe2p.a
+ $(Q) set -e; for i in $(HFILES); do \
+ echo " INSTALL_DATA $(includedir)/e2p/$$i"; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/e2p/$$i; \
+ done
+ $(E) " INSTALL_DATA $(pkgconfigdir)/e2p.pc"
+ $(Q) $(INSTALL_DATA) e2p.pc $(DESTDIR)$(pkgconfigdir)/e2p.pc
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/libe2p.a \
+ $(DESTDIR)$(pkgconfigdir)/e2p.pc
+ $(RM) -rf $(DESTDIR)$(includedir)/e2p
+
+clean::
+ $(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/*
+ $(RM) -f ../libe2p.a ../libe2p_p.a tst_ostype tst_feature e2p.pc
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend Makefile e2p.pc \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+$(OBJS): subdirs
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+feature.o: $(srcdir)/feature.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(top_srcdir)/lib/ext2fs/kernel-jbd.h \
+ $(top_srcdir)/lib/ext2fs/jfs_compat.h $(top_srcdir)/lib/ext2fs/kernel-list.h \
+ $(top_srcdir)/lib/ext2fs/compiler.h
+fgetflags.o: $(srcdir)/fgetflags.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+fsetflags.o: $(srcdir)/fsetflags.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+fgetversion.o: $(srcdir)/fgetversion.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+fsetversion.o: $(srcdir)/fsetversion.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+getflags.o: $(srcdir)/getflags.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+getversion.o: $(srcdir)/getversion.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+hashstr.o: $(srcdir)/hashstr.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+iod.o: $(srcdir)/iod.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+ls.o: $(srcdir)/ls.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+ljs.o: $(srcdir)/ljs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/kernel-jbd.h $(top_srcdir)/lib/ext2fs/jfs_compat.h \
+ $(top_srcdir)/lib/ext2fs/kernel-list.h $(top_srcdir)/lib/ext2fs/compiler.h
+mntopts.o: $(srcdir)/mntopts.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+parse_num.o: $(srcdir)/parse_num.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+pe.o: $(srcdir)/pe.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+pf.o: $(srcdir)/pf.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+ps.o: $(srcdir)/ps.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+setflags.o: $(srcdir)/setflags.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+setversion.o: $(srcdir)/setversion.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+uuid.o: $(srcdir)/uuid.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/e2p.h $(top_srcdir)/lib/ext2fs/ext2_fs.h
+ostype.o: $(srcdir)/ostype.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+percent.o: $(srcdir)/percent.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+crypto_mode.o: $(srcdir)/crypto_mode.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+fgetproject.o: $(srcdir)/fgetproject.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/project.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/e2p.h
+fsetproject.o: $(srcdir)/fsetproject.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/project.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/e2p.h
+encoding.o: $(srcdir)/encoding.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
+errcode.o: $(srcdir)/errcode.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/e2p.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h
diff --git a/lib/e2p/crypto_mode.c b/lib/e2p/crypto_mode.c
new file mode 100644
index 0000000..4933016
--- /dev/null
+++ b/lib/e2p/crypto_mode.c
@@ -0,0 +1,74 @@
+/*
+ * crypto_mode.c --- convert between encryption modes and strings
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct mode {
+ int num;
+ const char *string;
+};
+
+static struct mode mode_list[] = {
+ { EXT4_ENCRYPTION_MODE_INVALID, "Invalid"},
+ { EXT4_ENCRYPTION_MODE_AES_256_XTS, "AES-256-XTS"},
+ { EXT4_ENCRYPTION_MODE_AES_256_GCM, "AES-256-GCM"},
+ { EXT4_ENCRYPTION_MODE_AES_256_CBC, "AES-256-CBC"},
+ { 0, 0 },
+};
+
+const char *e2p_encmode2string(int num)
+{
+ struct mode *p;
+ static char buf[20];
+
+ for (p = mode_list; p->string; p++) {
+ if (num == p->num)
+ return p->string;
+ }
+ sprintf(buf, "ENC_MODE_%d", num);
+ return buf;
+}
+
+/*
+ * Returns the hash algorithm, or -1 on error
+ */
+int e2p_string2encmode(char *string)
+{
+ struct mode *p;
+ char *eptr;
+ int num;
+
+ for (p = mode_list; p->string; p++) {
+ if (!strcasecmp(string, p->string)) {
+ return p->num;
+ }
+ }
+ if (strncasecmp(string, "ENC_MODE_", 9))
+ return -1;
+
+ if (string[9] == 0)
+ return -1;
+ num = strtol(string+9, &eptr, 10);
+ if (num > 255 || num < 0)
+ return -1;
+ if (*eptr)
+ return -1;
+ return num;
+}
+
diff --git a/lib/e2p/e2p.h b/lib/e2p/e2p.h
new file mode 100644
index 0000000..5f4793e
--- /dev/null
+++ b/lib/e2p/e2p.h
@@ -0,0 +1,92 @@
+/*
+ * 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 <sys/types.h> /* Needed by dirent.h on netbsd */
+#include <stdio.h>
+#include <dirent.h>
+
+#include <ext2fs/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 fgetproject(const char *name, unsigned long *project);
+int fsetproject(const char *name, unsigned long project);
+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_arg);
+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);
+
+#define E2P_LIST_JOURNAL_FLAG_FC 0x1
+void e2p_list_journal_super(FILE *f, char *journal_sb_buf,
+ int exp_block_size, int flags);
+
+void e2p_feature_to_string(int compat, unsigned int mask, char *buf,
+ size_t buf_len);
+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);
+
+const char *e2p_encmode2string(int num);
+int e2p_string2encmode(char *string);
+
+int e2p_str2encoding(const char *string);
+const char *e2p_encoding2str(int encoding);
+int e2p_get_encoding_flags(int encoding);
+int e2p_str2encoding_flags(int encoding, char *param, __u16 *flags);
+
+const char *e2p_errcode2str(unsigned int err);
diff --git a/lib/e2p/e2p.pc.in b/lib/e2p/e2p.pc.in
new file mode 100644
index 0000000..150b089
--- /dev/null
+++ b/lib/e2p/e2p.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: e2p
+Description: Ext2fs userspace programs utility library
+Version: @E2FSPROGS_VERSION@
+Requires:
+Cflags: -I${includedir}/e2p -I${includedir}
+Libs: -L${libdir} -le2p
diff --git a/lib/e2p/encoding.c b/lib/e2p/encoding.c
new file mode 100644
index 0000000..24266fc
--- /dev/null
+++ b/lib/e2p/encoding.c
@@ -0,0 +1,118 @@
+/*
+ * encoding.c --- convert between encoding magic numbers and strings
+ *
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "e2p.h"
+
+#define ARRAY_SIZE(array) \
+ (sizeof(array) / sizeof(array[0]))
+
+static const struct {
+ const char *name;
+ __u16 encoding_magic;
+ __u16 default_flags;
+
+} ext4_encoding_map[] = {
+ {
+ .encoding_magic = EXT4_ENC_UTF8_12_1,
+ .name = "utf8-12.1",
+ .default_flags = 0,
+ },
+ {
+ .encoding_magic = EXT4_ENC_UTF8_12_1,
+ .name = "utf8",
+ .default_flags = 0,
+ },
+};
+
+static const struct enc_flags {
+ __u16 flag;
+ const char *param;
+} encoding_flags[] = {
+ { EXT4_ENC_STRICT_MODE_FL, "strict" },
+};
+
+/* Return a positive number < 0xff indicating the encoding magic number
+ * or a negative value indicating error. */
+int e2p_str2encoding(const char *string)
+{
+ unsigned int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(ext4_encoding_map); i++)
+ if (!strcmp(string, ext4_encoding_map[i].name))
+ return ext4_encoding_map[i].encoding_magic;
+
+ return -EINVAL;
+}
+
+/* Return the name of an encoding or NULL */
+const char *e2p_encoding2str(int encoding)
+{
+ unsigned int i;
+ static char buf[32];
+
+ for (i = 0 ; i < ARRAY_SIZE(ext4_encoding_map); i++)
+ if (ext4_encoding_map[i].encoding_magic == encoding)
+ return ext4_encoding_map[i].name;
+ sprintf(buf, "UNKNOWN_ENCODING_%d", encoding);
+ return buf;
+}
+
+int e2p_get_encoding_flags(int encoding)
+{
+ unsigned int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(ext4_encoding_map); i++)
+ if (ext4_encoding_map[i].encoding_magic == encoding)
+ return ext4_encoding_map[i].default_flags;
+
+ return 0;
+}
+
+int e2p_str2encoding_flags(int encoding, char *param, __u16 *flags)
+{
+ char *f = strtok(param, "-");
+ const struct enc_flags *fl;
+ unsigned int i, neg = 0;
+
+ if (encoding != EXT4_ENC_UTF8_12_1)
+ return -EINVAL;
+ while (f) {
+ neg = 0;
+ if (!strncmp("no", f, 2)) {
+ neg = 1;
+ f += 2;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(encoding_flags); i++) {
+ fl = &encoding_flags[i];
+ if (!strcmp(fl->param, f)) {
+ if (neg)
+ *flags &= ~fl->flag;
+ else
+ *flags |= fl->flag;
+
+ goto next_flag;
+ }
+ }
+ return -EINVAL;
+ next_flag:
+ f = strtok(NULL, "-");
+ }
+ return 0;
+}
diff --git a/lib/e2p/errcode.c b/lib/e2p/errcode.c
new file mode 100644
index 0000000..1627c9d
--- /dev/null
+++ b/lib/e2p/errcode.c
@@ -0,0 +1,48 @@
+/*
+ * errcode.c - convert an error code to a string
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "e2p.h"
+
+static const char *err_string[] = {
+ "",
+ "UNKNOWN", /* 1 */
+ "EIO", /* 2 */
+ "ENOMEM", /* 3 */
+ "EFSBADCRC", /* 4 */
+ "EFSCORRUPTED", /* 5 */
+ "ENOSPC", /* 6 */
+ "ENOKEY", /* 7 */
+ "EROFS", /* 8 */
+ "EFBIG", /* 9 */
+ "EEXIST", /* 10 */
+ "ERANGE", /* 11 */
+ "EOVERFLOW", /* 12 */
+ "EBUSY", /* 13 */
+ "ENOTDIR", /* 14 */
+ "ENOTEMPTY", /* 15 */
+ "ESHUTDOWN", /* 16 */
+ "EFAULT", /* 17 */
+};
+
+#define ARRAY_SIZE(array) \
+ (sizeof(array) / sizeof(array[0]))
+
+/* Return the name of an encoding or NULL */
+const char *e2p_errcode2str(unsigned int err)
+{
+ static char buf[32];
+
+ if (err < ARRAY_SIZE(err_string))
+ return err_string[err];
+
+ sprintf(buf, "UNKNOWN_ERRCODE_%u", err);
+ return buf;
+}
+
+
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
new file mode 100644
index 0000000..29b7b15
--- /dev/null
+++ b/lib/e2p/feature.c
@@ -0,0 +1,449 @@
+/*
+ * feature.c --- convert between features and strings
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+#include <ext2fs/ext2fs.h>
+#include <ext2fs/kernel-jbd.h>
+
+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_COMPAT, EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP,
+ "snapshot_bitmap" },
+ { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_SPARSE_SUPER2,
+ "sparse_super2" },
+ { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_FAST_COMMIT,
+ "fast_commit" },
+ { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_STABLE_INODES,
+ "stable_inodes" },
+ { E2P_FEATURE_COMPAT, EXT4_FEATURE_COMPAT_ORPHAN_FILE,
+ "orphan_file" },
+
+ { 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_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_QUOTA,
+ "quota" },
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_BIGALLOC,
+ "bigalloc"},
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM,
+ "metadata_csum"},
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_REPLICA,
+ "replica" },
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_READONLY,
+ "read-only" },
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_PROJECT,
+ "project"},
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS,
+ "shared_blocks"},
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_VERITY,
+ "verity"},
+ { E2P_FEATURE_RO_INCOMPAT, EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT,
+ "orphan_present" },
+
+ { 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_MMP,
+ "mmp" },
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG,
+ "flex_bg"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_EA_INODE,
+ "ea_inode"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_DIRDATA,
+ "dirdata"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CSUM_SEED,
+ "metadata_csum_seed"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_LARGEDIR,
+ "large_dir"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INLINE_DATA,
+ "inline_data"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT,
+ "encrypt"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CASEFOLD,
+ "casefold"},
+ { E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CASEFOLD,
+ "fname_encoding"},
+ { 0, 0, 0 },
+};
+
+static struct feature jrnl_feature_list[] = {
+ { E2P_FEATURE_COMPAT, JBD2_FEATURE_COMPAT_CHECKSUM,
+ "journal_checksum" },
+
+ { E2P_FEATURE_INCOMPAT, JBD2_FEATURE_INCOMPAT_REVOKE,
+ "journal_incompat_revoke" },
+ { E2P_FEATURE_INCOMPAT, JBD2_FEATURE_INCOMPAT_64BIT,
+ "journal_64bit" },
+ { E2P_FEATURE_INCOMPAT, JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT,
+ "journal_async_commit" },
+ { E2P_FEATURE_INCOMPAT, JBD2_FEATURE_INCOMPAT_CSUM_V2,
+ "journal_checksum_v2" },
+ { E2P_FEATURE_INCOMPAT, JBD2_FEATURE_INCOMPAT_CSUM_V3,
+ "journal_checksum_v3" },
+ { 0, 0, 0 },
+};
+
+void e2p_feature_to_string(int compat, unsigned int mask, char *buf,
+ size_t buf_len)
+{
+ struct feature *f;
+ char fchar;
+ int fnum;
+
+ for (f = feature_list; f->string; f++) {
+ if ((compat == f->compat) &&
+ (mask == f->mask)) {
+ strncpy(buf, f->string, buf_len);
+ buf[buf_len - 1] = 0;
+ return;
+ }
+ }
+ 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);
+}
+
+const char *e2p_feature2string(int compat, unsigned int mask)
+{
+ static char buf[20];
+
+ e2p_feature_to_string(compat, mask, buf, sizeof(buf) / sizeof(buf[0]));
+ 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 > 31 || 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 > 31 || num < 0)
+ return 1;
+ if (*eptr)
+ return 1;
+ *mask = 1 << num;
+ return 0;
+}
+static char *skip_over_blanks(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ while (*cp && !isspace(*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++;
+ /* fallthrough */
+ 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/e2p/fgetflags.c b/lib/e2p/fgetflags.c
new file mode 100644
index 0000000..24a7166
--- /dev/null
+++ b/lib/e2p/fgetflags.c
@@ -0,0 +1,117 @@
+/*
+ * fgetflags.c - Get a file flags on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_EXT2_IOCTLS
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+#include "e2p.h"
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_NOFOLLOW)
+
+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;
+#elif APPLE_DARWIN && HAVE_EXT2_IOCTLS
+ int f, save_errno = 0;
+
+ f = -1;
+ save_errno = syscall(SYS_fsctl, name, EXT2_IOC_GETFLAGS, &f, 0);
+ *flags = f;
+ return (save_errno);
+#elif HAVE_EXT2_IOCTLS
+ struct stat buf;
+ int fd, r, f, save_errno = 0;
+
+ if (!stat(name, &buf) &&
+ !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ fd = open(name, OPEN_FLAGS);
+ if (fd == -1) {
+ if (errno == ELOOP || errno == ENXIO)
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ if (!fstat(fd, &buf) &&
+ !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
+ close(fd);
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ r = ioctl(fd, EXT2_IOC_GETFLAGS, &f);
+ if (r == -1) {
+ if (errno == ENOTTY)
+ errno = EOPNOTSUPP;
+ save_errno = errno;
+ }
+ *flags = f;
+ close(fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+#else
+ errno = EOPNOTSUPP;
+ return -1;
+#endif
+}
diff --git a/lib/e2p/fgetproject.c b/lib/e2p/fgetproject.c
new file mode 100644
index 0000000..12320b5
--- /dev/null
+++ b/lib/e2p/fgetproject.c
@@ -0,0 +1,63 @@
+/*
+ * fgetproject.c --- get project id
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_EXT2_IOCTLS
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include "project.h"
+#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 fgetproject(const char *name, unsigned long *project)
+{
+#ifndef FS_IOC_FSGETXATTR
+ errno = EOPNOTSUPP;
+ return -1;
+#else
+ int fd, r, save_errno = 0;
+ struct fsxattr fsx;
+
+ fd = open (name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+ r = ioctl (fd, FS_IOC_FSGETXATTR, &fsx);
+ if (r == 0)
+ *project = fsx.fsx_projid;
+ save_errno = errno;
+ close (fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+#endif
+}
diff --git a/lib/e2p/fgetversion.c b/lib/e2p/fgetversion.c
new file mode 100644
index 0000000..f3a5b4c
--- /dev/null
+++ b/lib/e2p/fgetversion.c
@@ -0,0 +1,74 @@
+/*
+ * fgetversion.c - Get a file version on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#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)
+{
+ unsigned int ver = -1;
+ int rc = -1;
+#if HAVE_EXT2_IOCTLS
+# if !APPLE_DARWIN
+ int fd, save_errno = 0;
+
+ fd = open(name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+
+ rc = ioctl(fd, EXT2_IOC_GETVERSION, &ver);
+ if (rc == -1)
+ save_errno = errno;
+ close(fd);
+ if (rc == -1)
+ errno = save_errno;
+# else /* APPLE_DARWIN */
+ rc = syscall(SYS_fsctl, name, EXT2_IOC_GETVERSION, &ver, 0);
+# endif /* !APPLE_DARWIN */
+#else /* ! HAVE_EXT2_IOCTLS */
+ errno = EOPNOTSUPP;
+#endif /* ! HAVE_EXT2_IOCTLS */
+ if (rc == 0)
+ *version = ver;
+
+ return rc;
+}
diff --git a/lib/e2p/fsetflags.c b/lib/e2p/fsetflags.c
new file mode 100644
index 0000000..d865d24
--- /dev/null
+++ b/lib/e2p/fsetflags.c
@@ -0,0 +1,118 @@
+/*
+ * fsetflags.c - Set a file flags on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_EXT2_IOCTLS
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#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
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_NOFOLLOW)
+
+int fsetflags (const char * name, unsigned long flags)
+{
+#if HAVE_CHFLAGS && !(APPLE_DARWIN && HAVE_EXT2_IOCTLS)
+ 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);
+#elif APPLE_DARWIN && HAVE_EXT2_IOCTLS
+ int f = (int) flags;
+ return syscall(SYS_fsctl, name, EXT2_IOC_SETFLAGS, &f, 0);
+#elif HAVE_EXT2_IOCTLS
+ struct stat buf;
+ int fd, r, f, save_errno = 0;
+
+ if (!stat(name, &buf) &&
+ !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ fd = open(name, OPEN_FLAGS);
+ if (fd == -1) {
+ if (errno == ELOOP || errno == ENXIO)
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ if (!fstat(fd, &buf) &&
+ !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
+ close(fd);
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ f = (int) flags;
+ r = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+ if (r == -1) {
+ if (errno == ENOTTY)
+ errno = EOPNOTSUPP;
+ save_errno = errno;
+ }
+ close(fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+#else
+ errno = EOPNOTSUPP;
+ return -1;
+#endif
+}
diff --git a/lib/e2p/fsetproject.c b/lib/e2p/fsetproject.c
new file mode 100644
index 0000000..5df7090
--- /dev/null
+++ b/lib/e2p/fsetproject.c
@@ -0,0 +1,69 @@
+/*
+ * fgetproject.c --- get project id
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_EXT2_IOCTLS
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include "project.h"
+#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 fsetproject(const char *name, unsigned long project)
+{
+#ifndef FS_IOC_FSGETXATTR
+ errno = EOPNOTSUPP;
+ return -1;
+#else
+ int fd, r, save_errno = 0;
+ struct fsxattr fsx;
+
+ fd = open (name, OPEN_FLAGS);
+ if (fd == -1)
+ return -1;
+ r = ioctl (fd, FS_IOC_FSGETXATTR, &fsx);
+ if (r == -1) {
+ save_errno = errno;
+ goto errout;
+ }
+ fsx.fsx_projid = project;
+ r = ioctl (fd, FS_IOC_FSSETXATTR, &fsx);
+ if (r == -1)
+ save_errno = errno;
+errout:
+ close (fd);
+ if (save_errno)
+ errno = save_errno;
+ return r;
+#endif
+}
diff --git a/lib/e2p/fsetversion.c b/lib/e2p/fsetversion.c
new file mode 100644
index 0000000..5f844b5
--- /dev/null
+++ b/lib/e2p/fsetversion.c
@@ -0,0 +1,71 @@
+/*
+ * fsetversion.c - Set a file version on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#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 */
+ errno = EOPNOTSUPP;
+ return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
diff --git a/lib/e2p/getflags.c b/lib/e2p/getflags.c
new file mode 100644
index 0000000..6708cd6
--- /dev/null
+++ b/lib/e2p/getflags.c
@@ -0,0 +1,71 @@
+/*
+ * getflags.c - Get a file flags on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_EXT2_IOCTLS
+#include <sys/ioctl.h>
+#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
+ struct stat buf;
+ 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;
+notsupp:
+#endif /* HAVE_EXT2_IOCTLS */
+#endif
+ errno = EOPNOTSUPP;
+ return -1;
+}
diff --git a/lib/e2p/getversion.c b/lib/e2p/getversion.c
new file mode 100644
index 0000000..d374a0e
--- /dev/null
+++ b/lib/e2p/getversion.c
@@ -0,0 +1,41 @@
+/*
+ * getversion.c - Get a file version on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#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 r;
+#else /* ! HAVE_EXT2_IOCTLS */
+ errno = EOPNOTSUPP;
+ return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
diff --git a/lib/e2p/hashstr.c b/lib/e2p/hashstr.c
new file mode 100644
index 0000000..a73758c
--- /dev/null
+++ b/lib/e2p/hashstr.c
@@ -0,0 +1,72 @@
+/*
+ * feature.c --- convert between features and strings
+ *
+ * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#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/e2p/iod.c b/lib/e2p/iod.c
new file mode 100644
index 0000000..6a030dd
--- /dev/null
+++ b/lib/e2p/iod.c
@@ -0,0 +1,76 @@
+/*
+ * iod.c - Iterate a function on each entry of a directory
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#include "e2p.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+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/e2p/ljs.c b/lib/e2p/ljs.c
new file mode 100644
index 0000000..5972819
--- /dev/null
+++ b/lib/e2p/ljs.c
@@ -0,0 +1,134 @@
+/*
+ * ljs.c - List the contents of an journal superblock
+ *
+ * Copyright (C) 1995, 1996, 1997 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <grp.h>
+#include <pwd.h>
+#include <time.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "e2p.h"
+#include "ext2fs/kernel-jbd.h"
+
+#ifdef WORDS_BIGENDIAN
+#define e2p_be32(x) (x)
+#else
+static __u32 e2p_swab32(__u32 val)
+{
+ return ((val>>24) | ((val>>8)&0xFF00) |
+ ((val<<8)&0xFF0000) | (val<<24));
+}
+
+#define e2p_be32(x) e2p_swab32(x)
+#endif
+
+/*
+ * This function is copied from kernel-jbd.h's function
+ * jbd2_journal_get_num_fc_blks() to avoid inter-library dependencies.
+ */
+static inline int get_num_fc_blks(journal_superblock_t *jsb)
+{
+ int num_fc_blocks = e2p_be32(jsb->s_num_fc_blks);
+
+ return num_fc_blocks ? num_fc_blocks : JBD2_DEFAULT_FAST_COMMIT_BLOCKS;
+}
+
+static const char *journal_checksum_type_str(__u8 type)
+{
+ switch (type) {
+ case JBD2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
+void e2p_list_journal_super(FILE *f, char *journal_sb_buf,
+ int exp_block_size, int flags)
+{
+ journal_superblock_t *jsb = (journal_superblock_t *) journal_sb_buf;
+ __u32 *mask_ptr, mask, m;
+ unsigned int size;
+ int j, printed = 0;
+ unsigned int i, nr_users;
+ int num_fc_blks = 0;
+ int journal_blks = 0;
+
+ if (flags & E2P_LIST_JOURNAL_FLAG_FC)
+ num_fc_blks = get_num_fc_blks((journal_superblock_t *)journal_sb_buf);
+ journal_blks = ntohl(jsb->s_maxlen) - num_fc_blks;
+ fprintf(f, "%s", "Journal features: ");
+ for (i=0, mask_ptr=&jsb->s_feature_compat; i <3; i++,mask_ptr++) {
+ mask = e2p_be32(*mask_ptr);
+ for (j=0,m=1; j < 32; j++, m<<=1) {
+ if (mask & m) {
+ fprintf(f, " %s", e2p_jrnl_feature2string(i, m));
+ printed++;
+ }
+ }
+ }
+ if (printed == 0)
+ fprintf(f, " (none)");
+ fputc('\n', f);
+ fputs("Total journal size: ", f);
+ size = (ntohl(jsb->s_blocksize) / 1024) * ntohl(jsb->s_maxlen);
+ if (size < 8192)
+ fprintf(f, "%uk\n", size);
+ else
+ fprintf(f, "%uM\n", size >> 10);
+ nr_users = (unsigned int) ntohl(jsb->s_nr_users);
+ if (exp_block_size != (int) ntohl(jsb->s_blocksize))
+ fprintf(f, "Journal block size: %u\n",
+ (unsigned int)ntohl(jsb->s_blocksize));
+ fprintf(f, "Total journal blocks: %u\n",
+ (unsigned int)(journal_blks + num_fc_blks));
+ fprintf(f, "Max transaction length: %u\n",
+ (unsigned int)journal_blks);
+ fprintf(f, "Fast commit length: %u\n",
+ (unsigned int)num_fc_blks);
+
+ if (ntohl(jsb->s_first) != 1)
+ fprintf(f, "Journal first block: %u\n",
+ (unsigned int)ntohl(jsb->s_first));
+ fprintf(f, "Journal sequence: 0x%08x\n"
+ "Journal start: %u\n",
+ (unsigned int)ntohl(jsb->s_sequence),
+ (unsigned int)ntohl(jsb->s_start));
+ if (nr_users != 1)
+ fprintf(f, "Journal number of users: %u\n", nr_users);
+ if (jsb->s_feature_compat & e2p_be32(JBD2_FEATURE_COMPAT_CHECKSUM))
+ fprintf(f, "%s", "Journal checksum type: crc32\n");
+ if ((jsb->s_feature_incompat &
+ e2p_be32(JBD2_FEATURE_INCOMPAT_CSUM_V3)) ||
+ (jsb->s_feature_incompat &
+ e2p_be32(JBD2_FEATURE_INCOMPAT_CSUM_V2)))
+ fprintf(f, "Journal checksum type: %s\n"
+ "Journal checksum: 0x%08x\n",
+ journal_checksum_type_str(jsb->s_checksum_type),
+ e2p_be32(jsb->s_checksum));
+ if ((nr_users > 1) ||
+ !e2p_is_null_uuid(&jsb->s_users[0])) {
+ for (i=0; i < nr_users && i < JBD2_USERS_MAX; i++) {
+ printf(i ? " %s\n"
+ : "Journal users: %s\n",
+ e2p_uuid2str(&jsb->s_users[i * UUID_SIZE]));
+ }
+ }
+ if (jsb->s_errno != 0)
+ fprintf(f, "Journal errno: %d\n",
+ (int) ntohl(jsb->s_errno));
+}
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
new file mode 100644
index 0000000..0b74aea
--- /dev/null
+++ b/lib/e2p/ls.c
@@ -0,0 +1,494 @@
+/*
+ * ls.c - List the contents of an ext2fs superblock
+ *
+ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * Laboratoire MASI, Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright (C) 1995, 1996, 1997 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <grp.h>
+#include <pwd.h>
+#include <time.h>
+
+#include "e2p.h"
+#include "support/quotaio.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 "<none>";
+
+ 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 |
+ (ext2fs_has_feature_64bit(super) ?
+ (__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 |
+ (ext2fs_has_feature_64bit(super) ?
+ (__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 |
+ (ext2fs_has_feature_64bit(super) ?
+ (__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
+
+static const char *checksum_type(__u8 type)
+{
+ switch (type) {
+ case EXT2_CRC32C_CHKSUM:
+ return "crc32c";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *quota_prefix[MAXQUOTAS] = {
+ [USRQUOTA] = "User quota inode:",
+ [GRPQUOTA] = "Group quota inode:",
+ [PRJQUOTA] = "Project quota inode:",
+};
+
+/**
+ * Convert type of quota to written representation
+ */
+static const char *quota_type2prefix(enum quota_type qtype)
+{
+ return quota_prefix[qtype];
+}
+
+void list_super2(struct ext2_super_block * sb, FILE *f)
+{
+ int inode_blocks_per_group;
+ char *str;
+ time_t tm;
+ enum quota_type qtype;
+
+ 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])
+ fprintf(f, "Filesystem volume name: %.*s\n",
+ EXT2_LEN_STR(sb->s_volume_name));
+ else
+ fprintf(f, "Filesystem volume name: <none>\n");
+ if (sb->s_last_mounted[0])
+ fprintf(f, "Last mounted on: %.*s\n",
+ EXT2_LEN_STR(sb->s_last_mounted));
+ else
+ fprintf(f, "Last mounted on: <not available>\n");
+ 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",
+ EXT2_LEN_STR(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",
+ (unsigned long long) e2p_blocks_count(sb));
+ fprintf(f, "Reserved block count: %llu\n",
+ (unsigned long long) e2p_r_blocks_count(sb));
+ if (sb->s_overhead_clusters)
+ fprintf(f, "Overhead clusters: %u\n",
+ sb->s_overhead_clusters);
+ fprintf(f, "Free blocks: %llu\n",
+ (unsigned long long) 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));
+ if (ext2fs_has_feature_bigalloc(sb))
+ fprintf(f, "Cluster size: %u\n",
+ EXT2_CLUSTER_SIZE(sb));
+ else
+ fprintf(f, "Fragment size: %u\n",
+ EXT2_CLUSTER_SIZE(sb));
+ if (ext2fs_has_feature_64bit(sb))
+ fprintf(f, "Group descriptor size: %u\n", sb->s_desc_size);
+ 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);
+ if (ext2fs_has_feature_bigalloc(sb))
+ fprintf(f, "Clusters per group: %u\n",
+ sb->s_clusters_per_group);
+ else
+ fprintf(f, "Fragments per group: %u\n",
+ sb->s_clusters_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",
+ 1U << 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",
+ (unsigned long long) sb->s_kbytes_written);
+ else if (sb->s_kbytes_written < POW2(23))
+ fprintf(f, "%llu MB\n", (unsigned long long)
+ (sb->s_kbytes_written + POW2(9)) >> 10);
+ else if (sb->s_kbytes_written < POW2(33))
+ fprintf(f, "%llu GB\n", (unsigned long long)
+ (sb->s_kbytes_written + POW2(19)) >> 20);
+ else if (sb->s_kbytes_written < POW2(43))
+ fprintf(f, "%llu TB\n", (unsigned long long)
+ (sb->s_kbytes_written + POW2(29)) >> 30);
+ else
+ fprintf(f, "%llu PB\n", (unsigned long long)
+ (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 (ext2fs_has_feature_dir_index(sb) ||
+ 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_backup_bgs[0] || sb->s_backup_bgs[1]) {
+ fprintf(f, "Backup block groups: ");
+ if (sb->s_backup_bgs[0])
+ fprintf(f, "%u ", sb->s_backup_bgs[0]);
+ if (sb->s_backup_bgs[1])
+ fprintf(f, "%u ", sb->s_backup_bgs[1]);
+ fputc('\n', f);
+ }
+ 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",
+ (unsigned long long) 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));
+ fprintf(f, "First error function: %.*s\n",
+ EXT2_LEN_STR(sb->s_first_error_func));
+ fprintf(f, "First error line #: %u\n",
+ sb->s_first_error_line);
+ if (sb->s_first_error_ino)
+ fprintf(f, "First error inode #: %u\n",
+ sb->s_first_error_ino);
+ if (sb->s_first_error_block)
+ fprintf(f, "First error block #: %llu\n",
+ (unsigned long long) sb->s_first_error_block);
+ if (sb->s_first_error_errcode)
+ fprintf(f, "First error err: %s\n",
+ e2p_errcode2str(sb->s_first_error_errcode));
+ }
+ if (sb->s_last_error_time) {
+ tm = sb->s_last_error_time;
+ fprintf(f, "Last error time: %s", ctime(&tm));
+ fprintf(f, "Last error function: %.*s\n",
+ EXT2_LEN_STR(sb->s_last_error_func));
+ fprintf(f, "Last error line #: %u\n",
+ sb->s_last_error_line);
+ if (sb->s_last_error_ino)
+ fprintf(f, "Last error inode #: %u\n",
+ sb->s_last_error_ino);
+ if (sb->s_last_error_block)
+ fprintf(f, "Last error block #: %llu\n",
+ (unsigned long long) sb->s_last_error_block);
+ if (sb->s_last_error_errcode)
+ fprintf(f, "Last error err: %s\n",
+ e2p_errcode2str(sb->s_last_error_errcode));
+ }
+ if (ext2fs_has_feature_mmp(sb)) {
+ fprintf(f, "MMP block number: %llu\n",
+ (unsigned long long) sb->s_mmp_block);
+ fprintf(f, "MMP update interval: %u\n",
+ sb->s_mmp_update_interval);
+ }
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ if (*quota_sb_inump(sb, qtype) != 0)
+ fprintf(f, "%-26s%u\n",
+ quota_type2prefix(qtype),
+ *quota_sb_inump(sb, qtype));
+ }
+
+ if (ext2fs_has_feature_metadata_csum(sb)) {
+ fprintf(f, "Checksum type: %s\n",
+ checksum_type(sb->s_checksum_type));
+ fprintf(f, "Checksum: 0x%08x\n",
+ sb->s_checksum);
+ }
+ if (!e2p_is_null_uuid(sb->s_encrypt_pw_salt))
+ fprintf(f, "Encryption PW Salt: %s\n",
+ e2p_uuid2str(sb->s_encrypt_pw_salt));
+
+ if (ext2fs_has_feature_csum_seed(sb))
+ fprintf(f, "Checksum seed: 0x%08x\n",
+ sb->s_checksum_seed);
+ if (ext2fs_has_feature_casefold(sb))
+ fprintf(f, "Character encoding: %s\n",
+ e2p_encoding2str(sb->s_encoding));
+ if (ext2fs_has_feature_orphan_file(sb))
+ fprintf(f, "Orphan file inode: %u\n",
+ sb->s_orphan_file_inum);
+}
+
+void list_super (struct ext2_super_block * s)
+{
+ list_super2(s, stdout);
+}
+
diff --git a/lib/e2p/mntopts.c b/lib/e2p/mntopts.c
new file mode 100644
index 0000000..7d0ae9c
--- /dev/null
+++ b/lib/e2p/mntopts.c
@@ -0,0 +1,150 @@
+/*
+ * mountopts.c --- convert between default mount options and strings
+ *
+ * Copyright (C) 2002 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <errno.h>
+
+#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_", 7))
+ return 1;
+
+ if (string[8] == 0)
+ return 1;
+ num = strtol(string+8, &eptr, 10);
+ if (num > 31 || num < 0)
+ return 1;
+ if (*eptr)
+ return 1;
+ *mask = 1 << num;
+ return 0;
+}
+
+static char *skip_over_blanks(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ while (*cp && !isspace(*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++;
+ /* fallthrough */
+ 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/e2p/ostype.c b/lib/e2p/ostype.c
new file mode 100644
index 0000000..c5fd8ab
--- /dev/null
+++ b/lib/e2p/ostype.c
@@ -0,0 +1,79 @@
+/*
+ * getostype.c - Get the Filesystem OS type
+ *
+ * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "e2p.h"
+#include <string.h>
+#include <stdlib.h>
+
+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 >= 0 && 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);
+ free(s);
+ if (i != os) {
+ fprintf(stderr, "Failure!\n");
+ exit(1);
+ }
+ }
+ exit(0);
+}
+#endif
+
+
diff --git a/lib/e2p/parse_num.c b/lib/e2p/parse_num.c
new file mode 100644
index 0000000..e8d6283
--- /dev/null
+++ b/lib/e2p/parse_num.c
@@ -0,0 +1,91 @@
+/*
+ * parse_num.c - Parse the number of blocks
+ *
+ * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "e2p.h"
+
+#include <stdlib.h>
+
+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;
+ /* fallthrough */
+ case 'G': case 'g':
+ num <<= 10;
+ /* fallthrough */
+ case 'M': case 'm':
+ num <<= 10;
+ /* fallthrough */
+ case 'K': case 'k':
+ if (log_block_size < 0)
+ num <<= 10;
+ else
+ num >>= log_block_size;
+ break;
+ case 's':
+ if (log_block_size < 0)
+ num <<= 9;
+ else
+ 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 <unistd.h>
+#include <stdio.h>
+
+main(int argc, char **argv)
+{
+ unsigned long num;
+ int log_block_size = 0;
+
+ if (argc != 2 && argc != 3) {
+ fprintf(stderr, "Usage: %s arg [log_block_size]\n", argv[0]);
+ exit(1);
+ }
+
+ if (argc == 3) {
+ char *p;
+
+ log_block_size = strtol(argv[2], &p, 0);
+ if (*p) {
+ fprintf(stderr, "Bad log_block_size: %s\n", argv[2]);
+ exit(1);
+ }
+ }
+
+ num = parse_num_blocks(argv[1], log_block_size);
+
+ printf("Parsed number: %lu\n", num);
+ exit(0);
+}
+#endif
diff --git a/lib/e2p/pe.c b/lib/e2p/pe.c
new file mode 100644
index 0000000..1f24545
--- /dev/null
+++ b/lib/e2p/pe.c
@@ -0,0 +1,40 @@
+/*
+ * pe.c - Print a second extended filesystem errors behavior
+ *
+ * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#include <stdio.h>
+
+#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/e2p/percent.c b/lib/e2p/percent.c
new file mode 100644
index 0000000..e340cd7
--- /dev/null
+++ b/lib/e2p/percent.c
@@ -0,0 +1,67 @@
+/*
+ * percent.c - Take percentage of a number
+ *
+ * Copyright (C) 2006 Theodore Ts'o <tytso@mit.edu>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "e2p.h"
+
+#include <stdlib.h>
+
+/*
+ * 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 <unistd.h>
+#include <stdio.h>
+
+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/e2p/pf.c b/lib/e2p/pf.c
new file mode 100644
index 0000000..81e3bb2
--- /dev/null
+++ b/lib/e2p/pf.c
@@ -0,0 +1,79 @@
+/*
+ * pf.c - Print file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#include <stdio.h>
+
+#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" },
+ { EXT4_ENCRYPT_FL, "E", "Encrypted" },
+ { 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" },
+ { FS_NOCOW_FL, "C", "No_COW" },
+ { FS_DAX_FL, "x", "DAX" },
+ { EXT4_CASEFOLD_FL, "F", "Casefold" },
+ { EXT4_INLINE_DATA_FL, "N", "Inline_Data" },
+ { EXT4_PROJINHERIT_FL, "P", "Project_Hierarchy" },
+ { EXT4_VERITY_FL, "V", "Verity" },
+ { EXT2_NOCOMPR_FL, "m", "Dont_Compress" },
+ { 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/e2p/project.h b/lib/e2p/project.h
new file mode 100644
index 0000000..253425a
--- /dev/null
+++ b/lib/e2p/project.h
@@ -0,0 +1,27 @@
+/*
+ * project.h
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include <ext2fs/ext2_fs.h>
+
+#if defined(__linux__) && !defined(FS_IOC_FSGETXATTR)
+#define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr)
+#define FS_IOC_FSSETXATTR _IOW('X', 32, struct fsxattr)
+
+/*
+ * Structure for FS_IOC_FSGETXATTR and FS_IOC_FSSETXATTR.
+ */
+struct fsxattr {
+ __u32 fsx_xflags; /* xflags field value (get/set) */
+ __u32 fsx_extsize; /* extsize field value (get/set)*/
+ __u32 fsx_nextents; /* nextents field value (get) */
+ __u32 fsx_projid; /* project identifier (get/set) */
+ unsigned char fsx_pad[12];
+};
+#endif
+
diff --git a/lib/e2p/ps.c b/lib/e2p/ps.c
new file mode 100644
index 0000000..757f8a6
--- /dev/null
+++ b/lib/e2p/ps.c
@@ -0,0 +1,32 @@
+/*
+ * ps.c - Print filesystem state
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#include <stdio.h>
+
+#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/e2p/setflags.c b/lib/e2p/setflags.c
new file mode 100644
index 0000000..0f6a3e0
--- /dev/null
+++ b/lib/e2p/setflags.c
@@ -0,0 +1,77 @@
+/*
+ * setflags.c - Set a file flags on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_EXT2_IOCTLS
+#include <sys/ioctl.h>
+#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
+ 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 /* ! HAVE_CHFLAGS */
+#if HAVE_EXT2_IOCTLS
+ struct stat buf;
+ 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);
+#else
+ errno = EOPNOTSUPP;
+ return -1;
+#endif /* HAVE_EXT2_IOCTLS */
+#endif /* HAVE_CHFLAGS */
+}
diff --git a/lib/e2p/setversion.c b/lib/e2p/setversion.c
new file mode 100644
index 0000000..dd4a3f0
--- /dev/null
+++ b/lib/e2p/setversion.c
@@ -0,0 +1,40 @@
+/*
+ * setversion.c - Set a file version on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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 "config.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#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 */
+ errno = EOPNOTSUPP;
+ return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
diff --git a/lib/e2p/uuid.c b/lib/e2p/uuid.c
new file mode 100644
index 0000000..a102092
--- /dev/null
+++ b/lib/e2p/uuid.c
@@ -0,0 +1,85 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <ext2fs/ext2_types.h>
+
+#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 "<none>";
+ e2p_uuid_to_str(uu, buf);
+ return buf;
+}
+
diff --git a/lib/et/Android.bp b/lib/et/Android.bp
new file mode 100644
index 0000000..565feb5
--- /dev/null
+++ b/lib/et/Android.bp
@@ -0,0 +1,40 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-0BSD
+ // SPDX-license-identifier-BSD
+ // SPDX-license-identifier-GPL-2.0
+ // SPDX-license-identifier-MIT
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library {
+ name: "libext2_com_err",
+ host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "error_message.c",
+ "et_name.c",
+ "init_et.c",
+ "com_err.c",
+ "com_right.c",
+ ],
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
diff --git a/lib/et/Makefile.in b/lib/et/Makefile.in
new file mode 100644
index 0000000..d411f23
--- /dev/null
+++ b/lib/et/Makefile.in
@@ -0,0 +1,185 @@
+#
+# Makefile for lib/et
+#
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/et
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+
+DEP_MAKEFILE = $(DEP_LIB_MAKEFILES)
+
+@MCONFIG@
+
+all:: compile_et com_err.pc
+
+OBJS= error_message.o et_name.o init_et.o com_err.o com_right.o
+SRCS = $(srcdir)/error_message.c $(srcdir)/et_name.c $(srcdir)/init_et.c \
+ $(srcdir)/com_err.c $(srcdir)/com_right.c
+
+HFILES= com_err.h
+SHARE_FILES= et_c.awk et_h.awk
+
+LIBRARY= libcom_err
+LIBDIR= et
+
+ELF_VERSION = 2.1
+ELF_SO_VERSION = 2
+ELF_IMAGE = libcom_err
+ELF_MYDIR = et
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS = @SEM_INIT_LIB@
+
+BSDLIB_VERSION = 1.1
+BSDLIB_IMAGE = libcom_err
+BSDLIB_MYDIR = et
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+#
+# what to build...
+#
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+
+compile_et: $(DEP_SUBSTITUTE) $(srcdir)/compile_et.sh.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE) $(srcdir)/compile_et.sh.in compile_et
+ $(Q) $(CHMOD) +x compile_et
+
+DVI=texi2dvi
+DVIPS=dvips -o "$@"
+INFO=@MAKEINFO@
+HTML=makeinfo --html --no-split
+PS2PDF=ps2pdf
+
+com_err.ps : com_err.dvi
+com_err.dvi: com_err.texinfo
+
+com_err.info: $(srcdir)/com_err.texinfo
+ $(E) " MAKEINFO $@"
+ -$(Q) $(INFO) $(srcdir)/com_err.texinfo
+
+com_err.dvi: $(srcdir)/com_err.texinfo
+ $(E) " TEXI2DVI $@"
+ -$(Q) $(DVI) $(srcdir)/com_err.texinfo
+
+com_err.ps: com_err.dvi
+ $(E) " DVIPS $@"
+ -$(Q) $(DVIPS) com_err.dvi
+
+com_err.pdf: com_err.ps
+ $(E) " PS2PDF $@"
+ -$(Q) $(PS2PDF) com_err.ps
+
+com_err.html: $(srcdir)/com_err.texinfo
+ $(E) " MAKEINFO $@"
+ -$(Q) $(HTML) $(srcdir)/com_err.texinfo
+
+com_err.pc: $(srcdir)/com_err.pc.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); CONFIG_FILES=lib/et/com_err.pc ./config.status
+
+#libcom_err.o: $(LIBOBJS)
+# $(LD) -r -s -o libcom_err.o $(LIBOBJS)
+# chmod -x libcom_err.o
+
+TAGS: $(SRCS)
+ $(TAGS) $(SRCS)
+
+installdirs::
+ $(E) " MKDIR_P $(libdir) $(includedir)/et $(datadir)/et $(bindir) $(man1dir) $(man3dir)"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(includedir)/et $(DESTDIR)$(datadir)/et \
+ $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) \
+ $(DESTDIR)$(man3dir) $(DESTDIR)$(pkgconfigdir)
+
+install:: compile_et libcom_err.a $(HFILES) installdirs com_err.pc
+ $(E) " INSTALL_DATA $(libdir)/libcom_err.a"
+ $(Q) $(INSTALL_DATA) libcom_err.a $(DESTDIR)$(libdir)/libcom_err.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libcom_err.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libcom_err.a
+ $(Q) for i in $(HFILES); do \
+ echo " INSTALL_DATA $(includedir)/et/$$i"; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/et/$$i; \
+ done
+ $(Q) (cd $(DESTDIR)$(includedir) ;\
+ $(LN) $(LINK_INSTALL_FLAGS) et/com_err.h . )
+ $(Q) for i in $(SHARE_FILES); do \
+ echo " INSTALL_DATA $(datadir)/et/$$i"; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(datadir)/et/$$i; \
+ done
+ $(E) " INSTALL_SCRIPT $(bindir)/compile_et"
+ $(Q) $(INSTALL_SCRIPT) compile_et $(DESTDIR)$(bindir)/compile_et
+ $(E) " INSTALL_DATA $(man3dir)/com_err.3"
+ $(Q) $(INSTALL_DATA) $(srcdir)/com_err.3 $(DESTDIR)$(man3dir)/com_err.3
+ $(E) " INSTALL_DATA $(man1dir)/compile_et.1"
+ $(Q) $(INSTALL_DATA) $(srcdir)/compile_et.1 \
+ $(DESTDIR)$(man1dir)/compile_et.1
+ $(E) " INSTALL_DATA $(pkgconfigdir)/com_err.pc"
+ $(Q) $(INSTALL_DATA) com_err.pc $(DESTDIR)$(pkgconfigdir)/com_err.pc
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/libcom_err.a \
+ $(DESTDIR)$(bindir)/compile_et \
+ $(DESTDIR)$(pkgconfigdir)/com_err.pc
+ $(RM) -rf $(DESTDIR)$(includedir)/et $(DESTDIR)$(datadir)/et
+
+fullcheck check:: compile_et
+ for i in $(srcdir)/test_cases/*.et ; do \
+ t=`basename $$i | sed -e 's/.et//'`; \
+ _ET_DIR_OVERRIDE=$(srcdir) ./compile_et $$i ; \
+ diff -c $(srcdir)/test_cases/$$t.c $$t.c > $$t.failed; \
+ if [ $$? -ne 0 ]; then echo Test case $$t failed; exit 1 ; fi ; \
+ diff -c $(srcdir)/test_cases/$$t.h $$t.h >> $$t.failed; \
+ if [ $$? -ne 0 ]; then echo Test case $$t failed; exit 1 ; fi ; \
+ $(RM) -f $$t.c $$t.h $$t.failed; \
+ echo "Test case $$t succeeded" ; \
+ done
+
+clean::
+ $(RM) -f compile_et libcom_err.a libcom_err_p.a com_err.info
+ $(RM) -f $(OBJS) profiled/*
+ $(RM) -f *~ \#* *.bak *.otl *.aux *.toc *.PS *.dvi *.ps TAGS *.ln \
+ *.html *.cp *.fn *.fns *.ky *.log *.pc *.pg *.toc *.tp *.vr \
+ *.pdf
+ $(RM) -f ../libcom_err.a ../libcom_err_p.a
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend Makefile com_err.pc \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+$(OBJS): subdirs
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+error_message.o: $(srcdir)/error_message.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/com_err.h $(srcdir)/error_table.h \
+ $(srcdir)/internal.h
+et_name.o: $(srcdir)/et_name.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/com_err.h $(srcdir)/error_table.h \
+ $(srcdir)/internal.h
+init_et.o: $(srcdir)/init_et.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/com_err.h $(srcdir)/error_table.h
+com_err.o: $(srcdir)/com_err.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/com_err.h $(srcdir)/error_table.h \
+ $(srcdir)/internal.h
+com_right.o: $(srcdir)/com_right.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/com_err.h $(srcdir)/error_table.h
diff --git a/lib/et/com_err.3 b/lib/et/com_err.3
new file mode 100644
index 0000000..5a25f09
--- /dev/null
+++ b/lib/et/com_err.3
@@ -0,0 +1,87 @@
+.\" Copyright (c) 1988 Massachusetts Institute of Technology,
+.\" Student Information Processing Board.
+.\"
+.TH COM_ERR 3 "22 Nov 1988" SIPB
+.SH NAME
+com_err \- common error display routine
+.SH SYNOPSIS
+.nf
+ #include <et/com_err.h>
+void (*\fIproc\fR) (const char *, long, const char *, va_list);
+.PP
+void com_err (const char *whoami, long code, const char *format, ...);
+.PP
+proc = set_com_err_hook (proc);
+.PP
+proc = reset_com_err_hook ();
+.PP
+void initialize_XXXX_error_table ();
+.fi
+.SH DESCRIPTION
+.I Com_err
+displays an error message on the standard error stream
+.I stderr
+(see
+.IR stdio (3S))
+composed of the
+.I whoami
+string, which should specify the program name or some subportion of
+a program, followed by an error message generated from the
+.I code
+value (derived from
+.IR compile_et (1)),
+and a string produced using the
+.I format
+string and any following arguments, in the same style as
+.IR fprintf (3).
+
+The behavior of
+.I com_err
+can be modified using
+.I set_com_err_hook;
+this defines a procedure which is called with the arguments passed to
+.I com_err,
+instead of the default internal procedure which sends the formatted
+text to error output. Thus the error messages from a program can all
+easily be diverted to another form of diagnostic logging, such as
+.IR syslog (3).
+.I Reset_com_err_hook
+may be used to restore the behavior of
+.I com_err
+to its default form. Both procedures return the previous ``hook''
+value. These ``hook'' procedures must have the declaration given for
+.I proc
+above in the synopsis.
+
+The
+.I initialize_XXXX_error_table
+routine is generated mechanically by
+.IR compile_et (1)
+from a source file containing names and associated strings. Each
+table has a name of up to four characters, which is used in place of
+the
+.B XXXX
+in the name of the routine. These routines should be called before
+any of the corresponding error codes are used, so that the
+.I com_err
+library will recognize error codes from these tables when they are
+used.
+
+The
+.B com_err.h
+header file should be included in any source file that uses routines
+from the
+.I com_err
+library; executable files must be linked using
+.I ``-lcom_err''
+in order to cause the
+.I com_err
+library to be included.
+
+.\" .IR for manual entries
+.\" .PP for paragraph breaks
+
+.SH "SEE ALSO"
+compile_et (1), syslog (3).
+
+Ken Raeburn, "A Common Error Description Library for UNIX".
diff --git a/lib/et/com_err.c b/lib/et/com_err.c
new file mode 100644
index 0000000..7294c6d
--- /dev/null
+++ b/lib/et/com_err.c
@@ -0,0 +1,105 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "com_err.h"
+#include "error_table.h"
+#include "internal.h"
+
+static void
+default_com_err_proc (const char *whoami, errcode_t code, const
+ char *fmt, va_list args)
+ COM_ERR_ATTR((format(printf, 3, 0)));
+
+static void
+default_com_err_proc (const char *whoami, errcode_t code, const
+ char *fmt, va_list args)
+{
+ int do_cr = 1, fd = fileno(stderr);
+
+ if (whoami) {
+ fputs(whoami, stderr);
+ fputs(": ", stderr);
+ }
+ if (code) {
+ fputs(error_message(code), stderr);
+ fputs(" ", stderr);
+ }
+ if (fmt) {
+ vfprintf (stderr, fmt, args);
+ }
+ if (!isatty(fd))
+ do_cr = 0;
+#ifdef HAVE_TERMIOS_H
+ else {
+ struct termios t;
+
+ if ((tcgetattr(fd, &t)) == 0 &&
+ (t.c_oflag & OPOST) && (t.c_oflag & ONLCR))
+ do_cr = 0;
+ }
+#endif
+ if (do_cr)
+ fputc('\r', stderr);
+ fputc('\n', stderr);
+ fflush(stderr);
+}
+
+typedef void (*errf) (const char *, errcode_t, const char *, va_list)
+ COM_ERR_ATTR((format(printf, 3, 0)));
+
+errf com_err_hook = default_com_err_proc;
+
+void com_err_va (const char *whoami, errcode_t code, const char *fmt,
+ va_list args)
+{
+ (*com_err_hook) (whoami, code, fmt, args);
+}
+
+void com_err (const char *whoami,
+ errcode_t code,
+ const char *fmt, ...)
+{
+ va_list pvar;
+
+ if (!com_err_hook)
+ com_err_hook = default_com_err_proc;
+ va_start(pvar, fmt);
+ com_err_va (whoami, code, fmt, pvar);
+ va_end(pvar);
+}
+
+errf set_com_err_hook(errf new_proc)
+{
+ errf x = com_err_hook;
+
+ if (new_proc)
+ com_err_hook = new_proc;
+ else
+ com_err_hook = default_com_err_proc;
+
+ return x;
+}
+
+errf reset_com_err_hook(void) {
+ errf x = com_err_hook;
+ com_err_hook = default_com_err_proc;
+ return x;
+}
diff --git a/lib/et/com_err.h b/lib/et/com_err.h
new file mode 100644
index 0000000..27a36ea
--- /dev/null
+++ b/lib/et/com_err.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+
+#include <stddef.h>
+#include <stdarg.h>
+
+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 char *(*set_com_err_gettext (char *(*) (const char *)))
+ (const char *);
+
+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/et/com_err.pc.in b/lib/et/com_err.pc.in
new file mode 100644
index 0000000..86df8a2
--- /dev/null
+++ b/lib/et/com_err.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: com_err
+Description: Common error description library
+Version: @E2FSPROGS_VERSION@
+Requires:
+Cflags: -I${includedir}/et -I${includedir}
+Libs: -L${libdir} -lcom_err
+Libs.private: @SEM_INIT_LIB@
diff --git a/lib/et/com_err.texinfo b/lib/et/com_err.texinfo
new file mode 100644
index 0000000..3e7ae32
--- /dev/null
+++ b/lib/et/com_err.texinfo
@@ -0,0 +1,532 @@
+\input texinfo @c -*-texinfo-*-
+
+@c $Header$
+@c $Source$
+@c $Locker$
+
+@c Note that although this source file is in texinfo format (more
+@c or less), it is not yet suitable for turning into an ``info''
+@c file. Sorry, maybe next time.
+@c
+@c In order to produce hardcopy documentation from a texinfo file,
+@c run ``tex com_err.texinfo'' which will load in texinfo.tex,
+@c provided in this distribution. (texinfo.tex is from the Free
+@c Software Foundation, and is under different copyright restrictions
+@c from the rest of this package.)
+
+@setfilename com_err.info
+@settitle A Common Error Description Library for UNIX
+
+@ifinfo
+@dircategory Development
+@direntry
+* Com_err: (com_err). A Common Error Description Library for UNIX.
+@end direntry
+@end ifinfo
+
+@c smallbook
+
+@iftex
+@finalout
+@end iftex
+
+@ifinfo
+This file documents the use of the Common Error Description library.
+
+Copyright (C) 1987, 1988 Student Information Processing Board of the
+Massachusetts Institute of Technology.
+
+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, and 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.
+
+Note that the file texinfo.tex, provided with this distribution, is from
+the Free Software Foundation, and is under different copyright restrictions
+from the remainder of this package.
+
+@ignore
+Permission is granted to process this file through Tex and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+(this paragraph not being relevant to the printed manual).
+
+@end ignore
+@end ifinfo
+
+@setchapternewpage odd
+
+@titlepage
+@center @titlefont{A Common Error Description}
+@center @titlefont{Library for UNIX}
+@sp 2
+@center Ken Raeburn
+@center Bill Sommerfeld
+@sp 1
+@center MIT Student Information Processing Board
+@sp 3
+@center last updated 1 January 1989
+@center for version 1.2
+@center ***DRAFT COPY ONLY***
+
+@vskip 2in
+
+@center @b{Abstract}
+
+UNIX has always had a clean and simple system call interface, with a
+standard set of error codes passed between the kernel and user
+programs. Unfortunately, the same cannot be said of many of the
+libraries layered on top of the primitives provided by the kernel.
+Typically, each one has used a different style of indicating errors to
+their callers, leading to a total hodgepodge of error handling, and
+considerable amounts of work for the programmer. This paper describes
+a library and associated utilities which allows a more uniform way for
+libraries to return errors to their callers, and for programs to
+describe errors and exceptional conditions to their users.
+
+@page
+@vskip 0pt plus 1filll
+
+Copyright @copyright{} 1987, 1988 by the Student Information Processing
+Board of the Massachusetts Institute of Technology.
+
+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, and 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.
+
+Note that the file texinfo.tex, provided with this distribution, is from
+the Free Software Foundation, and is under different copyright restrictions
+from the remainder of this package.
+
+@end titlepage
+
+@node Top, Why com_err?, (dir), (dir)
+
+@top A Common Error Description Library for UNIX
+
+This manual documents the com_err library.
+
+@menu
+* Why com_err?::
+* Error codes::
+* Error table source file::
+* The error-table compiler::
+* Run-time support routines::
+* Coding Conventions::
+* Building and Installation::
+* Bug Reports::
+* Acknowledgements::
+@end menu
+
+@page
+
+@node Why com_err?, Error codes, Top, Top
+@chapter Why com_err?
+
+In building application software packages, a programmer often has to
+deal with a number of libraries, each of which can use a different
+error-reporting mechanism. Sometimes one of two values is returned,
+indicating simply SUCCESS or FAILURE, with no description of errors
+encountered. Sometimes it is an index into a table of text strings,
+where the name of the table used is dependent on the library being
+used when the error is generated; since each table starts numbering at
+0 or 1, additional information as to the source of the error code is
+needed to determine which table to look at. Sometimes no text messages are
+supplied at all, and the programmer must supply them at any point at which
+he may wish to report error conditions.
+Often, a global variable is assigned some value describing the error, but
+the programmer has to know in each case whether to look at @code{errno},
+@code{h_errno}, the return value from @code{hes_err()}, or whatever other
+variables or routines are specified.
+And what happens if something
+in the procedure of
+examining or reporting the error changes the same variable?
+
+The package we have developed is an attempt to present a common
+error-handling mechanism to manipulate the most common form of error code
+in a fashion that does not have the problems listed above.
+
+A list of up to 256 text messages is supplied to a translator we have
+written, along with the three- to four-character ``name'' of the error
+table. The library using this error table need only call a routine
+generated from this error-table source to make the table ``known'' to the
+com_err library, and any error code the library generates can be converted
+to the corresponding error message. There is also a default format for
+error codes accidentally returned before making the table known, which is
+of the form @samp{unknown code foo 32}, where @samp{foo} would be the name
+of the table.
+
+@node Error codes, Error table source file, Why com_err?, Top
+@chapter Error codes
+
+Error codes themselves are 32 bit (signed) integers, of which the high
+order 24 bits are an identifier of which error table the error code is
+from, and the low order 8 bits are a sequential error number within
+the table. An error code may thus be easily decomposed into its component
+parts. Only the lowest 32 bits of an error code are considered significant
+on systems which support wider values.
+
+Error table 0 is defined to match the UNIX system call error table
+(@code{sys_errlist}); this allows @code{errno} values to be used directly
+in the library (assuming that @code{errno} is of a type with the same width
+as @t{long}). Other error table numbers are formed by compacting together
+the first four characters of the error table name. The mapping between
+characters in the name and numeric values in the error code are defined in
+a system-independent fashion, so that two systems that can pass integral
+values between them can reliably pass error codes without loss of meaning;
+this should work even if the character sets used are not the same.
+(However, if this is to be done, error table 0 should be avoided, since the
+local system call error tables may differ.)
+
+Any variable which is to contain an error code should be declared @t{long}.
+The draft proposed American National Standard for C (as of May, 1988)
+requires that @t{long} variables be at least 32 bits; any system which does
+not support 32-bit @t{long} values cannot make use of this package (nor
+much other software that assumes an ANSI-C environment base) without
+significant effort.
+
+@node Error table source file, The error-table compiler, Error codes, Top
+@chapter Error table source file
+
+The error table source file begins with the declaration of the table name,
+as
+
+@example
+error_table @var{tablename}
+@end example
+
+Individual error codes are
+specified with
+
+@example
+error_code @var{ERROR_NAME}, @var{"text message"}
+@end example
+
+where @samp{ec} can also be used as a short form of @samp{error_code}. To
+indicate the end of the table, use @samp{end}. Thus, a (short) sample
+error table might be:
+
+@example
+
+ error_table dsc
+
+ error_code DSC_DUP_MTG_NAME,
+ "Meeting already exists"
+
+ ec DSC_BAD_PATH,
+ "A bad meeting pathname was given"
+
+ ec DSC_BAD_MODES,
+ "Invalid mode for this access control list"
+
+ end
+
+@end example
+
+@node The error-table compiler, Run-time support routines, Error table source file, Top
+@chapter The error-table compiler
+
+The error table compiler is named @code{compile_et}. It takes one
+argument, the pathname of a file (ending in @samp{.et}, e.g.,
+@samp{dsc_err.et}) containing an error table source file. It parses the
+error table, and generates two output files -- a C header file
+(@samp{discuss_err.h}) which contains definitions of the numerical values
+of the error codes defined in the error table, and a C source file which
+should be compiled and linked with the executable. The header file must be
+included in the source of a module which wishes to reference the error
+codes defined; the object module generated from the C code may be linked in
+to a program which wishes to use the printed forms of the error codes.
+
+@node Run-time support routines, Coding Conventions, The error-table compiler, Top
+@chapter Run-time support routines
+
+Any source file which uses the routines supplied with or produced by the
+com_err package should include the header file @file{<com_err.h>}. It
+contains declarations and definitions which may be needed on some systems.
+(Some functions cannot be referenced properly without the return type
+declarations in this file. Some functions may work properly on most
+architectures even without the header file, but relying on this is not
+recommended.)
+
+The run-time support routines and variables provided via this package
+include the following:
+
+@example
+void initialize_@var{xxxx}_error_table (void);
+@end example
+
+One of these routines is built by the error compiler for each error table.
+It makes the @var{xxxx} error table ``known'' to the error reporting
+system. By convention, this routine should be called in the initialization
+routine of the @var{xxxx} library. If the library has no initialization
+routine, some combination of routines which form the core of the library
+should ensure that this routine is called. It is not advised to leave it
+the caller to make this call.
+
+There is no harm in calling this routine more than once.
+
+@example
+#define ERROR_TABLE_BASE_@var{xxxx} @var{nnnnn}L
+@end example
+
+This symbol contains the value of the first error code entry in the
+specified table.
+This rarely needs be used by the
+programmer.
+
+@deftypefun const char *error_message (long @var{code});
+
+This routine returns the character string error message associated
+with @code{code}; if this is associated with an unknown error table, or
+if the code is associated with a known error table but the code is not
+in the table, a string of the form @samp{Unknown code @var{xxxx nn}} is
+returned, where @var{xxxx} is the error table name produced by
+reversing the compaction performed on the error table number implied
+by that error code, and @var{nn} is the offset from that base value.
+
+Although this routine is available for use when needed, its use should be
+left to circumstances which render @code{com_err} (below) unusable.
+
+@end deftypefun
+
+@deftypefun void com_err (const char *@var{whoami}, long @var{error_code}, const char *@var{format}, ...);
+
+This routine provides an alternate way to print error messages to
+standard error; it allows the error message to be passed in as a
+parameter, rather than in an external variable. @emph{Provide grammatical
+context for ``message.''}
+
+The module reporting the error should be passed in via @var{whoami}.
+If @var{format} is @code{(char *)NULL}, the formatted message will not be
+printed. @var{format} may not be omitted.
+
+@end deftypefun
+
+@deftypefun void com_err_va (const char *@var{whoami}, long @var{error_code}, const char *@var{format}, va_list @var{args});
+
+This routine provides an interface, equivalent to @code{com_err} above,
+which may be used by higher-level variadic functions (functions which
+accept variable numbers of arguments).
+
+@end deftypefun
+
+@deftypefun void *set_com_err_hook (void (*@var{proc}) (const char *@var{whoami}, long @var{error_code}, va_list @var{args}) (const char *@var{whoami}, long @var{error_code}, va_list @var{args}));
+
+@deftypefunx void reset_com_err_hook ();
+
+These two routines allow a routine to be dynamically substituted for
+@samp{com_err}. After @samp{set_com_err_hook} has been called,
+calls to @samp{com_err} will turn into calls to the new hook routine.
+@samp{reset_com_err_hook} turns off this hook. This may intended to
+be used in daemons (to use a routine which calls @cite{syslog(3)}), or
+in a window system application (which could pop up a dialogue box).
+
+If a program is to be used in an environment in which simply printing
+messages to the @code{stderr} stream would be inappropriate (such as in a
+daemon program which runs without a terminal attached),
+@code{set_com_err_hook} may be used to redirect output from @code{com_err}.
+The following is an example of an error handler which uses @cite{syslog(3)}
+as supplied in BSD 4.3:
+
+@example
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+/* extern openlog (const char * name, int logopt, int facility); */
+/* extern syslog (int priority, char * message, ...); */
+
+void hook (const char * whoami, long code,
+ const char * format, va_list args)
+@{
+ char buffer[BUFSIZ];
+ static int initialized = 0;
+ if (!initialized) @{
+ openlog (whoami,
+ LOG_NOWAIT|LOG_CONS|LOG_PID|LOG_NDELAY,
+ LOG_DAEMON);
+ initialized = 1;
+ @}
+ vsprintf (buffer, format, args);
+ syslog (LOG_ERR, "%s %s", error_message (code), buffer);
+@}
+@end example
+
+After making the call
+@code{set_com_err_hook (hook);},
+any calls to @code{com_err} will result in messages being sent to the
+@var{syslogd} daemon for logging.
+The name of the program, @samp{whoami}, is supplied to the
+@samp{openlog()} call, and the message is formatted into a buffer and
+passed to @code{syslog}.
+
+Note that since the extra arguments to @code{com_err} are passed by
+reference via the @code{va_list} value @code{args}, the hook routine may
+place any form of interpretation on them, including ignoring them. For
+consistency, @code{printf}-style interpretation is suggested, via
+@code{vsprintf} (or @code{_doprnt} on BSD systems without full support for
+the ANSI C library).
+
+@end deftypefun
+
+@node Coding Conventions, Building and Installation, Run-time support routines, Top
+@chapter Coding Conventions
+
+The following conventions are just some general stylistic conventions
+to follow when writing robust libraries and programs. Conventions
+similar to this are generally followed inside the UNIX kernel and most
+routines in the Multics operating system. In general, a routine
+either succeeds (returning a zero error code, and doing some side
+effects in the process), or it fails, doing minimal side effects; in
+any event, any invariant which the library assumes must be maintained.
+
+In general, it is not in the domain of non user-interface library
+routines to write error messages to the user's terminal, or halt the
+process. Such forms of ``error handling'' should be reserved for
+failures of internal invariants and consistency checks only, as it
+provides the user of the library no way to clean up for himself in the
+event of total failure.
+
+Library routines which can fail should be set up to return an error
+code. This should usually be done as the return value of the
+function; if this is not acceptable, the routine should return a
+``null'' value, and put the error code into a parameter passed by
+reference.
+
+Routines which use the first style of interface can be used from
+user-interface levels of a program as follows:
+
+@example
+@{
+ if ((code = initialize_world(getuid(), random())) != 0) @{
+ com_err("demo", code,
+ "when trying to initialize world");
+ exit(1);
+ @}
+ if ((database = open_database("my_secrets", &code))==NULL) @{
+ com_err("demo", code,
+ "while opening my_secrets");
+ exit(1);
+ @}
+@}
+@end example
+
+A caller which fails to check the return status is in error. It is
+possible to look for code which ignores error returns by using lint;
+look for error messages of the form ``foobar returns value which is
+sometimes ignored'' or ``foobar returns value which is always
+ignored.''
+
+Since libraries may be built out of other libraries, it is often necessary
+for the success of one routine to depend on another. When a lower level
+routine returns an error code, the middle level routine has a few possible
+options. It can simply return the error code to its caller after doing
+some form of cleanup, it can substitute one of its own, or it can take
+corrective action of its own and continue normally. For instance, a
+library routine which makes a ``connect'' system call to make a network
+connection may reflect the system error code @code{ECONNREFUSED}
+(Connection refused) to its caller, or it may return a ``server not
+available, try again later,'' or it may try a different server.
+
+Cleanup which is typically necessary may include, but not be limited
+to, freeing allocated memory which will not be needed any more,
+unlocking concurrency locks, dropping reference counts, closing file
+descriptors, or otherwise undoing anything which the procedure did up
+to this point. When there are a lot of things which can go wrong, it
+is generally good to write one block of error-handling code which is
+branched to, using a goto, in the event of failure. A common source
+of errors in UNIX programs is failing to close file descriptors on
+error returns; this leaves a number of ``zombied'' file descriptors
+open, which eventually causes the process to run out of file
+descriptors and fall over.
+
+@example
+@{
+ FILE *f1=NULL, *f2=NULL, *f3=NULL;
+ int status = 0;
+
+ if ( (f1 = fopen(FILE1, "r")) == NULL) @{
+ status = errno;
+ goto error;
+ @}
+
+ /*
+ * Crunch for a while
+ */
+
+ if ( (f2 = fopen(FILE2, "w")) == NULL) @{
+ status = errno;
+ goto error;
+ @}
+
+ if ( (f3 = fopen(FILE3, "a+")) == NULL) @{
+ status = errno;
+ goto error;
+ @}
+
+ /*
+ * Do more processing.
+ */
+ fclose(f1);
+ fclose(f2);
+ fclose(f3);
+ return 0;
+
+error:
+ if (f1) fclose(f1);
+ if (f2) fclose(f2);
+ if (f3) fclose(f3);
+ return status;
+@}
+@end example
+
+@node Building and Installation, Bug Reports, Coding Conventions, Top
+@chapter Building and Installation
+
+The distribution of this package will probably be done as a compressed
+``tar''-format file available via anonymous FTP from SIPB.MIT.EDU.
+Retrieve @samp{pub/com_err.tar.Z} and extract the contents. A subdirectory
+@t{profiled} should be created to hold objects compiled for profiling.
+Running ``make all'' should then be sufficient to build the library and
+error-table compiler. The files @samp{libcom_err.a},
+@samp{libcom_err_p.a}, @samp{com_err.h}, and @samp{compile_et} should be
+installed for use; @samp{com_err.3} and @samp{compile_et.1} can also be
+installed as manual pages.
+
+@node Bug Reports, Acknowledgements, Building and Installation, Top
+@chapter Bug Reports
+
+The principal author of this library is: Ken
+Raeburn, @t{raeburn@@MIT.EDU}.
+
+This version of the com_err library is being maintained by Theodore
+Ts'o, and so bugs and comments should be sent to @t{tytso@@thunk.org}.
+
+
+@node Acknowledgements, , Bug Reports, Top
+@chapter Acknowledgements
+
+I would like to thank: Bill Sommerfeld, for his help with some of this
+documentation, and catching some of the bugs the first time around;
+Honeywell Information Systems, for not killing off the @emph{Multics}
+operating system before I had an opportunity to use it; Honeywell's
+customers, who persuaded them not to do so, for a while; Ted Anderson of
+CMU, for catching some problems before version 1.2 left the nest; Stan
+Zanarotti and several others of MIT's Student Information Processing Board,
+for getting us started with ``discuss,'' for which this package was
+originally written; and everyone I've talked into --- I mean, asked to read
+this document and the ``man'' pages.
+
+@contents
+@bye
diff --git a/lib/et/com_right.c b/lib/et/com_right.c
new file mode 100644
index 0000000..a4ed535
--- /dev/null
+++ b/lib/et/com_right.c
@@ -0,0 +1,120 @@
+/*
+ * com_right.c -- provide Heimdall / Kerberos4kth com_err interfaces
+ * for backwards compatibility
+ *
+ * Copyright (c) 2003 by Theodore Ts'o
+ *
+ * Taken from lib/com_err/error.c from Kerberos4kth distribution.
+ *
+ * Copyright (c) 1997, 1998, 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "com_err.h"
+#include "error_table.h"
+
+const char *
+com_right(struct et_list *list, long code)
+{
+ struct et_list *p;
+ for (p = list; p; p = p->next) {
+ if (code >= p->table->base && code < p->table->base + p->table->n_msgs)
+ return p->table->msgs[code - p->table->base];
+ }
+ return NULL;
+}
+
+const char *
+com_right_r(struct et_list *list, long code, char *str, size_t len)
+{
+ struct et_list *p;
+ for (p = list; p; p = p->next) {
+ if ((code >= p->table->base) &&
+ (code < p->table->base + p->table->n_msgs)) {
+ strncpy(str, p->table->msgs[code - p->table->base], len);
+ str[len-1] = '\0';
+ return str;
+ }
+ }
+ return NULL;
+}
+
+struct foobar {
+ struct et_list etl;
+ struct error_table tab;
+};
+
+/*
+ * We provide this routine for compatibility with Heimdall generated
+ * foo_err.c files, but we don't use this ourselves for foo_err.c
+ * files generated by our compile_et. This is so our foo_err.c
+ * files can be used with older com_err libraries without running
+ * afoul of dependencies.
+ */
+void
+initialize_error_table_r(struct et_list **list,
+ const char **messages,
+ int num_errors,
+ long base)
+{
+ struct et_list *et, **end;
+ struct error_table *tab;
+ struct foobar *f;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == messages)
+ return;
+ f = malloc(sizeof(*f));
+ if (f == NULL)
+ return;
+ et = &f->etl;
+ et->table = tab = &f->tab;
+ tab->msgs = messages;
+ tab->n_msgs = num_errors;
+ tab->base = base;
+ et->next = NULL;
+ *end = et;
+}
+
+
+void
+free_error_table(struct et_list *et)
+{
+ while(et){
+ struct et_list *p = et;
+ et = et->next;
+ free(p);
+ }
+}
diff --git a/lib/et/compile_et.1 b/lib/et/compile_et.1
new file mode 100644
index 0000000..7219441
--- /dev/null
+++ b/lib/et/compile_et.1
@@ -0,0 +1,82 @@
+.\" Copyright (c) 1988 Massachusetts Institute of Technology,
+.\" Student Information Processing Board. All rights reserved.
+.\"
+.\" $Header$
+.\"
+.TH COMPILE_ET 1 "30 Mar 1998" SIPB
+.SH NAME
+compile_et \- error table compiler
+.SH SYNOPSIS
+.B compile_et
+file
+.SH DESCRIPTION
+.B Compile_et
+converts a table listing error-code names and associated messages into
+a C source file suitable for use with the
+.IR com_err (3)
+library.
+
+The source file name must end with a suffix of ``.et''; the file
+consists of a declaration supplying the name (up to four characters
+long) of the error-code table:
+
+.B error_table
+.I name
+
+followed by up to 256 entries of the form:
+
+.B error_code
+.I name,
+"
+.I string
+"
+
+and a final
+
+.B end
+
+to indicate the end of the table.
+
+The name of the table is used to construct the name of a subroutine
+.I initialize_XXXX_error_table
+which must be called in order for the
+.I com_err
+library to recognize the error table.
+
+The various error codes defined are assigned sequentially increasing
+numbers (starting with a large number computed as a hash function of
+the name of the table); thus for compatibility it is suggested that
+new codes be added only to the end of an existing table, and that no
+codes be removed from tables.
+
+The names defined in the table are placed into a C header file with
+preprocessor directives defining them as integer constants of up to
+32 bits in magnitude.
+
+A C source file is also generated which should be compiled and linked
+with the object files which reference these error codes; it contains
+the text of the messages and the initialization subroutine. Both C
+files have names derived from that of the original source file, with
+the ``.et'' suffix replaced by ``.c'' and ``.h''.
+
+A ``#'' in the source file is treated as a comment character, and all
+remaining text to the end of the source line will be ignored.
+
+.SH BUGS
+
+Since the original
+.B compile_et
+uses a very simple parser based on
+.IR yacc (1),
+and this current version of
+.B compile_et
+uses an awk/sed combination of scripts,
+its error recovery leaves much to be desired.
+
+.\" .IR for manual entries
+.\" .PP for paragraph breaks
+
+.SH "SEE ALSO"
+com_err (3).
+
+Ken Raeburn, "A Common Error Description Library for UNIX".
diff --git a/lib/et/compile_et.sh.in b/lib/et/compile_et.sh.in
new file mode 100644
index 0000000..3cba7c7
--- /dev/null
+++ b/lib/et/compile_et.sh.in
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+#
+
+AWK=@AWK@
+DIR=@datadir@/et
+
+if test "$1" = "--build-tree" ; then
+ shift;
+ DIR="$ET_DIR"
+fi
+
+if test "x$1" = x ; then
+ echo "Usage: compile_et file"
+ exit 1
+fi
+
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+if test -n "$_ET_DIR_OVERRIDE" ; then
+ DIR="$_ET_DIR_OVERRIDE";
+fi
+
+if test ! -f "$DIR/et_h.awk" || test ! -f "$DIR/et_c.awk" ; then
+ echo "compile_et: Couldn't find compile_et's template files."
+ exit 1
+fi
+
+ROOT=`echo $1 | sed -e s/.et$//`
+BASE=`basename $ROOT`
+
+if test ! -f "$ROOT.et" ; then
+ echo "compile_et: $ROOT.et: File not found"
+ exit 1;
+fi
+
+$AWK -f "${DIR}/et_h.awk" "outfile=${BASE}.h.$$" "outfn=${BASE}.h" "$ROOT.et"
+if test -f ${BASE}.h && cmp -s ${BASE}.h.$$ ${BASE}.h ; then
+ rm -f ${BASE}.h.$$
+else
+ mv -f ${BASE}.h.$$ ${BASE}.h
+ chmod a-w ${BASE}.h
+fi
+$AWK -f "${DIR}/et_c.awk" "outfile=${BASE}.c.$$" "outfn=${BASE}.c" "$ROOT.et"
+if test -f ${BASE}.c && cmp -s ${BASE}.c.$$ ${BASE}.c ; then
+ rm -f ${BASE}.c.$$
+else
+ mv -f ${BASE}.c.$$ ${BASE}.c
+ chmod a-w ${BASE}.c
+fi
diff --git a/lib/et/error_message.c b/lib/et/error_message.c
new file mode 100644
index 0000000..8b9474f
--- /dev/null
+++ b/lib/et/error_message.c
@@ -0,0 +1,355 @@
+/*
+ * $Header$
+ * $Source$
+ * $Locker$
+ *
+ * Copyright 1987 by the Student Information Processing Board
+ * of the Massachusetts Institute of Technology
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SEMAPHORE_H
+#include <semaphore.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL
+#include <fcntl.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "com_err.h"
+#include "error_table.h"
+#include "internal.h"
+
+#ifdef TLS
+#define THREAD_LOCAL static TLS
+#else
+#define THREAD_LOCAL static
+#endif
+
+THREAD_LOCAL char buffer[25];
+
+struct et_list * _et_list = (struct et_list *) NULL;
+struct et_list * _et_dynamic_list = (struct et_list *) NULL;
+
+#ifdef __GNUC__
+#define COMERR_ATTR(x) __attribute__(x)
+#else
+#define COMERR_ATTR(x)
+#endif
+
+#ifdef HAVE_SEM_INIT
+static sem_t _et_lock;
+static int _et_lock_initialized;
+
+static void COMERR_ATTR((constructor)) setup_et_lock(void)
+{
+ sem_init(&_et_lock, 0, 1);
+ _et_lock_initialized = 1;
+}
+
+static void COMERR_ATTR((destructor)) fini_et_lock(void)
+{
+ sem_destroy(&_et_lock);
+ _et_lock_initialized = 0;
+}
+#endif
+
+
+int et_list_lock(void)
+{
+#ifdef HAVE_SEM_INIT
+ if (!_et_lock_initialized)
+ setup_et_lock();
+ return sem_wait(&_et_lock);
+#else
+ return 0;
+#endif
+}
+
+int et_list_unlock(void)
+{
+#ifdef HAVE_SEM_INIT
+ if (_et_lock_initialized)
+ return sem_post(&_et_lock);
+#endif
+ return 0;
+}
+
+typedef char *(*gettextf) (const char *);
+
+static gettextf com_err_gettext = NULL;
+
+gettextf set_com_err_gettext(gettextf new_proc)
+{
+ gettextf x = com_err_gettext;
+
+ com_err_gettext = new_proc;
+
+ return x;
+}
+
+#ifdef __GNU__
+#define SYS_ERR_BASE 0x40000000
+#else
+#define SYS_ERR_BASE 0
+#endif
+
+const char * error_message (errcode_t code)
+{
+ int offset;
+ struct et_list *et;
+ errcode_t table_num;
+ int started = 0;
+ char *cp;
+
+ offset = (int) (code & ((1<<ERRCODE_RANGE)-1));
+ table_num = code - offset;
+ if (table_num == SYS_ERR_BASE) {
+#ifdef HAS_SYS_ERRLIST
+ if (code < sys_nerr)
+ return(sys_errlist[code]);
+ else
+ goto oops;
+#else
+ cp = strerror(code);
+ if (cp)
+ return(cp);
+ else
+ goto oops;
+#endif
+ }
+ et_list_lock();
+ for (et = _et_list; et; et = et->next) {
+ if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
+ /* This is the right table */
+ if (et->table->n_msgs <= offset) {
+ break;
+ } else {
+ const char *msg = et->table->msgs[offset];
+ et_list_unlock();
+ if (com_err_gettext)
+ return (*com_err_gettext)(msg);
+ else
+ return msg;
+ }
+ }
+ }
+ for (et = _et_dynamic_list; et; et = et->next) {
+ if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) {
+ /* This is the right table */
+ if (et->table->n_msgs <= offset) {
+ break;
+ } else {
+ const char *msg = et->table->msgs[offset];
+ et_list_unlock();
+ if (com_err_gettext)
+ return (*com_err_gettext)(msg);
+ else
+ return msg;
+ }
+ }
+ }
+ et_list_unlock();
+oops:
+ strcpy (buffer, "Unknown code ");
+ if (table_num) {
+ strcat (buffer, error_table_name (table_num));
+ strcat (buffer, " ");
+ }
+ for (cp = buffer; *cp; cp++)
+ ;
+ if (offset >= 100) {
+ *cp++ = '0' + offset / 100;
+ offset %= 100;
+ started++;
+ }
+ if (started || offset >= 10) {
+ *cp++ = '0' + offset / 10;
+ offset %= 10;
+ }
+ *cp++ = '0' + offset;
+ *cp = '\0';
+ return(buffer);
+}
+
+/*
+ * This routine will only return a value if the we are not running as
+ * a privileged process.
+ */
+static char *safe_getenv(const char *arg)
+{
+#if !defined(_WIN32)
+ if ((getuid() != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#endif
+#if HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
+
+#define DEBUG_INIT 0x8000
+#define DEBUG_ADDREMOVE 0x0001
+
+static int debug_mask = 0;
+static FILE *debug_f = 0;
+
+static void init_debug(void)
+{
+ char *dstr, *fn, *tmp;
+
+ if (debug_mask & DEBUG_INIT)
+ return;
+
+ dstr = getenv("COMERR_DEBUG");
+ if (dstr) {
+ debug_mask = strtoul(dstr, &tmp, 0);
+ if (*tmp || errno)
+ debug_mask = 0;
+ }
+
+ debug_mask |= DEBUG_INIT;
+ if (debug_mask == DEBUG_INIT)
+ return;
+
+ fn = safe_getenv("COMERR_DEBUG_FILE");
+ if (fn)
+ debug_f = fopen(fn, "a");
+ if (!debug_f)
+ debug_f = fopen("/dev/tty", "a");
+ if (debug_f) {
+#ifdef HAVE_FCNTL
+ int fd = fileno(debug_f);
+
+ if (fd >= 0) {
+ int flags = fcntl(fd, F_GETFD);
+
+ if (flags >= 0)
+ flags = fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ if (flags < 0) {
+ fprintf(debug_f, "Couldn't set FD_CLOEXEC "
+ "on debug FILE: %s\n", strerror(errno));
+ fclose(debug_f);
+ debug_f = NULL;
+ debug_mask = DEBUG_INIT;
+ }
+ }
+#endif
+ } else
+ debug_mask = DEBUG_INIT;
+}
+
+/*
+ * New interface provided by krb5's com_err library
+ */
+errcode_t add_error_table(const struct error_table * et)
+{
+ struct et_list *el;
+
+ if (!(el = (struct et_list *) malloc(sizeof(struct et_list))))
+ return ENOMEM;
+
+ if (et_list_lock() != 0) {
+ free(el);
+ return errno;
+ }
+
+ el->table = et;
+ el->next = _et_dynamic_list;
+ _et_dynamic_list = el;
+
+ init_debug();
+ if (debug_mask & DEBUG_ADDREMOVE)
+ fprintf(debug_f, "add_error_table: %s (0x%p)\n",
+ error_table_name(et->base),
+ (const void *) et);
+
+ et_list_unlock();
+ return 0;
+}
+
+/*
+ * New interface provided by krb5's com_err library
+ */
+errcode_t remove_error_table(const struct error_table * et)
+{
+ struct et_list *el;
+ struct et_list *el2 = 0;
+
+ if (et_list_lock() != 0)
+ return ENOENT;
+
+ el = _et_dynamic_list;
+ init_debug();
+ while (el) {
+ if (el->table->base == et->base) {
+ if (el2) /* Not the beginning of the list */
+ el2->next = el->next;
+ else
+ _et_dynamic_list = el->next;
+ (void) free(el);
+ if (debug_mask & DEBUG_ADDREMOVE)
+ fprintf(debug_f,
+ "remove_error_table: %s (0x%p)\n",
+ error_table_name(et->base),
+ (const void *) et);
+ et_list_unlock();
+ return 0;
+ }
+ el2 = el;
+ el = el->next;
+ }
+ if (debug_mask & DEBUG_ADDREMOVE)
+ fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n",
+ error_table_name(et->base),
+ (const void *) et);
+ et_list_unlock();
+ return ENOENT;
+}
+
+/*
+ * Variant of the interface provided by Heimdal's com_err library
+ */
+void
+add_to_error_table(struct et_list *new_table)
+{
+ add_error_table(new_table->table);
+}
diff --git a/lib/et/error_table.h b/lib/et/error_table.h
new file mode 100644
index 0000000..24e4762
--- /dev/null
+++ b/lib/et/error_table.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1988 by the Student Information Processing Board of the
+ * Massachusetts Institute of Technology.
+ *
+ * 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.
+ */
+
+#ifndef _ET_H
+
+struct et_list {
+ struct et_list *next;
+ const struct error_table *table;
+};
+extern struct et_list *_et_list, *_et_dynamic_list;
+
+#define ERRCODE_RANGE 8 /* # of bits to shift table number */
+#define BITS_PER_CHAR 6 /* # bits to shift per character in name */
+
+extern const char *error_table_name(errcode_t num);
+
+#define _ET_H
+#endif
diff --git a/lib/et/et_c.awk b/lib/et/et_c.awk
new file mode 100644
index 0000000..99c33ba
--- /dev/null
+++ b/lib/et/et_c.awk
@@ -0,0 +1,269 @@
+BEGIN {
+if ( length(outfn) == 0) {
+ outfn = outfile
+}
+char_shift=64
+## "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+c2n["A"]=1
+c2n["B"]=2
+c2n["C"]=3
+c2n["D"]=4
+c2n["E"]=5
+c2n["F"]=6
+c2n["G"]=7
+c2n["H"]=8
+c2n["I"]=9
+c2n["J"]=10
+c2n["K"]=11
+c2n["L"]=12
+c2n["M"]=13
+c2n["N"]=14
+c2n["O"]=15
+c2n["P"]=16
+c2n["Q"]=17
+c2n["R"]=18
+c2n["S"]=19
+c2n["T"]=20
+c2n["U"]=21
+c2n["V"]=22
+c2n["W"]=23
+c2n["X"]=24
+c2n["Y"]=25
+c2n["Z"]=26
+c2n["a"]=27
+c2n["b"]=28
+c2n["c"]=29
+c2n["d"]=30
+c2n["e"]=31
+c2n["f"]=32
+c2n["g"]=33
+c2n["h"]=34
+c2n["i"]=35
+c2n["j"]=36
+c2n["k"]=37
+c2n["l"]=38
+c2n["m"]=39
+c2n["n"]=40
+c2n["o"]=41
+c2n["p"]=42
+c2n["q"]=43
+c2n["r"]=44
+c2n["s"]=45
+c2n["t"]=46
+c2n["u"]=47
+c2n["v"]=48
+c2n["w"]=49
+c2n["x"]=50
+c2n["y"]=51
+c2n["z"]=52
+c2n["0"]=53
+c2n["1"]=54
+c2n["2"]=55
+c2n["3"]=56
+c2n["4"]=57
+c2n["5"]=58
+c2n["6"]=59
+c2n["7"]=60
+c2n["8"]=61
+c2n["9"]=62
+c2n["_"]=63
+}
+/^#/ { next }
+/^[ \t]*(error_table|et)[ \t]+[a-zA-Z][a-zA-Z0-9_]+/ {
+ table_number = 0
+ mod_base = 1000000
+ if (NF > 2) {
+ table_name = $3
+ base_name = $2
+ } else {
+ table_name = $2
+ base_name = table_name
+ }
+ for(i=1; i<=length(base_name); i++) {
+ table_number=(table_number*char_shift)+c2n[substr(base_name,i,1)]
+ }
+
+ # We start playing *_high, *low games here because the some
+ # awk programs do not have the necessary precision (sigh)
+ tab_base_low = table_number % mod_base
+ if (tab_base_low < 0) {
+ # Work around stupid bug in the ARM libm
+ tab_base_low = tab_base_low + mod_base
+ }
+ tab_base_high = int(table_number / mod_base)
+ tab_base_sign = 1;
+
+ # figure out: table_number_base=table_number*256
+ tab_base_low = tab_base_low * 256
+ tab_base_high = (tab_base_high * 256) + \
+ int(tab_base_low / mod_base)
+ tab_base_low = tab_base_low % mod_base
+ if (tab_base_low < 0) {
+ # Work around stupid bug in the ARM libm
+ tab_base_low = tab_base_low + mod_base
+ }
+
+ if (table_number > 128*256*256) {
+ # figure out: table_number_base -= 256*256*256*256
+ # sub_high, sub_low is 256*256*256*256
+ sub_low = 256*256*256 % mod_base
+ sub_high = int(256*256*256 / mod_base)
+
+ sub_low = sub_low * 256
+ sub_high = (sub_high * 256) + int(sub_low / mod_base)
+ sub_low = sub_low % mod_base
+
+ tab_base_low = sub_low - tab_base_low;
+ tab_base_high = sub_high - tab_base_high;
+ tab_base_sign = -1;
+ if (tab_base_low < 0) {
+ tab_base_low = tab_base_low + mod_base
+ tab_base_high--
+ }
+ }
+ print "/*" > outfile
+ print " * " outfn ":" > outfile
+ print " * This file is automatically generated; please do not edit it." > outfile
+ print " */" > outfile
+
+ print "" > outfile
+ print "#include <stdlib.h>" > outfile
+ print "" > outfile
+ print "#define N_(a) a" > outfile
+ print "" > outfile
+ print "static const char * const text[] = {" > outfile
+ table_item_count = 0
+}
+
+(continuation == 1) && ($0 ~ /\\[ \t]*$/) {
+ text=substr($0,1,length($0)-1);
+# printf "\t\t\"%s\"\n", text > outfile
+ cont_buf=cont_buf text;
+}
+
+(continuation == 1) && ($0 ~ /"[ \t]*$/) {
+# printf "\t\t\"%s,\n", $0 > outfile
+ printf "\tN_(%s),\n", cont_buf $0 > outfile
+ continuation = 0;
+}
+
+/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[^ \t]/ {
+ # Be tolerant to missing whitespace after `,' ...
+ sub(/,/, ", ")
+}
+
+/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*$/ {
+ table_item_count++
+ skipone=1
+ next
+}
+
+/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*".*"[ \t]*$/ {
+ text=""
+ for (i=3; i<=NF; i++) {
+ text = text FS $i
+ }
+ text=substr(text,2,length(text)-1);
+ printf "\tN_(%s),\n", text > outfile
+ table_item_count++
+}
+
+/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,[ \t]*".*\\[ \t]*$/ {
+ text=""
+ for (i=3; i<=NF; i++) {
+ text = text FS $i
+ }
+ text=substr(text,2,length(text)-2);
+# printf "\t%s\"\n", text > outfile
+ cont_buf=text
+ table_item_count++
+ continuation++;
+}
+
+/^[ \t]*".*\\[ \t]*$/ {
+ if (skipone) {
+ text=substr($0,1,length($0)-1);
+# printf "\t%s\"\n", text > outfile
+ cont_buf=text
+ continuation++;
+ }
+ skipone=0
+}
+
+{
+ if (skipone) {
+ printf "\tN_(%s),\n", $0 > outfile
+ }
+ skipone=0
+}
+
+/^[ \t]*(prefix)$/ {
+ prefix_str = ""
+}
+
+/^[ \t]*(prefix)[ \t]+[A-Z_0-9]+/ {
+ prefix_str = $2 "_"
+}
+
+/^[ \t]*(index)[ \t]+[A-Z_0-9]+/ {
+ new_idx = $2
+ for (i = table_item_count ; i < new_idx; i++) {
+ printf "\tN_(\"Reserved %s error (%d)\"),\n", \
+ table_name, table_item_count++ > outfile
+ }
+}
+
+END {
+ print " 0" > outfile
+ print "};" > outfile
+ print "" > outfile
+ print "struct error_table {" > outfile
+ print " char const * const * msgs;" > outfile
+ print " long base;" > outfile
+ print " int n_msgs;" > outfile
+ print "};" > outfile
+ print "struct et_list {" > outfile
+ print " struct et_list *next;" > outfile
+ print " const struct error_table * table;" > outfile
+ print "};" > outfile
+ print "extern struct et_list *_et_list;" > outfile
+ print "" > outfile
+ if (tab_base_high == 0) {
+ print "const struct error_table et_" table_name "_error_table = { text, " \
+ sprintf("%dL, %d };", tab_base_sign*tab_base_low, \
+ table_item_count) > outfile
+ } else {
+ print "const struct error_table et_" table_name "_error_table = { text, " \
+ sprintf("%d%06dL, %d };", tab_base_sign*tab_base_high, \
+ tab_base_low, table_item_count) > outfile
+ }
+ print "" > outfile
+ print "static struct et_list link = { 0, 0 };" > outfile
+ print "" > outfile
+ print "void initialize_" table_name "_error_table_r(struct et_list **list);" > outfile
+ print "void initialize_" table_name "_error_table(void);" > outfile
+ print "" > outfile
+ print "void initialize_" table_name "_error_table(void) {" > outfile
+ print " initialize_" table_name "_error_table_r(&_et_list);" > outfile
+ print "}" > outfile
+ print "" > outfile
+ print "/* For Heimdal compatibility */" > outfile
+ print "void initialize_" table_name "_error_table_r(struct et_list **list)" > outfile
+ print "{" > outfile
+ print " struct et_list *et, **end;" > outfile
+ print "" > outfile
+ print " for (end = list, et = *list; et; end = &et->next, et = et->next)" > outfile
+ print " if (et->table->msgs == text)" > outfile
+ print " return;" > outfile
+ print " et = malloc(sizeof(struct et_list));" > outfile
+ print " if (et == 0) {" > outfile
+ print " if (!link.table)" > outfile
+ print " et = &link;" > outfile
+ print " else" > outfile
+ print " return;" > outfile
+ print " }" > outfile
+ print " et->table = &et_" table_name "_error_table;" > outfile
+ print " et->next = 0;" > outfile
+ print " *end = et;" > outfile
+ print "}" > outfile
+}
diff --git a/lib/et/et_h.awk b/lib/et/et_h.awk
new file mode 100644
index 0000000..8337121
--- /dev/null
+++ b/lib/et/et_h.awk
@@ -0,0 +1,204 @@
+BEGIN {
+if ( length(outfn) == 0) {
+ outfn = outfile
+}
+char_shift=64
+## "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+c2n["A"]=1
+c2n["B"]=2
+c2n["C"]=3
+c2n["D"]=4
+c2n["E"]=5
+c2n["F"]=6
+c2n["G"]=7
+c2n["H"]=8
+c2n["I"]=9
+c2n["J"]=10
+c2n["K"]=11
+c2n["L"]=12
+c2n["M"]=13
+c2n["N"]=14
+c2n["O"]=15
+c2n["P"]=16
+c2n["Q"]=17
+c2n["R"]=18
+c2n["S"]=19
+c2n["T"]=20
+c2n["U"]=21
+c2n["V"]=22
+c2n["W"]=23
+c2n["X"]=24
+c2n["Y"]=25
+c2n["Z"]=26
+c2n["a"]=27
+c2n["b"]=28
+c2n["c"]=29
+c2n["d"]=30
+c2n["e"]=31
+c2n["f"]=32
+c2n["g"]=33
+c2n["h"]=34
+c2n["i"]=35
+c2n["j"]=36
+c2n["k"]=37
+c2n["l"]=38
+c2n["m"]=39
+c2n["n"]=40
+c2n["o"]=41
+c2n["p"]=42
+c2n["q"]=43
+c2n["r"]=44
+c2n["s"]=45
+c2n["t"]=46
+c2n["u"]=47
+c2n["v"]=48
+c2n["w"]=49
+c2n["x"]=50
+c2n["y"]=51
+c2n["z"]=52
+c2n["0"]=53
+c2n["1"]=54
+c2n["2"]=55
+c2n["3"]=56
+c2n["4"]=57
+c2n["5"]=58
+c2n["6"]=59
+c2n["7"]=60
+c2n["8"]=61
+c2n["9"]=62
+c2n["_"]=63
+}
+/^#/ { next }
+/^[ \t]*(error_table|et)[ \t]+[a-zA-Z][a-zA-Z0-9_]+/ {
+ table_number = 0
+ mod_base = 1000000
+ if (NF > 2) {
+ table_name = $3
+ base_name = $2
+ } else {
+ table_name = $2
+ base_name = table_name
+ }
+ for(i=1; i<=length(base_name); i++) {
+ table_number=(table_number*char_shift)+c2n[substr(base_name,i,1)]
+ }
+ # We start playing *_high, *low games here because the some
+ # awk programs do not have the necessary precision (sigh)
+ tab_base_low = table_number % mod_base
+ if (tab_base_low < 0) {
+ # Work around stupid bug in the ARM libm
+ tab_base_low = tab_base_low + mod_base
+ }
+ tab_base_high = int(table_number / mod_base)
+ tab_base_sign = 1;
+
+ # figure out: table_number_base=table_number*256
+ tab_base_low = tab_base_low * 256
+ tab_base_high = (tab_base_high * 256) + \
+ int(tab_base_low / mod_base)
+ tab_base_low = tab_base_low % mod_base
+ if (tab_base_low < 0) {
+ # Work around stupid bug in the ARM libm
+ tab_base_low = tab_base_low + mod_base
+ }
+
+ if (table_number > 128*256*256) {
+ # figure out: table_number_base -= 256*256*256*256
+ # sub_high, sub_low is 256*256*256*256
+ sub_low = 256*256*256 % mod_base
+ sub_high = int(256*256*256 / mod_base)
+
+ sub_low = sub_low * 256
+ sub_high = (sub_high * 256) + int(sub_low / mod_base)
+ sub_low = sub_low % mod_base
+
+ tab_base_low = sub_low - tab_base_low;
+ tab_base_high = sub_high - tab_base_high;
+ tab_base_sign = -1;
+ if (tab_base_low < 0) {
+ tab_base_low = tab_base_low + mod_base
+ tab_base_high--
+ }
+ }
+ prefix_str = ""
+ curr_idx = 0
+ curr_low = tab_base_low
+ curr_high = tab_base_high
+ curr_sign = tab_base_sign
+ print "/*" > outfile
+ print " * " outfn ":" > outfile
+ print " * This file is automatically generated; please do not edit it." > outfile
+ print " */" > outfile
+ print "" > outfile
+ print "#include <et/com_err.h>" > outfile
+ print "" > outfile
+}
+
+/^[ \t]*(error_code|ec)[ \t]+[A-Z_0-9]+,/ {
+ # Be tolerant to missing whitespace after `,' ...
+ sub(/,/, ", ")
+
+ tag=prefix_str substr($2,1,length($2)-1)
+ if (curr_high == 0) {
+ printf "#define %-40s (%dL)\n", tag, \
+ curr_sign*curr_low > outfile
+ } else {
+ printf "#define %-40s (%d%06dL)\n", tag, curr_high*curr_sign, \
+ curr_low > outfile
+ }
+ curr_low += curr_sign;
+ curr_idx++;
+ if (curr_low >= mod_base) {
+ curr_low -= mod_base;
+ curr_high++
+ }
+ if (curr_low < 0) {
+ cur_low += mod_base
+ cur_high--
+ }
+}
+
+/^[ \t]*(prefix)$/ {
+ prefix_str = ""
+}
+
+/^[ \t]*(prefix)[ \t]+[A-Z_0-9]+/ {
+ prefix_str = $2 "_"
+}
+
+/^[ \t]*(index)[ \t]+[A-Z_0-9]+/ {
+ new_idx = $2
+ curr_low += curr_sign * (new_idx - curr_idx)
+ curr_idx = new_idx
+ if (curr_low >= mod_base) {
+ curr_low -= mod_base;
+ curr_high++
+ }
+ if (curr_low < 0) {
+ cur_low += mod_base
+ cur_high--
+ }
+}
+
+
+END {
+ print "extern const struct error_table et_" table_name "_error_table;" > outfile
+ print "extern void initialize_" table_name "_error_table(void);" > outfile
+ print "" > outfile
+ print "/* For compatibility with Heimdal */" > outfile
+ print "extern void initialize_" table_name "_error_table_r(struct et_list **list);" > outfile
+ print "" > outfile
+ if (tab_base_high == 0) {
+ print "#define ERROR_TABLE_BASE_" table_name " (" \
+ sprintf("%d", tab_base_sign*tab_base_low) \
+ "L)" > outfile
+ } else {
+ print "#define ERROR_TABLE_BASE_" table_name " (" \
+ sprintf("%d%06d", tab_base_sign*tab_base_high, \
+ tab_base_low) "L)" > outfile
+ }
+ print "" > outfile
+ print "/* for compatibility with older versions... */" > outfile
+ print "#define init_" table_name "_err_tbl initialize_" table_name "_error_table" > outfile
+ print "#define " table_name "_err_base ERROR_TABLE_BASE_" table_name > outfile
+}
diff --git a/lib/et/et_name.c b/lib/et/et_name.c
new file mode 100644
index 0000000..d9a3e87
--- /dev/null
+++ b/lib/et/et_name.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 1987 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 "config.h"
+#include "com_err.h"
+#include "error_table.h"
+#include "internal.h"
+
+static const char char_set[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+static char buf[6];
+
+const char * error_table_name(errcode_t num)
+{
+ int ch;
+ int i;
+ char *p;
+
+ /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */
+ p = buf;
+ num >>= ERRCODE_RANGE;
+ /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */
+ num &= 077777777L;
+ /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */
+ for (i = 4; i >= 0; i--) {
+ ch = (int)((num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1));
+ if (ch != 0)
+ *p++ = char_set[ch-1];
+ }
+ *p = '\0';
+ return(buf);
+}
diff --git a/lib/et/init_et.c b/lib/et/init_et.c
new file mode 100644
index 0000000..772660d
--- /dev/null
+++ b/lib/et/init_et.c
@@ -0,0 +1,53 @@
+/*
+ * $Header$
+ * $Source$
+ * $Locker$
+ *
+ * Copyright 1986, 1987, 1988 by MIT Information Systems and
+ * the 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 "config.h"
+#include <stdio.h>
+#include <errno.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include "com_err.h"
+#include "error_table.h"
+
+struct foobar {
+ struct et_list etl;
+ struct error_table et;
+};
+
+extern struct et_list * _et_dynamic_list;
+
+int init_error_table(const char * const *msgs, long base, int count)
+{
+ struct foobar * new_et;
+
+ if (!base || !count || !msgs)
+ return 0;
+
+ new_et = (struct foobar *) malloc(sizeof(struct foobar));
+ if (!new_et)
+ return ENOMEM; /* oops */
+ new_et->etl.table = &new_et->et;
+ new_et->et.msgs = msgs;
+ new_et->et.base = base;
+ new_et->et.n_msgs= count;
+
+ new_et->etl.next = _et_dynamic_list;
+ _et_dynamic_list = &new_et->etl;
+ return 0;
+}
diff --git a/lib/et/internal.h b/lib/et/internal.h
new file mode 100644
index 0000000..d16f373
--- /dev/null
+++ b/lib/et/internal.h
@@ -0,0 +1,19 @@
+/*
+ * internal include file for com_err package
+ *
+ * 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 <errno.h>
+
+#ifdef NEED_SYS_ERRLIST
+extern char const * const sys_errlist[];
+extern const int sys_nerr;
+#endif
diff --git a/lib/et/test_cases/continuation.c b/lib/et/test_cases/continuation.c
new file mode 100644
index 0000000..1207897
--- /dev/null
+++ b/lib/et/test_cases/continuation.c
@@ -0,0 +1,55 @@
+/*
+ * continuation.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#define N_(a) a
+
+static const char * const text[] = {
+ N_("New password was found in a dictionary of possible passwords and\ntherefore may be easily guessed. Please choose another password.\nSee the ovpasswd man page for help in choosing a good password."),
+ 0
+};
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_ovk_error_table = { text, 43787520L, 1 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_ovk_error_table_r(struct et_list **list);
+void initialize_ovk_error_table(void);
+
+void initialize_ovk_error_table(void) {
+ initialize_ovk_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_ovk_error_table_r(struct et_list **list)
+{
+ struct et_list *et, **end;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == text)
+ return;
+ et = malloc(sizeof(struct et_list));
+ if (et == 0) {
+ if (!link.table)
+ et = &link;
+ else
+ return;
+ }
+ et->table = &et_ovk_error_table;
+ et->next = 0;
+ *end = et;
+}
diff --git a/lib/et/test_cases/continuation.et b/lib/et/test_cases/continuation.et
new file mode 100644
index 0000000..64d1674
--- /dev/null
+++ b/lib/et/test_cases/continuation.et
@@ -0,0 +1,8 @@
+ error_table ovk
+
+error_code CHPASS_UTIL_PASSWORD_IN_DICTIONARY,
+"New password was found in a dictionary of possible passwords and\n\
+therefore may be easily guessed. Please choose another password.\n\
+See the ovpasswd man page for help in choosing a good password."
+
+ end
diff --git a/lib/et/test_cases/continuation.h b/lib/et/test_cases/continuation.h
new file mode 100644
index 0000000..9cfe602
--- /dev/null
+++ b/lib/et/test_cases/continuation.h
@@ -0,0 +1,19 @@
+/*
+ * continuation.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define CHPASS_UTIL_PASSWORD_IN_DICTIONARY (43787520L)
+extern const struct error_table et_ovk_error_table;
+extern void initialize_ovk_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_ovk_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_ovk (43787520L)
+
+/* for compatibility with older versions... */
+#define init_ovk_err_tbl initialize_ovk_error_table
+#define ovk_err_base ERROR_TABLE_BASE_ovk
diff --git a/lib/et/test_cases/heimdal.c b/lib/et/test_cases/heimdal.c
new file mode 100644
index 0000000..d9be4ce
--- /dev/null
+++ b/lib/et/test_cases/heimdal.c
@@ -0,0 +1,136 @@
+/*
+ * heimdal.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#define N_(a) a
+
+static const char * const text[] = {
+ N_("Kerberos successful"),
+ N_("Kerberos principal expired"),
+ N_("Kerberos service expired"),
+ N_("Kerberos auth expired"),
+ N_("Incorrect kerberos master key version"),
+ N_("Incorrect kerberos master key version"),
+ N_("Incorrect kerberos master key version"),
+ N_("Kerberos error: byte order unknown"),
+ N_("Kerberos principal unknown"),
+ N_("Kerberos principal not unique"),
+ N_("Kerberos principal has null key"),
+ N_("Reserved krb error (11)"),
+ N_("Reserved krb error (12)"),
+ N_("Reserved krb error (13)"),
+ N_("Reserved krb error (14)"),
+ N_("Reserved krb error (15)"),
+ N_("Reserved krb error (16)"),
+ N_("Reserved krb error (17)"),
+ N_("Reserved krb error (18)"),
+ N_("Reserved krb error (19)"),
+ N_("Generic error from Kerberos KDC"),
+ N_("Can't read Kerberos ticket file"),
+ N_("Can't find Kerberos ticket or TGT"),
+ N_("Reserved krb error (23)"),
+ N_("Reserved krb error (24)"),
+ N_("Reserved krb error (25)"),
+ N_("Kerberos TGT Expired"),
+ N_("Reserved krb error (27)"),
+ N_("Reserved krb error (28)"),
+ N_("Reserved krb error (29)"),
+ N_("Reserved krb error (30)"),
+ N_("Kerberos error: Can't decode authenticator"),
+ N_("Kerberos ticket expired"),
+ N_("Kerberos ticket not yet valid"),
+ N_("Kerberos error: Repeated request"),
+ N_("The kerberos ticket isn't for us"),
+ N_("Kerberos request inconsistent"),
+ N_("Kerberos error: delta_t too big"),
+ N_("Kerberos error: incorrect net address"),
+ N_("Kerberos protocol version mismatch"),
+ N_("Kerberos error: invalid msg type"),
+ N_("Kerberos error: message stream modified"),
+ N_("Kerberos error: message out of order"),
+ N_("Kerberos error: unauthorized request"),
+ N_("Reserved krb error (44)"),
+ N_("Reserved krb error (45)"),
+ N_("Reserved krb error (46)"),
+ N_("Reserved krb error (47)"),
+ N_("Reserved krb error (48)"),
+ N_("Reserved krb error (49)"),
+ N_("Reserved krb error (50)"),
+ N_("Kerberos error: current PW is null"),
+ N_("Kerberos error: Incorrect current password"),
+ N_("Kerberos protocol error"),
+ N_("Error returned by Kerberos KDC"),
+ N_("Null Kerberos ticket returned by KDC"),
+ N_("Kerberos error: Retry count exceeded"),
+ N_("Kerberos error: Can't send request"),
+ N_("Reserved krb error (58)"),
+ N_("Reserved krb error (59)"),
+ N_("Reserved krb error (60)"),
+ N_("Kerberos error: not all tickets returned"),
+ N_("Kerberos error: incorrect password"),
+ N_("Kerberos error: Protocol Error"),
+ N_("Reserved krb error (64)"),
+ N_("Reserved krb error (65)"),
+ N_("Reserved krb error (66)"),
+ N_("Reserved krb error (67)"),
+ N_("Reserved krb error (68)"),
+ N_("Reserved krb error (69)"),
+ N_("Other error"),
+ N_("Don't have Kerberos ticket-granting ticket"),
+ N_("Reserved krb error (72)"),
+ N_("Reserved krb error (73)"),
+ N_("Reserved krb error (74)"),
+ N_("Reserved krb error (75)"),
+ N_("No ticket file found"),
+ N_("Couldn't access ticket file"),
+ N_("Couldn't lock ticket file"),
+ N_("Bad ticket file format"),
+ N_("tf_init not called first"),
+ N_("Bad Kerberos name format"),
+ 0
+};
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_krb_error_table = { text, 39525376L, 82 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_krb_error_table_r(struct et_list **list);
+void initialize_krb_error_table(void);
+
+void initialize_krb_error_table(void) {
+ initialize_krb_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_krb_error_table_r(struct et_list **list)
+{
+ struct et_list *et, **end;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == text)
+ return;
+ et = malloc(sizeof(struct et_list));
+ if (et == 0) {
+ if (!link.table)
+ et = &link;
+ else
+ return;
+ }
+ et->table = &et_krb_error_table;
+ et->next = 0;
+ *end = et;
+}
diff --git a/lib/et/test_cases/heimdal.et b/lib/et/test_cases/heimdal.et
new file mode 100644
index 0000000..9dce192
--- /dev/null
+++ b/lib/et/test_cases/heimdal.et
@@ -0,0 +1,65 @@
+# Copyright 1987,1988 Massachusetts Institute of Technology
+#
+# For copying and distribution information, see the file
+# "mit-copyright.h".
+#
+# This might look like a com_err file, but is not
+#
+id "$Id: krb_err.et,v 1.7 1998/03/29 14:19:52 bg Exp $"
+
+error_table krb
+
+prefix KRBET
+ec KSUCCESS, "Kerberos successful"
+ec KDC_NAME_EXP, "Kerberos principal expired"
+ec KDC_SERVICE_EXP, "Kerberos service expired"
+ec KDC_AUTH_EXP, "Kerberos auth expired"
+ec KDC_PKT_VER, "Incorrect kerberos master key version"
+ec KDC_P_MKEY_VER, "Incorrect kerberos master key version"
+ec KDC_S_MKEY_VER, "Incorrect kerberos master key version"
+ec KDC_BYTE_ORDER, "Kerberos error: byte order unknown"
+ec KDC_PR_UNKNOWN, "Kerberos principal unknown"
+ec KDC_PR_N_UNIQUE, "Kerberos principal not unique"
+ec KDC_NULL_KEY, "Kerberos principal has null key"
+index 20
+ec KDC_GEN_ERR, "Generic error from Kerberos KDC"
+ec GC_TKFIL, "Can't read Kerberos ticket file"
+ec GC_NOTKT, "Can't find Kerberos ticket or TGT"
+index 26
+ec MK_AP_TGTEXP, "Kerberos TGT Expired"
+index 31
+ec RD_AP_UNDEC, "Kerberos error: Can't decode authenticator"
+ec RD_AP_EXP, "Kerberos ticket expired"
+ec RD_AP_NYV, "Kerberos ticket not yet valid"
+ec RD_AP_REPEAT, "Kerberos error: Repeated request"
+ec RD_AP_NOT_US, "The kerberos ticket isn't for us"
+ec RD_AP_INCON, "Kerberos request inconsistent"
+ec RD_AP_TIME, "Kerberos error: delta_t too big"
+ec RD_AP_BADD, "Kerberos error: incorrect net address"
+ec RD_AP_VERSION, "Kerberos protocol version mismatch"
+ec RD_AP_MSG_TYPE, "Kerberos error: invalid msg type"
+ec RD_AP_MODIFIED, "Kerberos error: message stream modified"
+ec RD_AP_ORDER, "Kerberos error: message out of order"
+ec RD_AP_UNAUTHOR, "Kerberos error: unauthorized request"
+index 51
+ec GT_PW_NULL, "Kerberos error: current PW is null"
+ec GT_PW_BADPW, "Kerberos error: Incorrect current password"
+ec GT_PW_PROT, "Kerberos protocol error"
+ec GT_PW_KDCERR, "Error returned by Kerberos KDC"
+ec GT_PW_NULLTKT, "Null Kerberos ticket returned by KDC"
+ec SKDC_RETRY, "Kerberos error: Retry count exceeded"
+ec SKDC_CANT, "Kerberos error: Can't send request"
+index 61
+ec INTK_W_NOTALL, "Kerberos error: not all tickets returned"
+ec INTK_BADPW, "Kerberos error: incorrect password"
+ec INTK_PROT, "Kerberos error: Protocol Error"
+index 70
+ec INTK_ERR, "Other error"
+ec AD_NOTGT, "Don't have Kerberos ticket-granting ticket"
+index 76
+ec NO_TKT_FIL, "No ticket file found"
+ec TKT_FIL_ACC, "Couldn't access ticket file"
+ec TKT_FIL_LCK, "Couldn't lock ticket file"
+ec TKT_FIL_FMT, "Bad ticket file format"
+ec TKT_FIL_INI, "tf_init not called first"
+ec KNAME_FMT, "Bad Kerberos name format"
diff --git a/lib/et/test_cases/heimdal.h b/lib/et/test_cases/heimdal.h
new file mode 100644
index 0000000..5c94354
--- /dev/null
+++ b/lib/et/test_cases/heimdal.h
@@ -0,0 +1,64 @@
+/*
+ * heimdal.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define KRBET_KSUCCESS (39525376L)
+#define KRBET_KDC_NAME_EXP (39525377L)
+#define KRBET_KDC_SERVICE_EXP (39525378L)
+#define KRBET_KDC_AUTH_EXP (39525379L)
+#define KRBET_KDC_PKT_VER (39525380L)
+#define KRBET_KDC_P_MKEY_VER (39525381L)
+#define KRBET_KDC_S_MKEY_VER (39525382L)
+#define KRBET_KDC_BYTE_ORDER (39525383L)
+#define KRBET_KDC_PR_UNKNOWN (39525384L)
+#define KRBET_KDC_PR_N_UNIQUE (39525385L)
+#define KRBET_KDC_NULL_KEY (39525386L)
+#define KRBET_KDC_GEN_ERR (39525396L)
+#define KRBET_GC_TKFIL (39525397L)
+#define KRBET_GC_NOTKT (39525398L)
+#define KRBET_MK_AP_TGTEXP (39525402L)
+#define KRBET_RD_AP_UNDEC (39525407L)
+#define KRBET_RD_AP_EXP (39525408L)
+#define KRBET_RD_AP_NYV (39525409L)
+#define KRBET_RD_AP_REPEAT (39525410L)
+#define KRBET_RD_AP_NOT_US (39525411L)
+#define KRBET_RD_AP_INCON (39525412L)
+#define KRBET_RD_AP_TIME (39525413L)
+#define KRBET_RD_AP_BADD (39525414L)
+#define KRBET_RD_AP_VERSION (39525415L)
+#define KRBET_RD_AP_MSG_TYPE (39525416L)
+#define KRBET_RD_AP_MODIFIED (39525417L)
+#define KRBET_RD_AP_ORDER (39525418L)
+#define KRBET_RD_AP_UNAUTHOR (39525419L)
+#define KRBET_GT_PW_NULL (39525427L)
+#define KRBET_GT_PW_BADPW (39525428L)
+#define KRBET_GT_PW_PROT (39525429L)
+#define KRBET_GT_PW_KDCERR (39525430L)
+#define KRBET_GT_PW_NULLTKT (39525431L)
+#define KRBET_SKDC_RETRY (39525432L)
+#define KRBET_SKDC_CANT (39525433L)
+#define KRBET_INTK_W_NOTALL (39525437L)
+#define KRBET_INTK_BADPW (39525438L)
+#define KRBET_INTK_PROT (39525439L)
+#define KRBET_INTK_ERR (39525446L)
+#define KRBET_AD_NOTGT (39525447L)
+#define KRBET_NO_TKT_FIL (39525452L)
+#define KRBET_TKT_FIL_ACC (39525453L)
+#define KRBET_TKT_FIL_LCK (39525454L)
+#define KRBET_TKT_FIL_FMT (39525455L)
+#define KRBET_TKT_FIL_INI (39525456L)
+#define KRBET_KNAME_FMT (39525457L)
+extern const struct error_table et_krb_error_table;
+extern void initialize_krb_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_krb_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_krb (39525376L)
+
+/* for compatibility with older versions... */
+#define init_krb_err_tbl initialize_krb_error_table
+#define krb_err_base ERROR_TABLE_BASE_krb
diff --git a/lib/et/test_cases/heimdal2.c b/lib/et/test_cases/heimdal2.c
new file mode 100644
index 0000000..d2ad572
--- /dev/null
+++ b/lib/et/test_cases/heimdal2.c
@@ -0,0 +1,122 @@
+/*
+ * heimdal2.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#define N_(a) a
+
+static const char * const text[] = {
+ N_("$Id: kadm_err.et,v 1.5 1998/01/16 23:11:27 joda Exp $"),
+ N_("Cannot fetch local realm"),
+ N_("Unable to fetch credentials"),
+ N_("Bad key supplied"),
+ N_("Can't encrypt data"),
+ N_("Cannot encode/decode authentication info"),
+ N_("Principal attempting change is in wrong realm"),
+ N_("Packet is too large"),
+ N_("Version number is incorrect"),
+ N_("Checksum does not match"),
+ N_("Unsealing private data failed"),
+ N_("Unsupported operation"),
+ N_("Could not find administrating host"),
+ N_("Administrating host name is unknown"),
+ N_("Could not find service name in services database"),
+ N_("Could not create socket"),
+ N_("Could not connect to server"),
+ N_("Could not fetch local socket address"),
+ N_("Could not fetch master key"),
+ N_("Could not verify master key"),
+ N_("Entry already exists in database"),
+ N_("Database store error"),
+ N_("Database read error"),
+ N_("Insufficient access to perform requested operation"),
+ N_("Data is available for return to client"),
+ N_("No such entry in the database"),
+ N_("Memory exhausted"),
+ N_("Could not fetch system hostname"),
+ N_("Could not bind port"),
+ N_("Length mismatch problem"),
+ N_("Illegal use of wildcard"),
+ N_("Database is locked or in use--try again later"),
+ N_("Insecure password rejected"),
+ N_("Cleartext password and DES key did not match"),
+ N_("Invalid principal for change srvtab request"),
+ N_("Attempt do delete immutable principal"),
+ N_("Reserved kadm error (36)"),
+ N_("Reserved kadm error (37)"),
+ N_("Reserved kadm error (38)"),
+ N_("Reserved kadm error (39)"),
+ N_("Reserved kadm error (40)"),
+ N_("Reserved kadm error (41)"),
+ N_("Reserved kadm error (42)"),
+ N_("Reserved kadm error (43)"),
+ N_("Reserved kadm error (44)"),
+ N_("Reserved kadm error (45)"),
+ N_("Reserved kadm error (46)"),
+ N_("Reserved kadm error (47)"),
+ N_("Reserved kadm error (48)"),
+ N_("Reserved kadm error (49)"),
+ N_("Reserved kadm error (50)"),
+ N_("Reserved kadm error (51)"),
+ N_("Reserved kadm error (52)"),
+ N_("Reserved kadm error (53)"),
+ N_("Reserved kadm error (54)"),
+ N_("Reserved kadm error (55)"),
+ N_("Reserved kadm error (56)"),
+ N_("Reserved kadm error (57)"),
+ N_("Reserved kadm error (58)"),
+ N_("Reserved kadm error (59)"),
+ N_("Reserved kadm error (60)"),
+ N_("Reserved kadm error (61)"),
+ N_("Reserved kadm error (62)"),
+ N_("Reserved kadm error (63)"),
+ N_("Null passwords are not allowed"),
+ N_("Password is too short"),
+ N_("Too few character classes in password"),
+ N_("Password is in the password dictionary"),
+ 0
+};
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_kadm_error_table = { text, -1783126272L, 68 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_kadm_error_table_r(struct et_list **list);
+void initialize_kadm_error_table(void);
+
+void initialize_kadm_error_table(void) {
+ initialize_kadm_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_kadm_error_table_r(struct et_list **list)
+{
+ struct et_list *et, **end;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == text)
+ return;
+ et = malloc(sizeof(struct et_list));
+ if (et == 0) {
+ if (!link.table)
+ et = &link;
+ else
+ return;
+ }
+ et->table = &et_kadm_error_table;
+ et->next = 0;
+ *end = et;
+}
diff --git a/lib/et/test_cases/heimdal2.et b/lib/et/test_cases/heimdal2.et
new file mode 100644
index 0000000..703600c
--- /dev/null
+++ b/lib/et/test_cases/heimdal2.et
@@ -0,0 +1,65 @@
+# $Id: kadm_err.et,v 1.5 1998/01/16 23:11:27 joda Exp $
+#
+# Copyright 1988 by the Massachusetts Institute of Technology.
+#
+# For copying and distribution information, please see the file
+# <mit-copyright.h>.
+#
+# Kerberos administration server error table
+#
+ et kadm
+
+# KADM_SUCCESS, as all success codes should be, is zero
+
+ec KADM_RCSID, "$Id: kadm_err.et,v 1.5 1998/01/16 23:11:27 joda Exp $"
+# /* Building and unbuilding the packet errors */
+ec KADM_NO_REALM, "Cannot fetch local realm"
+ec KADM_NO_CRED, "Unable to fetch credentials"
+ec KADM_BAD_KEY, "Bad key supplied"
+ec KADM_NO_ENCRYPT, "Can't encrypt data"
+ec KADM_NO_AUTH, "Cannot encode/decode authentication info"
+ec KADM_WRONG_REALM, "Principal attempting change is in wrong realm"
+ec KADM_NO_ROOM, "Packet is too large"
+ec KADM_BAD_VER, "Version number is incorrect"
+ec KADM_BAD_CHK, "Checksum does not match"
+ec KADM_NO_READ, "Unsealing private data failed"
+ec KADM_NO_OPCODE, "Unsupported operation"
+ec KADM_NO_HOST, "Could not find administrating host"
+ec KADM_UNK_HOST, "Administrating host name is unknown"
+ec KADM_NO_SERV, "Could not find service name in services database"
+ec KADM_NO_SOCK, "Could not create socket"
+ec KADM_NO_CONN, "Could not connect to server"
+ec KADM_NO_HERE, "Could not fetch local socket address"
+ec KADM_NO_MAST, "Could not fetch master key"
+ec KADM_NO_VERI, "Could not verify master key"
+
+# /* From the server side routines */
+ec KADM_INUSE, "Entry already exists in database"
+ec KADM_UK_SERROR, "Database store error"
+ec KADM_UK_RERROR, "Database read error"
+ec KADM_UNAUTH, "Insufficient access to perform requested operation"
+# KADM_DATA isn't really an error, but...
+ec KADM_DATA, "Data is available for return to client"
+ec KADM_NOENTRY, "No such entry in the database"
+
+ec KADM_NOMEM, "Memory exhausted"
+ec KADM_NO_HOSTNAME, "Could not fetch system hostname"
+ec KADM_NO_BIND, "Could not bind port"
+ec KADM_LENGTH_ERROR, "Length mismatch problem"
+ec KADM_ILL_WILDCARD, "Illegal use of wildcard"
+
+ec KADM_DB_INUSE, "Database is locked or in use--try again later"
+
+ec KADM_INSECURE_PW, "Insecure password rejected"
+ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match"
+
+ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request"
+ec KADM_IMMUTABLE, "Attempt do delete immutable principal"
+# password quality basically stolen from OV libkadm5
+index 64
+prefix KADM_PASS_Q
+ec NULL, "Null passwords are not allowed"
+ec TOOSHORT, "Password is too short"
+ec CLASS, "Too few character classes in password"
+ec DICT, "Password is in the password dictionary"
+end
diff --git a/lib/et/test_cases/heimdal2.h b/lib/et/test_cases/heimdal2.h
new file mode 100644
index 0000000..3c2cb7d
--- /dev/null
+++ b/lib/et/test_cases/heimdal2.h
@@ -0,0 +1,58 @@
+/*
+ * heimdal2.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define KADM_RCSID (-1783126272L)
+#define KADM_NO_REALM (-1783126271L)
+#define KADM_NO_CRED (-1783126270L)
+#define KADM_BAD_KEY (-1783126269L)
+#define KADM_NO_ENCRYPT (-1783126268L)
+#define KADM_NO_AUTH (-1783126267L)
+#define KADM_WRONG_REALM (-1783126266L)
+#define KADM_NO_ROOM (-1783126265L)
+#define KADM_BAD_VER (-1783126264L)
+#define KADM_BAD_CHK (-1783126263L)
+#define KADM_NO_READ (-1783126262L)
+#define KADM_NO_OPCODE (-1783126261L)
+#define KADM_NO_HOST (-1783126260L)
+#define KADM_UNK_HOST (-1783126259L)
+#define KADM_NO_SERV (-1783126258L)
+#define KADM_NO_SOCK (-1783126257L)
+#define KADM_NO_CONN (-1783126256L)
+#define KADM_NO_HERE (-1783126255L)
+#define KADM_NO_MAST (-1783126254L)
+#define KADM_NO_VERI (-1783126253L)
+#define KADM_INUSE (-1783126252L)
+#define KADM_UK_SERROR (-1783126251L)
+#define KADM_UK_RERROR (-1783126250L)
+#define KADM_UNAUTH (-1783126249L)
+#define KADM_DATA (-1783126248L)
+#define KADM_NOENTRY (-1783126247L)
+#define KADM_NOMEM (-1783126246L)
+#define KADM_NO_HOSTNAME (-1783126245L)
+#define KADM_NO_BIND (-1783126244L)
+#define KADM_LENGTH_ERROR (-1783126243L)
+#define KADM_ILL_WILDCARD (-1783126242L)
+#define KADM_DB_INUSE (-1783126241L)
+#define KADM_INSECURE_PW (-1783126240L)
+#define KADM_PW_MISMATCH (-1783126239L)
+#define KADM_NOT_SERV_PRINC (-1783126238L)
+#define KADM_IMMUTABLE (-1783126237L)
+#define KADM_PASS_Q_NULL (-1783126208L)
+#define KADM_PASS_Q_TOOSHORT (-1783126207L)
+#define KADM_PASS_Q_CLASS (-1783126206L)
+#define KADM_PASS_Q_DICT (-1783126205L)
+extern const struct error_table et_kadm_error_table;
+extern void initialize_kadm_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_kadm_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_kadm (-1783126272L)
+
+/* for compatibility with older versions... */
+#define init_kadm_err_tbl initialize_kadm_error_table
+#define kadm_err_base ERROR_TABLE_BASE_kadm
diff --git a/lib/et/test_cases/heimdal3.c b/lib/et/test_cases/heimdal3.c
new file mode 100644
index 0000000..b8b9b73
--- /dev/null
+++ b/lib/et/test_cases/heimdal3.c
@@ -0,0 +1,56 @@
+/*
+ * heimdal3.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#define N_(a) a
+
+static const char * const text[] = {
+ N_("Test message 1"),
+ N_("Test message 2"),
+ 0
+};
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_h3test_error_table = { text, 43787520L, 2 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_h3test_error_table_r(struct et_list **list);
+void initialize_h3test_error_table(void);
+
+void initialize_h3test_error_table(void) {
+ initialize_h3test_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_h3test_error_table_r(struct et_list **list)
+{
+ struct et_list *et, **end;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == text)
+ return;
+ et = malloc(sizeof(struct et_list));
+ if (et == 0) {
+ if (!link.table)
+ et = &link;
+ else
+ return;
+ }
+ et->table = &et_h3test_error_table;
+ et->next = 0;
+ *end = et;
+}
diff --git a/lib/et/test_cases/heimdal3.et b/lib/et/test_cases/heimdal3.et
new file mode 100644
index 0000000..a0bd5c1
--- /dev/null
+++ b/lib/et/test_cases/heimdal3.et
@@ -0,0 +1,5 @@
+error_table ovk h3test
+prefix H3TEST
+ec TEST1, "Test message 1"
+ec TEST2, "Test message 2"
+end
diff --git a/lib/et/test_cases/heimdal3.h b/lib/et/test_cases/heimdal3.h
new file mode 100644
index 0000000..91d2cb9
--- /dev/null
+++ b/lib/et/test_cases/heimdal3.h
@@ -0,0 +1,20 @@
+/*
+ * heimdal3.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define H3TEST_TEST1 (43787520L)
+#define H3TEST_TEST2 (43787521L)
+extern const struct error_table et_h3test_error_table;
+extern void initialize_h3test_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_h3test_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_h3test (43787520L)
+
+/* for compatibility with older versions... */
+#define init_h3test_err_tbl initialize_h3test_error_table
+#define h3test_err_base ERROR_TABLE_BASE_h3test
diff --git a/lib/et/test_cases/imap_err.c b/lib/et/test_cases/imap_err.c
new file mode 100644
index 0000000..4d2ffc6
--- /dev/null
+++ b/lib/et/test_cases/imap_err.c
@@ -0,0 +1,115 @@
+/*
+ * imap_err.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#define N_(a) a
+
+static const char * const text[] = {
+ N_( "Internal Error"),
+ N_( "System I/O error"),
+ N_( "Item does not exist"),
+ N_( "Operating System Error"),
+ N_( "mail system storage has been exceeded"),
+ N_( "Permission denied"),
+ N_( "Over quota"),
+ N_( "Message size exceeds fixed limit"),
+ N_( "Too many user flags in mailbox"),
+ N_( "Invalid namespace prefix in configuration file"),
+ N_( "Mailbox has an invalid format"),
+ N_( "Replication inconsistency detected"),
+ N_( "Mailbox format corruption detected"),
+ N_( "Operation is not supported on mailbox"),
+ N_( "Mailbox does not exist"),
+ N_( "Mailbox already exists"),
+ N_( "Invalid mailbox name"),
+ N_( "Invalid mailbox type"),
+ N_( "Mailbox has been moved to another server"),
+ N_( "Mailbox is currently reserved"),
+ N_( "Mailbox is locked"),
+ N_( "Delivery to mailbox is disabled"),
+ N_( "Unknown/invalid partition"),
+ N_( "Invalid identifier"),
+ N_( "Message contains NUL characters"),
+ N_( "Message contains bare newlines"),
+ N_( "Message contains non-ASCII characters in headers"),
+ N_( "Message contains invalid header"),
+ N_( "Message has no header/body separator"),
+ N_( "Quota root does not exist"),
+ N_( "Bad protocol"),
+ N_( "Syntax error in parameters"),
+ N_( "Invalid annotation entry"),
+ N_( "Invalid annotation attribute"),
+ N_( "Invalid annotation value"),
+ N_( "Bad URL"),
+ N_( "Zero-length message literal"),
+ N_( "Invalid server requested"),
+ N_( "Server(s) unavailable to complete operation"),
+ N_( "The remote Server(s) denied the operation"),
+ N_( "Retry operation"),
+ N_( "This mailbox hierarchy does not exist on a single backend server."),
+ N_( "The remote server does not support MULTIAPPEND"),
+ N_( "Unrecognized character set"),
+ N_( "Invalid user"),
+ N_( "Login incorrect"),
+ N_( "Anonymous login is not permitted"),
+ N_( "Unsupported quota resource"),
+ N_( "Authentication failed"),
+ N_( "Client cancelled authentication"),
+ N_( "Protocol error during authentication"),
+ N_( "Mailbox is over %s quota"),
+ N_( "Mailbox is at %d%% of %s quota"),
+ N_( "Message %d no longer exists"),
+ N_( "Unable to checkpoint \\Seen state"),
+ N_( "Unable to preserve \\Seen state"),
+ N_( "No matching messages"),
+ N_( "No matching annotations"),
+ N_( "[UNKNOWN-CTE] Can not process the binary data"),
+ N_( "LOGOUT received"),
+ N_( "Completed"),
+ 0
+};
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_imap_error_table = { text, -1904809472L, 61 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_imap_error_table_r(struct et_list **list);
+void initialize_imap_error_table(void);
+
+void initialize_imap_error_table(void) {
+ initialize_imap_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_imap_error_table_r(struct et_list **list)
+{
+ struct et_list *et, **end;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == text)
+ return;
+ et = malloc(sizeof(struct et_list));
+ if (et == 0) {
+ if (!link.table)
+ et = &link;
+ else
+ return;
+ }
+ et->table = &et_imap_error_table;
+ et->next = 0;
+ *end = et;
+}
diff --git a/lib/et/test_cases/imap_err.et b/lib/et/test_cases/imap_err.et
new file mode 100644
index 0000000..bfae243
--- /dev/null
+++ b/lib/et/test_cases/imap_err.et
@@ -0,0 +1,238 @@
+# imap_err.et -- Error codes for Cyrus IMAP server programs
+#
+# Copyright (c) 1994-2008 Carnegie Mellon University. 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. The name "Carnegie Mellon University" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For permission or any legal
+# details, please contact
+# Carnegie Mellon University
+# Center for Technology Transfer and Enterprise Creation
+# 4615 Forbes Avenue
+# Suite 302
+# Pittsburgh, PA 15213
+# (412) 268-7393, fax: (412) 268-7395
+# innovation@andrew.cmu.edu
+#
+# 4. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by Computing Services
+# at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+#
+# CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+error_table imap
+
+ec IMAP_INTERNAL,
+ "Internal Error"
+
+ec IMAP_IOERROR,
+ "System I/O error"
+
+ec IMAP_NOTFOUND,
+ "Item does not exist"
+
+ec IMAP_SYS_ERROR,
+ "Operating System Error"
+
+ec IMAP_NOSPACE,
+ "mail system storage has been exceeded"
+
+ec IMAP_PERMISSION_DENIED,
+ "Permission denied"
+
+ec IMAP_QUOTA_EXCEEDED,
+ "Over quota"
+
+ec IMAP_MESSAGE_TOO_LARGE,
+ "Message size exceeds fixed limit"
+
+ec IMAP_USERFLAG_EXHAUSTED,
+ "Too many user flags in mailbox"
+
+ec IMAP_NAMESPACE_BADPREFIX,
+ "Invalid namespace prefix in configuration file"
+
+ec IMAP_MAILBOX_BADFORMAT,
+ "Mailbox has an invalid format"
+
+ec IMAP_SYNC_CHECKSUM,
+ "Replication inconsistency detected"
+
+ec IMAP_MAILBOX_CHECKSUM,
+ "Mailbox format corruption detected"
+
+ec IMAP_MAILBOX_NOTSUPPORTED,
+ "Operation is not supported on mailbox"
+
+ec IMAP_MAILBOX_NONEXISTENT,
+ "Mailbox does not exist"
+
+ec IMAP_MAILBOX_EXISTS,
+ "Mailbox already exists"
+
+ec IMAP_MAILBOX_BADNAME,
+ "Invalid mailbox name"
+
+ec IMAP_MAILBOX_BADTYPE,
+ "Invalid mailbox type"
+
+ec IMAP_MAILBOX_MOVED,
+ "Mailbox has been moved to another server"
+
+ec IMAP_MAILBOX_RESERVED,
+ "Mailbox is currently reserved"
+
+ec IMAP_MAILBOX_LOCKED,
+ "Mailbox is locked"
+
+ec IMAP_MAILBOX_DISABLED,
+ "Delivery to mailbox is disabled"
+
+ec IMAP_PARTITION_UNKNOWN,
+ "Unknown/invalid partition"
+
+ec IMAP_INVALID_IDENTIFIER,
+ "Invalid identifier"
+
+ec IMAP_MESSAGE_CONTAINSNULL,
+ "Message contains NUL characters"
+
+ec IMAP_MESSAGE_CONTAINSNL,
+ "Message contains bare newlines"
+
+ec IMAP_MESSAGE_CONTAINS8BIT,
+ "Message contains non-ASCII characters in headers"
+
+ec IMAP_MESSAGE_BADHEADER,
+ "Message contains invalid header"
+
+ec IMAP_MESSAGE_NOBLANKLINE,
+ "Message has no header/body separator"
+
+ec IMAP_QUOTAROOT_NONEXISTENT,
+ "Quota root does not exist"
+
+ec IMAP_PROTOCOL_ERROR,
+ "Bad protocol"
+
+ec IMAP_PROTOCOL_BAD_PARAMETERS,
+ "Syntax error in parameters"
+
+ec IMAP_ANNOTATION_BADENTRY,
+ "Invalid annotation entry"
+
+ec IMAP_ANNOTATION_BADATTRIB,
+ "Invalid annotation attribute"
+
+ec IMAP_ANNOTATION_BADVALUE,
+ "Invalid annotation value"
+
+ec IMAP_BADURL,
+ "Bad URL"
+
+ec IMAP_ZERO_LENGTH_LITERAL,
+ "Zero-length message literal"
+
+# following used only proxy/db operations
+ec IMAP_BAD_SERVER,
+ "Invalid server requested"
+
+ec IMAP_SERVER_UNAVAILABLE,
+ "Server(s) unavailable to complete operation"
+
+ec IMAP_REMOTE_DENIED,
+ "The remote Server(s) denied the operation"
+
+ec IMAP_AGAIN,
+ "Retry operation"
+
+ec IMAP_NOT_SINGULAR_ROOT,
+ "This mailbox hierarchy does not exist on a single backend server."
+
+ec IMAP_REMOTE_NO_MULTIAPPEND,
+ "The remote server does not support MULTIAPPEND"
+
+# Following only used for internationalization of error messages
+
+ec IMAP_UNRECOGNIZED_CHARSET,
+ "Unrecognized character set"
+
+ec IMAP_INVALID_USER,
+ "Invalid user"
+
+ec IMAP_INVALID_LOGIN,
+ "Login incorrect"
+
+ec IMAP_ANONYMOUS_NOT_PERMITTED,
+ "Anonymous login is not permitted"
+
+ec IMAP_UNSUPPORTED_QUOTA,
+ "Unsupported quota resource"
+
+# Following used only for SASL operations
+
+ec IMAP_SASL_FAIL,
+ "Authentication failed"
+
+ec IMAP_SASL_CANCEL,
+ "Client cancelled authentication"
+
+ec IMAP_SASL_PROTERR,
+ "Protocol error during authentication"
+
+# Following used for internationalization of untagged OK/NO responses
+
+ec IMAP_NO_OVERQUOTA,
+ "Mailbox is over %s quota"
+
+ec IMAP_NO_CLOSEQUOTA,
+ "Mailbox is at %d%% of %s quota"
+
+ec IMAP_NO_MSGGONE,
+ "Message %d no longer exists"
+
+ec IMAP_NO_CHECKSEEN,
+ "Unable to checkpoint \\Seen state"
+
+ec IMAP_NO_CHECKPRESERVE,
+ "Unable to preserve \\Seen state"
+
+ec IMAP_NO_NOSUCHMSG,
+ "No matching messages"
+
+ec IMAP_NO_NOSUCHANNOTATION,
+ "No matching annotations"
+
+ec IMAP_NO_UNKNOWN_CTE,
+ "[UNKNOWN-CTE] Can not process the binary data"
+
+# Following used for internationalization of untagged BYE response
+
+ec IMAP_BYE_LOGOUT,
+ "LOGOUT received"
+
+# Following used for internationalization of tagged OK response
+
+ec IMAP_OK_COMPLETED,
+ "Completed"
+
+end
diff --git a/lib/et/test_cases/imap_err.h b/lib/et/test_cases/imap_err.h
new file mode 100644
index 0000000..bfa8f65
--- /dev/null
+++ b/lib/et/test_cases/imap_err.h
@@ -0,0 +1,79 @@
+/*
+ * imap_err.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define IMAP_INTERNAL (-1904809472L)
+#define IMAP_IOERROR (-1904809471L)
+#define IMAP_NOTFOUND (-1904809470L)
+#define IMAP_SYS_ERROR (-1904809469L)
+#define IMAP_NOSPACE (-1904809468L)
+#define IMAP_PERMISSION_DENIED (-1904809467L)
+#define IMAP_QUOTA_EXCEEDED (-1904809466L)
+#define IMAP_MESSAGE_TOO_LARGE (-1904809465L)
+#define IMAP_USERFLAG_EXHAUSTED (-1904809464L)
+#define IMAP_NAMESPACE_BADPREFIX (-1904809463L)
+#define IMAP_MAILBOX_BADFORMAT (-1904809462L)
+#define IMAP_SYNC_CHECKSUM (-1904809461L)
+#define IMAP_MAILBOX_CHECKSUM (-1904809460L)
+#define IMAP_MAILBOX_NOTSUPPORTED (-1904809459L)
+#define IMAP_MAILBOX_NONEXISTENT (-1904809458L)
+#define IMAP_MAILBOX_EXISTS (-1904809457L)
+#define IMAP_MAILBOX_BADNAME (-1904809456L)
+#define IMAP_MAILBOX_BADTYPE (-1904809455L)
+#define IMAP_MAILBOX_MOVED (-1904809454L)
+#define IMAP_MAILBOX_RESERVED (-1904809453L)
+#define IMAP_MAILBOX_LOCKED (-1904809452L)
+#define IMAP_MAILBOX_DISABLED (-1904809451L)
+#define IMAP_PARTITION_UNKNOWN (-1904809450L)
+#define IMAP_INVALID_IDENTIFIER (-1904809449L)
+#define IMAP_MESSAGE_CONTAINSNULL (-1904809448L)
+#define IMAP_MESSAGE_CONTAINSNL (-1904809447L)
+#define IMAP_MESSAGE_CONTAINS8BIT (-1904809446L)
+#define IMAP_MESSAGE_BADHEADER (-1904809445L)
+#define IMAP_MESSAGE_NOBLANKLINE (-1904809444L)
+#define IMAP_QUOTAROOT_NONEXISTENT (-1904809443L)
+#define IMAP_PROTOCOL_ERROR (-1904809442L)
+#define IMAP_PROTOCOL_BAD_PARAMETERS (-1904809441L)
+#define IMAP_ANNOTATION_BADENTRY (-1904809440L)
+#define IMAP_ANNOTATION_BADATTRIB (-1904809439L)
+#define IMAP_ANNOTATION_BADVALUE (-1904809438L)
+#define IMAP_BADURL (-1904809437L)
+#define IMAP_ZERO_LENGTH_LITERAL (-1904809436L)
+#define IMAP_BAD_SERVER (-1904809435L)
+#define IMAP_SERVER_UNAVAILABLE (-1904809434L)
+#define IMAP_REMOTE_DENIED (-1904809433L)
+#define IMAP_AGAIN (-1904809432L)
+#define IMAP_NOT_SINGULAR_ROOT (-1904809431L)
+#define IMAP_REMOTE_NO_MULTIAPPEND (-1904809430L)
+#define IMAP_UNRECOGNIZED_CHARSET (-1904809429L)
+#define IMAP_INVALID_USER (-1904809428L)
+#define IMAP_INVALID_LOGIN (-1904809427L)
+#define IMAP_ANONYMOUS_NOT_PERMITTED (-1904809426L)
+#define IMAP_UNSUPPORTED_QUOTA (-1904809425L)
+#define IMAP_SASL_FAIL (-1904809424L)
+#define IMAP_SASL_CANCEL (-1904809423L)
+#define IMAP_SASL_PROTERR (-1904809422L)
+#define IMAP_NO_OVERQUOTA (-1904809421L)
+#define IMAP_NO_CLOSEQUOTA (-1904809420L)
+#define IMAP_NO_MSGGONE (-1904809419L)
+#define IMAP_NO_CHECKSEEN (-1904809418L)
+#define IMAP_NO_CHECKPRESERVE (-1904809417L)
+#define IMAP_NO_NOSUCHMSG (-1904809416L)
+#define IMAP_NO_NOSUCHANNOTATION (-1904809415L)
+#define IMAP_NO_UNKNOWN_CTE (-1904809414L)
+#define IMAP_BYE_LOGOUT (-1904809413L)
+#define IMAP_OK_COMPLETED (-1904809412L)
+extern const struct error_table et_imap_error_table;
+extern void initialize_imap_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_imap_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_imap (-1904809472L)
+
+/* for compatibility with older versions... */
+#define init_imap_err_tbl initialize_imap_error_table
+#define imap_err_base ERROR_TABLE_BASE_imap
diff --git a/lib/et/test_cases/simple.c b/lib/et/test_cases/simple.c
new file mode 100644
index 0000000..10ab1a3
--- /dev/null
+++ b/lib/et/test_cases/simple.c
@@ -0,0 +1,76 @@
+/*
+ * simple.c:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <stdlib.h>
+
+#define N_(a) a
+
+static const char * const text[] = {
+ N_( "Can't read ticket file"),
+ N_( "Can't find ticket or TGT"),
+ N_( "TGT expired"),
+ N_( "Can't decode authenticator"),
+ N_( "Ticket expired"),
+ N_( "Repeated request"),
+ N_( "The ticket isn't for us"),
+ N_( "Request is inconsistent"),
+ N_( "Delta-T too big"),
+ N_( "Incorrect net address"),
+ N_( "Protocol version mismatch"),
+ N_( "Invalid message type"),
+ N_( "Message stream modified"),
+ N_( "Message out of order"),
+ N_( "Unauthorized request"),
+ N_( "Current password is null"),
+ N_( "Incorrect current password"),
+ N_( "Protocol error"),
+ N_( "Error returned by KDC"),
+ N_( "Null ticket returned by KDC"),
+ N_( "Retry count exceeded"),
+ N_( "Can't send request"),
+ 0
+};
+
+struct error_table {
+ char const * const * msgs;
+ long base;
+ int n_msgs;
+};
+struct et_list {
+ struct et_list *next;
+ const struct error_table * table;
+};
+extern struct et_list *_et_list;
+
+const struct error_table et_krb_error_table = { text, 39525376L, 22 };
+
+static struct et_list link = { 0, 0 };
+
+void initialize_krb_error_table_r(struct et_list **list);
+void initialize_krb_error_table(void);
+
+void initialize_krb_error_table(void) {
+ initialize_krb_error_table_r(&_et_list);
+}
+
+/* For Heimdal compatibility */
+void initialize_krb_error_table_r(struct et_list **list)
+{
+ struct et_list *et, **end;
+
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
+ if (et->table->msgs == text)
+ return;
+ et = malloc(sizeof(struct et_list));
+ if (et == 0) {
+ if (!link.table)
+ et = &link;
+ else
+ return;
+ }
+ et->table = &et_krb_error_table;
+ et->next = 0;
+ *end = et;
+}
diff --git a/lib/et/test_cases/simple.et b/lib/et/test_cases/simple.et
new file mode 100644
index 0000000..4c7b77f
--- /dev/null
+++ b/lib/et/test_cases/simple.et
@@ -0,0 +1,69 @@
+ error_table krb
+
+ error_code KRB_MK_AP_TKFIL,
+ "Can't read ticket file"
+
+ ec KRB_MK_AP_NOTKT,
+ "Can't find ticket or TGT"
+
+ ec KRB_MK_AP_TGTEXP,
+ "TGT expired"
+
+ ec KRB_RD_AP_UNDEC,
+ "Can't decode authenticator"
+
+ ec KRB_RD_AP_EXP,
+ "Ticket expired"
+
+ ec KRB_RD_AP_REPEAT,
+ "Repeated request"
+
+ ec KRB_RD_AP_NOT_US,
+ "The ticket isn't for us"
+
+ ec KRB_RD_AP_INCON,
+ "Request is inconsistent"
+
+ ec KRB_RD_AP_TIME,
+ "Delta-T too big"
+
+ ec KRB_RD_AP_BADD,
+ "Incorrect net address"
+
+ ec KRB_RD_AP_VERSION,
+ "Protocol version mismatch"
+
+ ec KRB_RD_AP_MSG_TYPE,
+ "Invalid message type"
+
+ ec KRB_RD_AP_MODIFIED,
+ "Message stream modified"
+
+ ec KRB_RD_AP_ORDER,
+ "Message out of order"
+
+ ec KRB_RD_AP_UNAUTHOR,
+ "Unauthorized request"
+
+ ec KRB_GT_PW_NULL,
+ "Current password is null"
+
+ ec KRB_GT_PW_BADPW,
+ "Incorrect current password"
+
+ ec KRB_GT_PW_PROT,
+ "Protocol error"
+
+ ec KRB_GT_PW_KDCERR,
+ "Error returned by KDC"
+
+ ec KRB_GT_PW_NULLTKT,
+ "Null ticket returned by KDC"
+
+ ec KRB_SKDC_RETRY,
+ "Retry count exceeded"
+
+ ec KRB_SKDC_CANT,
+ "Can't send request"
+
+ end
diff --git a/lib/et/test_cases/simple.h b/lib/et/test_cases/simple.h
new file mode 100644
index 0000000..e7800b7
--- /dev/null
+++ b/lib/et/test_cases/simple.h
@@ -0,0 +1,40 @@
+/*
+ * simple.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define KRB_MK_AP_TKFIL (39525376L)
+#define KRB_MK_AP_NOTKT (39525377L)
+#define KRB_MK_AP_TGTEXP (39525378L)
+#define KRB_RD_AP_UNDEC (39525379L)
+#define KRB_RD_AP_EXP (39525380L)
+#define KRB_RD_AP_REPEAT (39525381L)
+#define KRB_RD_AP_NOT_US (39525382L)
+#define KRB_RD_AP_INCON (39525383L)
+#define KRB_RD_AP_TIME (39525384L)
+#define KRB_RD_AP_BADD (39525385L)
+#define KRB_RD_AP_VERSION (39525386L)
+#define KRB_RD_AP_MSG_TYPE (39525387L)
+#define KRB_RD_AP_MODIFIED (39525388L)
+#define KRB_RD_AP_ORDER (39525389L)
+#define KRB_RD_AP_UNAUTHOR (39525390L)
+#define KRB_GT_PW_NULL (39525391L)
+#define KRB_GT_PW_BADPW (39525392L)
+#define KRB_GT_PW_PROT (39525393L)
+#define KRB_GT_PW_KDCERR (39525394L)
+#define KRB_GT_PW_NULLTKT (39525395L)
+#define KRB_SKDC_RETRY (39525396L)
+#define KRB_SKDC_CANT (39525397L)
+extern const struct error_table et_krb_error_table;
+extern void initialize_krb_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_krb_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_krb (39525376L)
+
+/* for compatibility with older versions... */
+#define init_krb_err_tbl initialize_krb_error_table
+#define krb_err_base ERROR_TABLE_BASE_krb
diff --git a/lib/et/texinfo.tex b/lib/et/texinfo.tex
new file mode 100644
index 0000000..dd52615
--- /dev/null
+++ b/lib/et/texinfo.tex
@@ -0,0 +1,7226 @@
+% texinfo.tex -- TeX macros to handle Texinfo files.
+%
+% Load plain if necessary, i.e., if running under initex.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+%
+\def\texinfoversion{2006-02-13.16}
+%
+% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995,
+% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free
+% Software Foundation, Inc.
+%
+% This texinfo.tex file is free software; you can redistribute it and/or
+% modify it under the terms of the GNU General Public License as
+% published by the Free Software Foundation; either version 2, or (at
+% your option) any later version.
+%
+% This texinfo.tex 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 texinfo.tex file; see the file COPYING. If not, write
+% to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+% Boston, MA 02110-1301, USA.
+%
+% As a special exception, when this file is read by TeX when processing
+% a Texinfo source document, you may use the result without
+% restriction. (This has been our intent since Texinfo was invented.)
+%
+% Please try the latest version of texinfo.tex before submitting bug
+% reports; you can get the latest version from:
+% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or
+% ftp://tug.org/tex/texinfo.tex
+% (and all CTAN mirrors, see http://www.ctan.org).
+% The texinfo.tex in any given distribution could well be out
+% of date, so if that's what you're using, please check.
+%
+% Send bug reports to bug-texinfo@gnu.org. Please include including a
+% complete document in each bug report with which we can reproduce the
+% problem. Patches are, of course, greatly appreciated.
+%
+% To process a Texinfo manual with TeX, it's most reliable to use the
+% texi2dvi shell script that comes with the distribution. For a simple
+% manual foo.texi, however, you can get away with this:
+% tex foo.texi
+% texindex foo.??
+% tex foo.texi
+% tex foo.texi
+% dvips foo.dvi -o # or whatever; this makes foo.ps.
+% The extra TeX runs get the cross-reference information correct.
+% Sometimes one run after texindex suffices, and sometimes you need more
+% than two; texi2dvi does it as many times as necessary.
+%
+% It is possible to adapt texinfo.tex for other languages, to some
+% extent. You can get the existing language-specific files from the
+% full Texinfo distribution.
+%
+% The GNU Texinfo home page is http://www.gnu.org/software/texinfo.
+
+
+\message{Loading texinfo [version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}%
+ \catcode`+=\active \catcode`\_=\active}
+
+\message{Basics,}
+\chardef\other=12
+
+% We never want plain's \outer definition of \+ in Texinfo.
+% For @tex, we can use \tabalign.
+\let\+ = \relax
+
+% Save some plain tex macros whose names we will redefine.
+\let\ptexb=\b
+\let\ptexbullet=\bullet
+\let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv=\equiv
+\let\ptexexclam=\!
+\let\ptexfootnote=\footnote
+\let\ptexgtr=>
+\let\ptexhat=^
+\let\ptexi=\i
+\let\ptexindent=\indent
+\let\ptexinsert=\insert
+\let\ptexlbrace=\{
+\let\ptexless=<
+\let\ptexnewwrite\newwrite
+\let\ptexnoindent=\noindent
+\let\ptexplus=+
+\let\ptexrbrace=\}
+\let\ptexslash=\/
+\let\ptexstar=\*
+\let\ptext=\t
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+ \let\linenumber = \empty % Pre-3.0.
+\else
+ \def\linenumber{l.\the\inputlineno:\space}
+\fi
+
+% Set up fixed words for English if not already set.
+\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi
+\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi
+\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi
+\ifx\putwordin\undefined \gdef\putwordin{in}\fi
+\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi
+\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi
+\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi
+\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi
+\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi
+\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi
+\ifx\putwordof\undefined \gdef\putwordof{of}\fi
+\ifx\putwordon\undefined \gdef\putwordon{on}\fi
+\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi
+\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi
+\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi
+\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi
+\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi
+\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi
+\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi
+%
+\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi
+\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi
+\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi
+\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi
+\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi
+\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi
+\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi
+\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi
+\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi
+\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi
+\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi
+\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi
+%
+\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi
+\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi
+\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi
+\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi
+\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi
+
+% Since the category of space is not known, we have to be careful.
+\chardef\spacecat = 10
+\def\spaceisspace{\catcode`\ =\spacecat}
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+% The following is used inside several \edef's.
+\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname}
+
+% Hyphenation fixes.
+\hyphenation{
+ Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script
+ ap-pen-dix bit-map bit-maps
+ data-base data-bases eshell fall-ing half-way long-est man-u-script
+ man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm
+ par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces
+ spell-ing spell-ings
+ stand-alone strong-est time-stamp time-stamps which-ever white-space
+ wide-spread wrap-around
+}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen\bindingoffset
+\newdimen\normaloffset
+\newdimen\pagewidth \newdimen\pageheight
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+% @| inserts a changebar to the left of the current line. It should
+% surround any changed text. This approach does *not* work if the
+% change spans more than two lines of output. To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+ % \vadjust can only be used in horizontal mode.
+ \leavevmode
+ %
+ % Append this vertical mode material after the current line in the output.
+ \vadjust{%
+ % We want to insert a rule with the height and depth of the current
+ % leading; that is exactly what \strutbox is supposed to record.
+ \vskip-\baselineskip
+ %
+ % \vadjust-items are inserted at the left edge of the type. So
+ % the \llap here moves out into the left-hand margin.
+ \llap{%
+ %
+ % For a thicker or thinner bar, change the `1pt'.
+ \vrule height\baselineskip width1pt
+ %
+ % This is the space between the bar and the text.
+ \hskip 12pt
+ }%
+ }%
+}
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal. We don't just call \tracingall here,
+% since that produces some useless output on the terminal. We also make
+% some effort to order the tracing commands to reduce output in the log
+% file; cf. trace.sty in LaTeX.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{%
+ \tracingstats2
+ \tracingpages1
+ \tracinglostchars2 % 2 gives us more in etex
+ \tracingparagraphs1
+ \tracingoutput1
+ \tracingmacros2
+ \tracingrestores1
+ \showboxbreadth\maxdimen \showboxdepth\maxdimen
+ \ifx\eTeXversion\undefined\else % etex gives us more logging
+ \tracingscantokens1
+ \tracingifs1
+ \tracinggroups1
+ \tracingnesting2
+ \tracingassigns1
+ \fi
+ \tracingcommands3 % 3 gives us more in etex
+ \errorcontextlines16
+}%
+
+% add check for \lastpenalty to plain's definitions. If the last thing
+% we did was a \nobreak, we don't want to insert more space.
+%
+\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount
+ \removelastskip\penalty-50\smallskip\fi\fi}
+\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount
+ \removelastskip\penalty-100\medskip\fi\fi}
+\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount
+ \removelastskip\penalty-200\bigskip\fi\fi}
+
+% For @cropmarks command.
+% Do @cropmarks to get crop marks.
+%
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
+%
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
+\newdimen\cornerlong \cornerlong=1pc
+\newdimen\cornerthick \cornerthick=.3pt
+\newdimen\topandbottommargin \topandbottommargin=.75in
+
+% Main output routine.
+\chardef\PAGE = 255
+\output = {\onepageout{\pagecontents\PAGE}}
+
+\newbox\headlinebox
+\newbox\footlinebox
+
+% \onepageout takes a vbox as an argument. Note that \pagecontents
+% does insertions, but you have to call it yourself.
+\def\onepageout#1{%
+ \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+ %
+ \ifodd\pageno \advance\hoffset by \bindingoffset
+ \else \advance\hoffset by -\bindingoffset\fi
+ %
+ % Do this outside of the \shipout so @code etc. will be expanded in
+ % the headline as they should be, not taken literally (outputting ''code).
+ \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}%
+ \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}%
+ %
+ {%
+ % Have to do this stuff outside the \shipout because we want it to
+ % take effect in \write's, yet the group defined by the \vbox ends
+ % before the \shipout runs.
+ %
+ \indexdummies % don't expand commands in the output.
+ \normalturnoffactive % \ in index entries must not stay \, e.g., if
+ % the page break happens to be in the middle of an example.
+ % We don't want .vr (or whatever) entries like this:
+ % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}}
+ % "\acronym" won't work when it's read back in;
+ % it needs to be
+ % {\code {{\tt \backslashcurfont }acronym}
+ \shipout\vbox{%
+ % Do this early so pdf references go to the beginning of the page.
+ \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi
+ %
+ \ifcropmarks \vbox to \outervsize\bgroup
+ \hsize = \outerhsize
+ \vskip-\topandbottommargin
+ \vtop to0pt{%
+ \line{\ewtop\hfil\ewtop}%
+ \nointerlineskip
+ \line{%
+ \vbox{\moveleft\cornerthick\nstop}%
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}%
+ }%
+ \vss}%
+ \vskip\topandbottommargin
+ \line\bgroup
+ \hfil % center the page within the outer (page) hsize.
+ \ifodd\pageno\hskip\bindingoffset\fi
+ \vbox\bgroup
+ \fi
+ %
+ \unvbox\headlinebox
+ \pagebody{#1}%
+ \ifdim\ht\footlinebox > 0pt
+ % Only leave this space if the footline is nonempty.
+ % (We lessened \vsize for it in \oddfootingxxx.)
+ % The \baselineskip=24pt in plain's \makefootline has no effect.
+ \vskip 2\baselineskip
+ \unvbox\footlinebox
+ \fi
+ %
+ \ifcropmarks
+ \egroup % end of \vbox\bgroup
+ \hfil\egroup % end of (centering) \line\bgroup
+ \vskip\topandbottommargin plus1fill minus1fill
+ \boxmaxdepth = \cornerthick
+ \vbox to0pt{\vss
+ \line{%
+ \vbox{\moveleft\cornerthick\nsbot}%
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}%
+ }%
+ \nointerlineskip
+ \line{\ewbot\hfil\ewbot}%
+ }%
+ \egroup % \vbox from first cropmarks clause
+ \fi
+ }% end of \shipout\vbox
+ }% end of group with \indexdummies
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+ \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1 \unvbox#1
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+% Here are the rules for the cropmarks. Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+ {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+ {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1. The argument is the rest of
+% the input line (except we remove a trailing comment). #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+%
+\def\parsearg{\parseargusing{}}
+\def\parseargusing#1#2{%
+ \def\next{#2}%
+ \begingroup
+ \obeylines
+ \spaceisspace
+ #1%
+ \parseargline\empty% Insert the \empty token, see \finishparsearg below.
+}
+
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ \argremovecomment #1\comment\ArgTerm%
+ }%
+}
+
+% First remove any @comment, then any @c comment.
+\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm}
+\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm}
+
+% Each occurrence of `\^^M' or `<space>\^^M' is replaced by a single space.
+%
+% \argremovec might leave us with trailing space, e.g.,
+% @end itemize @c foo
+% This space token undergoes the same procedure and is eventually removed
+% by \finishparsearg.
+%
+\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M}
+\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M}
+\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{%
+ \def\temp{#3}%
+ \ifx\temp\empty
+ % We cannot use \next here, as it holds the macro to run;
+ % thus we reuse \temp.
+ \let\temp\finishparsearg
+ \else
+ \let\temp\argcheckspaces
+ \fi
+ % Put the space token in:
+ \temp#1 #3\ArgTerm
+}
+
+% If a _delimited_ argument is enclosed in braces, they get stripped; so
+% to get _exactly_ the rest of the line, we had to prevent such situation.
+% We prepended an \empty token at the very beginning and we expand it now,
+% just before passing the control to \next.
+% (Similarly, we have to think about #3 of \argcheckspacesY above: it is
+% either the null string, or it ends with \^^M---thus there is no danger
+% that a pair of braces would be stripped.
+%
+% But first, we have to remove the trailing space token.
+%
+\def\finishparsearg#1 \ArgTerm{\expandafter\next\expandafter{#1}}
+
+% \parseargdef\foo{...}
+% is roughly equivalent to
+% \def\foo{\parsearg\Xfoo}
+% \def\Xfoo#1{...}
+%
+% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my
+% favourite TeX trick. --kasal, 16nov03
+
+\def\parseargdef#1{%
+ \expandafter \doparseargdef \csname\string#1\endcsname #1%
+}
+\def\doparseargdef#1#2{%
+ \def#2{\parsearg#1}%
+ \def#1##1%
+}
+
+% Several utility definitions with active space:
+{
+ \obeyspaces
+ \gdef\obeyedspace{ }
+
+ % Make each space character in the input produce a normal interword
+ % space in the output. Don't allow a line break at this space, as this
+ % is used only in environments like @example, where each line of input
+ % should produce a line of output anyway.
+ %
+ \gdef\sepspaces{\obeyspaces\let =\tie}
+
+ % If an index command is used in an @example environment, any spaces
+ % therein should become regular spaces in the raw index file, not the
+ % expansion of \tie (\leavevmode \penalty \@M \ ).
+ \gdef\unsepspaces{\let =\space}
+}
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+% Define the framework for environments in texinfo.tex. It's used like this:
+%
+% \envdef\foo{...}
+% \def\Efoo{...}
+%
+% It's the responsibility of \envdef to insert \begingroup before the
+% actual body; @end closes the group after calling \Efoo. \envdef also
+% defines \thisenv, so the current environment is known; @end checks
+% whether the environment name matches. The \checkenv macro can also be
+% used to check whether the current environment is the one expected.
+%
+% Non-false conditionals (@iftex, @ifset) don't fit into this, so they
+% are not treated as environments; they don't open a group. (The
+% implementation of @end takes care not to call \endgroup in this
+% special case.)
+
+
+% At runtime, environments start with this:
+\def\startenvironment#1{\begingroup\def\thisenv{#1}}
+% initialize
+\let\thisenv\empty
+
+% ... but they get defined via ``\envdef\foo{...}'':
+\long\def\envdef#1#2{\def#1{\startenvironment#1#2}}
+\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}}
+
+% Check whether we're in the right environment:
+\def\checkenv#1{%
+ \def\temp{#1}%
+ \ifx\thisenv\temp
+ \else
+ \badenverr
+ \fi
+}
+
+% Environment mismatch, #1 expected:
+\def\badenverr{%
+ \errhelp = \EMsimple
+ \errmessage{This command can appear only \inenvironment\temp,
+ not \inenvironment\thisenv}%
+}
+\def\inenvironment#1{%
+ \ifx#1\empty
+ out of any environment%
+ \else
+ in environment \expandafter\string#1%
+ \fi
+}
+
+% @end foo executes the definition of \Efoo.
+% But first, it executes a specialized version of \checkenv
+%
+\parseargdef\end{%
+ \if 1\csname iscond.#1\endcsname
+ \else
+ % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03
+ \expandafter\checkenv\csname#1\endcsname
+ \csname E#1\endcsname
+ \endgroup
+ \fi
+}
+
+\newhelp\EMsimple{Press RETURN to continue.}
+
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\tt\char64}}
+
+% This is turned off because it was never documented
+% and you can use @w{...} around a quote to suppress ligatures.
+%% Define @` and @' to be the same as ` and '
+%% but suppressing ligatures.
+%\def\`{{`}}
+%\def\'{{'}}
+
+% Used to generate quoted braces.
+\def\mylbrace {{\tt\char123}}
+\def\myrbrace {{\tt\char125}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+\begingroup
+ % Definitions to produce \{ and \} commands for indices,
+ % and @{ and @} for the aux/toc files.
+ \catcode`\{ = \other \catcode`\} = \other
+ \catcode`\[ = 1 \catcode`\] = 2
+ \catcode`\! = 0 \catcode`\\ = \other
+ !gdef!lbracecmd[\{]%
+ !gdef!rbracecmd[\}]%
+ !gdef!lbraceatcmd[@{]%
+ !gdef!rbraceatcmd[@}]%
+!endgroup
+
+% @comma{} to avoid , parsing problems.
+\let\comma = ,
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H.
+\let\, = \c
+\let\dotaccent = \.
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \t
+\let\ubaraccent = \b
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown @ordf @ordm
+% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}}
+\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+ \def\temp{#1}%
+ \ifx\temp\imacro \ptexi
+ \else\ifx\temp\jmacro \j
+ \else \errmessage{@dotless can be used only with i or j}%
+ \fi\fi
+}
+
+% The \TeX{} logo, as in plain, but resetting the spacing so that a
+% period following counts as ending a sentence. (Idea found in latex.)
+%
+\edef\TeX{\TeX \spacefactor=1000 }
+
+% @LaTeX{} logo. Not quite the same results as the definition in
+% latex.ltx, since we use a different font for the raised A; it's most
+% convenient for us to use an explicitly smaller font, rather than using
+% the \scriptstyle font (since we don't reset \scriptstyle and
+% \scriptscriptstyle).
+%
+\def\LaTeX{%
+ L\kern-.36em
+ {\setbox0=\hbox{T}%
+ \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}%
+ \kern-.15em
+ \TeX
+}
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break\hbox{}\ignorespaces}
+
+% @/ allows a line break.
+\let\/=\allowbreak
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=\endofsentencespacefactor\space}
+
+% @! is an end-of-sentence bang.
+\def\!{!\spacefactor=\endofsentencespacefactor\space}
+
+% @? is an end-of-sentence query.
+\def\?{?\spacefactor=\endofsentencespacefactor\space}
+
+% @frenchspacing on|off says whether to put extra space after punctuation.
+%
+\def\onword{on}
+\def\offword{off}
+%
+\parseargdef\frenchspacing{%
+ \def\temp{#1}%
+ \ifx\temp\onword \plainfrenchspacing
+ \else\ifx\temp\offword \plainnonfrenchspacing
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @frenchspacing option `\temp', must be on/off}%
+ \fi\fi
+}
+
+% @w prevents a word break. Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox. We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line. According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0). If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+% Another complication is that the group might be very large. This can
+% cause the glue on the previous page to be unduly stretched, because it
+% does not have much material. In this case, it's better to add an
+% explicit \vfill so that the extra space is at the bottom. The
+% threshold for doing this is if the group is more than \vfilllimit
+% percent of a page (\vfilllimit can be changed inside of @tex).
+%
+\newbox\groupbox
+\def\vfilllimit{0.7}
+%
+\envdef\group{%
+ \ifnum\catcode`\^^M=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ \startsavinginserts
+ %
+ \setbox\groupbox = \vtop\bgroup
+ % Do @comment since we are called inside an environment such as
+ % @example, where each end-of-line in the input causes an
+ % end-of-line in the output. We don't want the end-of-line after
+ % the `@group' to put extra space in the output. Since @group
+ % should appear on a line by itself (according to the Texinfo
+ % manual), we don't worry about eating any user text.
+ \comment
+}
+%
+% The \vtop produces a box with normal height and large depth; thus, TeX puts
+% \baselineskip glue before it, and (when the next line of text is done)
+% \lineskip glue after it. Thus, space below is not quite equal to space
+% above. But it's pretty close.
+\def\Egroup{%
+ % To get correct interline space between the last line of the group
+ % and the first line afterwards, we have to propagate \prevdepth.
+ \endgraf % Not \par, as it may have been set to \lisppar.
+ \global\dimen1 = \prevdepth
+ \egroup % End the \vtop.
+ % \dimen0 is the vertical size of the group's box.
+ \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox
+ % \dimen2 is how much space is left on the page (more or less).
+ \dimen2 = \pageheight \advance\dimen2 by -\pagetotal
+ % if the group doesn't fit on the current page, and it's a big big
+ % group, force a page break.
+ \ifdim \dimen0 > \dimen2
+ \ifdim \pagetotal < \vfilllimit\pageheight
+ \page
+ \fi
+ \fi
+ \box\groupbox
+ \prevdepth = \dimen1
+ \checkinserts
+}
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil \mil=0.001in
+
+% Old definition--didn't work.
+%\parseargdef\need{\par %
+%% This method tries to make TeX break the page naturally
+%% if the depth of the box does not fit.
+%{\baselineskip=0pt%
+%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak
+%\prevdepth=-1000pt
+%}}
+
+\parseargdef\need{%
+ % Ensure vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % If the @need value is less than one line space, it's useless.
+ \dimen0 = #1\mil
+ \dimen2 = \ht\strutbox
+ \advance\dimen2 by \dp\strutbox
+ \ifdim\dimen0 > \dimen2
+ %
+ % Do a \strut just to make the height of this box be normal, so the
+ % normal leading is inserted relative to the preceding line.
+ % And a page break here is fine.
+ \vtop to #1\mil{\strut\vfil}%
+ %
+ % TeX does not even consider page breaks if a penalty added to the
+ % main vertical list is 10000 or more. But in order to see if the
+ % empty box we just added fits on the page, we must make it consider
+ % page breaks. On the other hand, we don't want to actually break the
+ % page after the empty box. So we use a penalty of 9999.
+ %
+ % There is an extremely small chance that TeX will actually break the
+ % page at this \penalty, if there are no other feasible breakpoints in
+ % sight. (If the user is using lots of big @group commands, which
+ % almost-but-not-quite fill up a page, TeX will have a hard time doing
+ % good page breaking, for example.) However, I could not construct an
+ % example where a page broke at this \penalty; if it happens in a real
+ % document, then we can reconsider our strategy.
+ \penalty9999
+ %
+ % Back up by the size of the box, whether we did a page break or not.
+ \kern -#1\mil
+ %
+ % Do not allow a page break right after this kern.
+ \nobreak
+ \fi
+}
+
+% @br forces paragraph break (and is undocumented).
+
+\let\br = \par
+
+% @page forces the start of a new page.
+%
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}
+
+% This defn is used inside nofill environments such as @example.
+\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount
+ \leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current
+% paragraph. For more general purposes, use the \margin insertion
+% class. WHICH is `l' or `r'.
+%
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+%
+\def\doinmargin#1#2{\strut\vadjust{%
+ \nobreak
+ \kern-\strutdepth
+ \vtop to \strutdepth{%
+ \baselineskip=\strutdepth
+ \vss
+ % if you have multiple lines of stuff to put here, you'll need to
+ % make the vbox yourself of the appropriate size.
+ \ifx#1l%
+ \llap{\ignorespaces #2\hskip\inmarginspacing}%
+ \else
+ \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}%
+ \fi
+ \null
+ }%
+}}
+\def\inleftmargin{\doinmargin l}
+\def\inrightmargin{\doinmargin r}
+%
+% @inmargin{TEXT [, RIGHT-TEXT]}
+% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right;
+% else use TEXT for both).
+%
+\def\inmargin#1{\parseinmargin #1,,\finish}
+\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing.
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \def\lefttext{#1}% have both texts
+ \def\righttext{#2}%
+ \else
+ \def\lefttext{#1}% have only one text
+ \def\righttext{#1}%
+ \fi
+ %
+ \ifodd\pageno
+ \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin
+ \else
+ \def\temp{\inleftmargin\lefttext}%
+ \fi
+ \temp
+}
+
+% @include file insert text of that file as input.
+%
+\def\include{\parseargusing\filenamecatcodes\includezzz}
+\def\includezzz#1{%
+ \pushthisfilestack
+ \def\thisfile{#1}%
+ {%
+ \makevalueexpandable
+ \def\temp{\input #1 }%
+ \expandafter
+ }\temp
+ \popthisfilestack
+}
+\def\filenamecatcodes{%
+ \catcode`\\=\other
+ \catcode`~=\other
+ \catcode`^=\other
+ \catcode`_=\other
+ \catcode`|=\other
+ \catcode`<=\other
+ \catcode`>=\other
+ \catcode`+=\other
+ \catcode`-=\other
+}
+
+\def\pushthisfilestack{%
+ \expandafter\pushthisfilestackX\popthisfilestack\StackTerm
+}
+\def\pushthisfilestackX{%
+ \expandafter\pushthisfilestackY\thisfile\StackTerm
+}
+\def\pushthisfilestackY #1\StackTerm #2\StackTerm {%
+ \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}%
+}
+
+\def\popthisfilestack{\errthisfilestackempty}
+\def\errthisfilestackempty{\errmessage{Internal error:
+ the stack of filenames is empty.}}
+
+\def\thisfile{}
+
+% @center line
+% outputs that line, centered.
+%
+\parseargdef\center{%
+ \ifhmode
+ \let\next\centerH
+ \else
+ \let\next\centerV
+ \fi
+ \next{\hfil \ignorespaces#1\unskip \hfil}%
+}
+\def\centerH#1{%
+ {%
+ \hfil\break
+ \advance\hsize by -\leftskip
+ \advance\hsize by -\rightskip
+ \line{#1}%
+ \break
+ }%
+}
+\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}}
+
+% @sp n outputs n lines of vertical space
+
+\parseargdef\sp{\vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+\def\comment{\begingroup \catcode`\^^M=\other%
+\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other%
+\commentxxx}
+{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}}
+
+\let\c=\comment
+
+% @paragraphindent NCHARS
+% We'll use ems for NCHARS, close enough.
+% NCHARS can also be the word `asis' or `none'.
+% We cannot feasibly implement @paragraphindent asis, though.
+%
+\def\asisword{asis} % no translation, these are keywords
+\def\noneword{none}
+%
+\parseargdef\paragraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \defaultparindent = 0pt
+ \else
+ \defaultparindent = #1em
+ \fi
+ \fi
+ \parindent = \defaultparindent
+}
+
+% @exampleindent NCHARS
+% We'll use ems for NCHARS like @paragraphindent.
+% It seems @exampleindent asis isn't necessary, but
+% I preserve it to make it similar to @paragraphindent.
+\parseargdef\exampleindent{%
+ \def\temp{#1}%
+ \ifx\temp\asisword
+ \else
+ \ifx\temp\noneword
+ \lispnarrowing = 0pt
+ \else
+ \lispnarrowing = #1em
+ \fi
+ \fi
+}
+
+% @firstparagraphindent WORD
+% If WORD is `none', then suppress indentation of the first paragraph
+% after a section heading. If WORD is `insert', then do indent at such
+% paragraphs.
+%
+% The paragraph indentation is suppressed or not by calling
+% \suppressfirstparagraphindent, which the sectioning commands do.
+% We switch the definition of this back and forth according to WORD.
+% By default, we suppress indentation.
+%
+\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent}
+\def\insertword{insert}
+%
+\parseargdef\firstparagraphindent{%
+ \def\temp{#1}%
+ \ifx\temp\noneword
+ \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent
+ \else\ifx\temp\insertword
+ \let\suppressfirstparagraphindent = \relax
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @firstparagraphindent option `\temp'}%
+ \fi\fi
+}
+
+% Here is how we actually suppress indentation. Redefine \everypar to
+% \kern backwards by \parindent, and then reset itself to empty.
+%
+% We also make \indent itself not actually do anything until the next
+% paragraph.
+%
+\gdef\dosuppressfirstparagraphindent{%
+ \gdef\indent{%
+ \restorefirstparagraphindent
+ \indent
+ }%
+ \gdef\noindent{%
+ \restorefirstparagraphindent
+ \noindent
+ }%
+ \global\everypar = {%
+ \kern -\parindent
+ \restorefirstparagraphindent
+ }%
+}
+
+\gdef\restorefirstparagraphindent{%
+ \global \let \indent = \ptexindent
+ \global \let \noindent = \ptexnoindent
+ \global \everypar = {}%
+}
+
+
+% @asis just yields its argument. Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math outputs its argument in math mode.
+%
+% One complication: _ usually means subscripts, but it could also mean
+% an actual _ character, as in @math{@var{some_variable} + 1}. So make
+% _ active, and distinguish by seeing if the current family is \slfam,
+% which is what @var uses.
+{
+ \catcode`\_ = \active
+ \gdef\mathunderscore{%
+ \catcode`\_=\active
+ \def_{\ifnum\fam=\slfam \_\else\sb\fi}%
+ }
+}
+% Another complication: we want \\ (and @\) to output a \ character.
+% FYI, plain.tex uses \\ as a temporary control sequence (why?), but
+% this is not advertised and we don't care. Texinfo does not
+% otherwise define @\.
+%
+% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\.
+\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi}
+%
+\def\math{%
+ \tex
+ \mathunderscore
+ \let\\ = \mathbackslash
+ \mathactive
+ $\finishmath
+}
+\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex.
+
+% Some active characters (such as <) are spaced differently in math.
+% We have to reset their definitions in case the @math was an argument
+% to a command which sets the catcodes (such as @item or @section).
+%
+{
+ \catcode`^ = \active
+ \catcode`< = \active
+ \catcode`> = \active
+ \catcode`+ = \active
+ \gdef\mathactive{%
+ \let^ = \ptexhat
+ \let< = \ptexless
+ \let> = \ptexgtr
+ \let+ = \ptexplus
+ }
+}
+
+% @bullet and @minus need the same treatment as @math, just above.
+\def\bullet{$\ptexbullet$}
+\def\minus{$-$}
+
+% @dots{} outputs an ellipsis using the current font.
+% We do .5em per period so that it has the same spacing in a typewriter
+% font as three actual period characters.
+%
+\def\dots{%
+ \leavevmode
+ \hbox to 1.5em{%
+ \hskip 0pt plus 0.25fil
+ .\hfil.\hfil.%
+ \hskip 0pt plus 0.5fil
+ }%
+}
+
+% @enddots{} is an end-of-sentence ellipsis.
+%
+\def\enddots{%
+ \dots
+ \spacefactor=\endofsentencespacefactor
+}
+
+% @comma{} is so commas can be inserted into text without messing up
+% Texinfo's parsing.
+%
+\let\comma = ,
+
+% @refill is a no-op.
+\let\refill=\relax
+
+% If working on a large document in chapters, it is convenient to
+% be able to disable indexing, cross-referencing, and contents, for test runs.
+% This is done with @novalidate (before @setfilename).
+%
+\newif\iflinks \linkstrue % by default we want the aux files.
+\let\novalidate = \linksfalse
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \iflinks
+ \tryauxfile
+ % Open the new aux file. TeX will close it automatically at exit.
+ \immediate\openout\auxfile=\jobname.aux
+ \fi % \openindices needs to do some work in any case.
+ \openindices
+ \let\setfilename=\comment % Ignore extra @setfilename cmds.
+ %
+ % If texinfo.cnf is present on the system, read it.
+ % Useful for site-wide @afourpaper, etc.
+ \openin 1 texinfo.cnf
+ \ifeof 1 \else \input texinfo.cnf \fi
+ \closein 1
+ %
+ \comment % Ignore the actual filename.
+}
+
+% Called from \setfilename.
+%
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+
+\message{pdf,}
+% adobe `portable' document format
+\newcount\tempnum
+\newcount\lnkcount
+\newtoks\filename
+\newcount\filenamelength
+\newcount\pgn
+\newtoks\toksA
+\newtoks\toksB
+\newtoks\toksC
+\newtoks\toksD
+\newbox\boxA
+\newcount\countA
+\newif\ifpdf
+\newif\ifpdfmakepagedest
+
+% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1
+% can be set). So we test for \relax and 0 as well as \undefined,
+% borrowed from ifpdf.sty.
+\ifx\pdfoutput\undefined
+\else
+ \ifx\pdfoutput\relax
+ \else
+ \ifcase\pdfoutput
+ \else
+ \pdftrue
+ \fi
+ \fi
+\fi
+
+% PDF uses PostScript string constants for the names of xref targets,
+% for display in the outlines, and in other places. Thus, we have to
+% double any backslashes. Otherwise, a name like "\node" will be
+% interpreted as a newline (\n), followed by o, d, e. Not good.
+% http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html
+% (and related messages, the final outcome is that it is up to the TeX
+% user to double the backslashes and otherwise make the string valid, so
+% that's what we do).
+
+% double active backslashes.
+%
+{\catcode`\@=0 \catcode`\\=\active
+ @gdef@activebackslashdouble{%
+ @catcode`@\=@active
+ @let\=@doublebackslash}
+}
+
+% To handle parens, we must adopt a different approach, since parens are
+% not active characters. hyperref.dtx (which has the same problem as
+% us) handles it with this amazing macro to replace tokens. I've
+% tinkered with it a little for texinfo, but it's definitely from there.
+%
+% #1 is the tokens to replace.
+% #2 is the replacement.
+% #3 is the control sequence with the string.
+%
+\def\HyPsdSubst#1#2#3{%
+ \def\HyPsdReplace##1#1##2\END{%
+ ##1%
+ \ifx\\##2\\%
+ \else
+ #2%
+ \HyReturnAfterFi{%
+ \HyPsdReplace##2\END
+ }%
+ \fi
+ }%
+ \xdef#3{\expandafter\HyPsdReplace#3#1\END}%
+}
+\long\def\HyReturnAfterFi#1\fi{\fi#1}
+
+% #1 is a control sequence in which to do the replacements.
+\def\backslashparens#1{%
+ \xdef#1{#1}% redefine it as its expansion; the definition is simply
+ % \lastnode when called from \setref -> \pdfmkdest.
+ \HyPsdSubst{(}{\realbackslash(}{#1}%
+ \HyPsdSubst{)}{\realbackslash)}{#1}%
+}
+
+\ifpdf
+ \input pdfcolor
+ \pdfcatalog{/PageMode /UseOutlines}%
+ \def\dopdfimage#1#2#3{%
+ \def\imagewidth{#2}%
+ \def\imageheight{#3}%
+ % without \immediate, pdftex seg faults when the same image is
+ % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.)
+ \ifnum\pdftexversion < 14
+ \immediate\pdfimage
+ \else
+ \immediate\pdfximage
+ \fi
+ \ifx\empty\imagewidth\else width \imagewidth \fi
+ \ifx\empty\imageheight\else height \imageheight \fi
+ \ifnum\pdftexversion<13
+ #1.pdf%
+ \else
+ {#1.pdf}%
+ \fi
+ \ifnum\pdftexversion < 14 \else
+ \pdfrefximage \pdflastximage
+ \fi}
+ \def\pdfmkdest#1{{%
+ % We have to set dummies so commands such as @code, and characters
+ % such as \, aren't expanded when present in a section title.
+ \atdummies
+ \activebackslashdouble
+ \def\pdfdestname{#1}%
+ \backslashparens\pdfdestname
+ \pdfdest name{\pdfdestname} xyz%
+ }}%
+ %
+ % used to mark target names; must be expandable.
+ \def\pdfmkpgn#1{#1}%
+ %
+ \let\linkcolor = \Blue % was Cyan, but that seems light?
+ \def\endlink{\Black\pdfendlink}
+ % Adding outlines to PDF; macros for calculating structure of outlines
+ % come from Petr Olsak
+ \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+ \else \csname#1\endcsname \fi}
+ \def\advancenumber#1{\tempnum=\expnumber{#1}\relax
+ \advance\tempnum by 1
+ \expandafter\xdef\csname#1\endcsname{\the\tempnum}}
+ %
+ % #1 is the section text, which is what will be displayed in the
+ % outline by the pdf viewer. #2 is the pdf expression for the number
+ % of subentries (or empty, for subsubsections). #3 is the node text,
+ % which might be empty if this toc entry had no corresponding node.
+ % #4 is the page number
+ %
+ \def\dopdfoutline#1#2#3#4{%
+ % Generate a link to the node text if that exists; else, use the
+ % page number. We could generate a destination for the section
+ % text in the case where a section has no node, but it doesn't
+ % seem worth the trouble, since most documents are normally structured.
+ \def\pdfoutlinedest{#3}%
+ \ifx\pdfoutlinedest\empty
+ \def\pdfoutlinedest{#4}%
+ \else
+ % Doubled backslashes in the name.
+ {\activebackslashdouble \xdef\pdfoutlinedest{#3}%
+ \backslashparens\pdfoutlinedest}%
+ \fi
+ %
+ % Also double the backslashes in the display string.
+ {\activebackslashdouble \xdef\pdfoutlinetext{#1}%
+ \backslashparens\pdfoutlinetext}%
+ %
+ \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}%
+ }
+ %
+ \def\pdfmakeoutlines{%
+ \begingroup
+ % Thanh's hack / proper braces in bookmarks
+ \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace
+ \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace
+ %
+ % Read toc silently, to get counts of subentries for \pdfoutline.
+ \def\numchapentry##1##2##3##4{%
+ \def\thischapnum{##2}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsecentry##1##2##3##4{%
+ \advancenumber{chap\thischapnum}%
+ \def\thissecnum{##2}%
+ \def\thissubsecnum{0}%
+ }%
+ \def\numsubsecentry##1##2##3##4{%
+ \advancenumber{sec\thissecnum}%
+ \def\thissubsecnum{##2}%
+ }%
+ \def\numsubsubsecentry##1##2##3##4{%
+ \advancenumber{subsec\thissubsecnum}%
+ }%
+ \def\thischapnum{0}%
+ \def\thissecnum{0}%
+ \def\thissubsecnum{0}%
+ %
+ % use \def rather than \let here because we redefine \chapentry et
+ % al. a second time, below.
+ \def\appentry{\numchapentry}%
+ \def\appsecentry{\numsecentry}%
+ \def\appsubsecentry{\numsubsecentry}%
+ \def\appsubsubsecentry{\numsubsubsecentry}%
+ \def\unnchapentry{\numchapentry}%
+ \def\unnsecentry{\numsecentry}%
+ \def\unnsubsecentry{\numsubsecentry}%
+ \def\unnsubsubsecentry{\numsubsubsecentry}%
+ \readdatafile{toc}%
+ %
+ % Read toc second time, this time actually producing the outlines.
+ % The `-' means take the \expnumber as the absolute number of
+ % subentries, which we calculated on our first read of the .toc above.
+ %
+ % We use the node names as the destinations.
+ \def\numchapentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}%
+ \def\numsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}%
+ \def\numsubsecentry##1##2##3##4{%
+ \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}%
+ \def\numsubsubsecentry##1##2##3##4{% count is always zero
+ \dopdfoutline{##1}{}{##3}{##4}}%
+ %
+ % PDF outlines are displayed using system fonts, instead of
+ % document fonts. Therefore we cannot use special characters,
+ % since the encoding is unknown. For example, the eogonek from
+ % Latin 2 (0xea) gets translated to a | character. Info from
+ % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100.
+ %
+ % xx to do this right, we have to translate 8-bit characters to
+ % their "best" equivalent, based on the @documentencoding. Right
+ % now, I guess we'll just let the pdf reader have its way.
+ \indexnofonts
+ \setupdatafile
+ \catcode`\\=\active \otherbackslash
+ \input \jobname.toc
+ \endgroup
+ }
+ %
+ \def\skipspaces#1{\def\PP{#1}\def\D{|}%
+ \ifx\PP\D\let\nextsp\relax
+ \else\let\nextsp\skipspaces
+ \ifx\p\space\else\addtokens{\filename}{\PP}%
+ \advance\filenamelength by 1
+ \fi
+ \fi
+ \nextsp}
+ \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax}
+ \ifnum\pdftexversion < 14
+ \let \startlink \pdfannotlink
+ \else
+ \let \startlink \pdfstartlink
+ \fi
+ % make a live url in pdf output.
+ \def\pdfurl#1{%
+ \begingroup
+ % it seems we really need yet another set of dummies; have not
+ % tried to figure out what each command should do in the context
+ % of @url. for now, just make @/ a no-op, that's the only one
+ % people have actually reported a problem with.
+ %
+ \normalturnoffactive
+ \def\@{@}%
+ \let\/=\empty
+ \makevalueexpandable
+ \leavevmode\Red
+ \startlink attr{/Border [0 0 0]}%
+ user{/Subtype /Link /A << /S /URI /URI (#1) >>}%
+ \endgroup}
+ \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}}
+ \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+ \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks}
+ \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}}
+ \def\maketoks{%
+ \expandafter\poptoks\the\toksA|ENDTOKS|\relax
+ \ifx\first0\adn0
+ \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3
+ \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6
+ \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9
+ \else
+ \ifnum0=\countA\else\makelink\fi
+ \ifx\first.\let\next=\done\else
+ \let\next=\maketoks
+ \addtokens{\toksB}{\the\toksD}
+ \ifx\first,\addtokens{\toksB}{\space}\fi
+ \fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \next}
+ \def\makelink{\addtokens{\toksB}%
+ {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0}
+ \def\pdflink#1{%
+ \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}}
+ \linkcolor #1\endlink}
+ \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\else
+ \let\pdfmkdest = \gobble
+ \let\pdfurl = \gobble
+ \let\endlink = \relax
+ \let\linkcolor = \relax
+ \let\pdfmakeoutlines = \relax
+\fi % \ifx\pdfoutput
+
+
+\message{fonts,}
+
+% Change the current font style to #1, remembering it in \curfontstyle.
+% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in
+% italics, not bold italics.
+%
+\def\setfontstyle#1{%
+ \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd.
+ \csname ten#1\endcsname % change the current font
+}
+
+% Select #1 fonts with the current style.
+%
+\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname}
+
+\def\rm{\fam=0 \setfontstyle{rm}}
+\def\it{\fam=\itfam \setfontstyle{it}}
+\def\sl{\fam=\slfam \setfontstyle{sl}}
+\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf}
+\def\tt{\fam=\ttfam \setfontstyle{tt}}
+
+% Texinfo sort of supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf.
+\newfam\sffam
+\def\sf{\fam=\sffam \setfontstyle{sf}}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this font style.
+\def\ttsl{\setfontstyle{ttsl}}
+
+% Default leading.
+\newdimen\textleading \textleading = 13.2pt
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly. There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+\def\setleading#1{%
+ \normalbaselineskip = #1\relax
+ \normallineskip = \lineskipfactor\normalbaselineskip
+ \normalbaselines
+ \setbox\strutbox =\hbox{%
+ \vrule width0pt height\strutheightpercent\baselineskip
+ depth \strutdepthpercent \baselineskip
+ }%
+}
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+% #3 is the font's design size, #4 is a scale factor
+\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4}
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx} %where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+% Text fonts (11.2pt, magstep1).
+\def\textnominalsize{11pt}
+\edef\mainmagstep{\magstephalf}
+\setfont\textrm\rmshape{10}{\mainmagstep}
+\setfont\texttt\ttshape{10}{\mainmagstep}
+\setfont\textbf\bfshape{10}{\mainmagstep}
+\setfont\textit\itshape{10}{\mainmagstep}
+\setfont\textsl\slshape{10}{\mainmagstep}
+\setfont\textsf\sfshape{10}{\mainmagstep}
+\setfont\textsc\scshape{10}{\mainmagstep}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+
+% A few fonts for @defun names and args.
+\setfont\defbf\bfshape{10}{\magstep1}
+\setfont\deftt\ttshape{10}{\magstep1}
+\setfont\defttsl\ttslshape{10}{\magstep1}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf}
+
+% Fonts for indices, footnotes, small examples (9pt).
+\def\smallnominalsize{9pt}
+\setfont\smallrm\rmshape{9}{1000}
+\setfont\smalltt\ttshape{9}{1000}
+\setfont\smallbf\bfshape{10}{900}
+\setfont\smallit\itshape{9}{1000}
+\setfont\smallsl\slshape{9}{1000}
+\setfont\smallsf\sfshape{9}{1000}
+\setfont\smallsc\scshape{10}{900}
+\setfont\smallttsl\ttslshape{10}{900}
+\font\smalli=cmmi9
+\font\smallsy=cmsy9
+
+% Fonts for small examples (8pt).
+\def\smallernominalsize{8pt}
+\setfont\smallerrm\rmshape{8}{1000}
+\setfont\smallertt\ttshape{8}{1000}
+\setfont\smallerbf\bfshape{10}{800}
+\setfont\smallerit\itshape{8}{1000}
+\setfont\smallersl\slshape{8}{1000}
+\setfont\smallersf\sfshape{8}{1000}
+\setfont\smallersc\scshape{10}{800}
+\setfont\smallerttsl\ttslshape{10}{800}
+\font\smalleri=cmmi8
+\font\smallersy=cmsy8
+
+% Fonts for title page (20.4pt):
+\def\titlenominalsize{20pt}
+\setfont\titlerm\rmbshape{12}{\magstep3}
+\setfont\titleit\itbshape{10}{\magstep4}
+\setfont\titlesl\slbshape{10}{\magstep4}
+\setfont\titlett\ttbshape{12}{\magstep3}
+\setfont\titlettsl\ttslshape{10}{\magstep4}
+\setfont\titlesf\sfbshape{17}{\magstep1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\authorrm{\secrm}
+\def\authortt{\sectt}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\def\chapnominalsize{17pt}
+\setfont\chaprm\rmbshape{12}{\magstep2}
+\setfont\chapit\itbshape{10}{\magstep3}
+\setfont\chapsl\slbshape{10}{\magstep3}
+\setfont\chaptt\ttbshape{12}{\magstep2}
+\setfont\chapttsl\ttslshape{10}{\magstep3}
+\setfont\chapsf\sfbshape{17}{1000}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+
+% Section fonts (14.4pt).
+\def\secnominalsize{14pt}
+\setfont\secrm\rmbshape{12}{\magstep1}
+\setfont\secit\itbshape{10}{\magstep2}
+\setfont\secsl\slbshape{10}{\magstep2}
+\setfont\sectt\ttbshape{12}{\magstep1}
+\setfont\secttsl\ttslshape{10}{\magstep2}
+\setfont\secsf\sfbshape{12}{\magstep1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+
+% Subsection fonts (13.15pt).
+\def\ssecnominalsize{13pt}
+\setfont\ssecrm\rmbshape{12}{\magstephalf}
+\setfont\ssecit\itbshape{10}{1315}
+\setfont\ssecsl\slbshape{10}{1315}
+\setfont\ssectt\ttbshape{12}{\magstephalf}
+\setfont\ssecttsl\ttslshape{10}{1315}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{1315}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+
+% Reduced fonts for @acro in text (10pt).
+\def\reducednominalsize{10pt}
+\setfont\reducedrm\rmshape{10}{1000}
+\setfont\reducedtt\ttshape{10}{1000}
+\setfont\reducedbf\bfshape{10}{1000}
+\setfont\reducedit\itshape{10}{1000}
+\setfont\reducedsl\slshape{10}{1000}
+\setfont\reducedsf\sfshape{10}{1000}
+\setfont\reducedsc\scshape{10}{1000}
+\setfont\reducedttsl\ttslshape{10}{1000}
+\font\reducedi=cmmi10
+\font\reducedsy=cmsy10
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families. Since
+% texinfo doesn't allow for producing subscripts and superscripts except
+% in the main text, we don't bother to reset \scriptfont and
+% \scriptscriptfont (which would also require loading a lot more fonts).
+%
+\def\resetmathfonts{%
+ \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy
+ \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf
+ \textfont\ttfam=\tentt \textfont\sffam=\tensf
+}
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE. We do this because \STYLE needs to also set the
+% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire
+% \tenSTYLE to set the current font.
+%
+% Each font-changing command also sets the names \lsize (one size lower)
+% and \lllsize (three sizes lower). These relative commands are used in
+% the LaTeX logo and acronyms.
+%
+% This all needs generalizing, badly.
+%
+\def\textfonts{%
+ \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+ \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+ \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy
+ \let\tenttsl=\textttsl
+ \def\curfontsize{text}%
+ \def\lsize{reduced}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{\textleading}}
+\def\titlefonts{%
+ \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+ \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+ \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+ \let\tenttsl=\titlettsl
+ \def\curfontsize{title}%
+ \def\lsize{chap}\def\lllsize{subsec}%
+ \resetmathfonts \setleading{25pt}}
+\def\titlefont#1{{\titlefonts\rm #1}}
+\def\chapfonts{%
+ \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+ \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+ \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy
+ \let\tenttsl=\chapttsl
+ \def\curfontsize{chap}%
+ \def\lsize{sec}\def\lllsize{text}%
+ \resetmathfonts \setleading{19pt}}
+\def\secfonts{%
+ \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+ \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+ \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy
+ \let\tenttsl=\secttsl
+ \def\curfontsize{sec}%
+ \def\lsize{subsec}\def\lllsize{reduced}%
+ \resetmathfonts \setleading{16pt}}
+\def\subsecfonts{%
+ \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+ \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+ \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy
+ \let\tenttsl=\ssecttsl
+ \def\curfontsize{ssec}%
+ \def\lsize{text}\def\lllsize{small}%
+ \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts
+\def\reducedfonts{%
+ \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl
+ \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc
+ \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy
+ \let\tenttsl=\reducedttsl
+ \def\curfontsize{reduced}%
+ \def\lsize{small}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{10.5pt}}
+\def\smallfonts{%
+ \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl
+ \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc
+ \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy
+ \let\tenttsl=\smallttsl
+ \def\curfontsize{small}%
+ \def\lsize{smaller}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{10.5pt}}
+\def\smallerfonts{%
+ \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl
+ \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc
+ \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy
+ \let\tenttsl=\smallerttsl
+ \def\curfontsize{smaller}%
+ \def\lsize{smaller}\def\lllsize{smaller}%
+ \resetmathfonts \setleading{9.5pt}}
+
+% Set the fonts to use with the @small... environments.
+\let\smallexamplefonts = \smallfonts
+
+% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample
+% can fit this many characters:
+% 8.5x11=86 smallbook=72 a4=90 a5=69
+% If we use \scriptfonts (8pt), then we can fit this many characters:
+% 8.5x11=90+ smallbook=80 a4=90+ a5=77
+% For me, subjectively, the few extra characters that fit aren't worth
+% the additional smallness of 8pt. So I'm making the default 9pt.
+%
+% By the way, for comparison, here's what fits with @example (10pt):
+% 8.5x11=71 smallbook=60 a4=75 a5=58
+%
+% I wish the USA used A4 paper.
+% --karl, 24jan03.
+
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\textfonts \rm
+
+% Define these so they can be easily changed for other fonts.
+\def\angleleft{$\langle$}
+\def\angleright{$\rangle$}
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}
+\setfont\shortcontbf\bfshape{10}{\magstep1} % no cmb12
+\setfont\shortcontsl\slshape{12}{1000}
+\setfont\shortconttt\ttshape{12}{1000}
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
+% unless the following character is such as not to need one.
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else
+ \ptexslash\fi\fi\fi}
+\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx}
+\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally uses \ttsl.
+% @var is set to this for defun arguments.
+\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx}
+
+% like \smartslanted except unconditionally use \sl. We never want
+% ttsl for book titles, do we?
+\def\cite#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\slanted=\smartslanted
+\let\var=\smartslanted
+\let\dfn=\smartslanted
+\let\emph=\smartitalic
+
+% @b, explicit bold.
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% @sansserif, explicit sans.
+\def\sansserif#1{{\sf #1}}
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph. Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+% Set sfcode to normal for the chars that usually have another value.
+% Can't use plain's \frenchspacing because it uses the `\x notation, and
+% sometimes \x has an active definition that messes things up.
+%
+\chardef\colonChar = `\:
+\chardef\commaChar = `\,
+\chardef\dotChar = `\.
+\chardef\exclamChar= `\!
+\chardef\questChar = `\?
+\chardef\semiChar = `\;
+%
+\catcode`@=11
+ \def\plainfrenchspacing{%
+ \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m
+ \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m
+ \def\endofsentencespacefactor{1000}% for @. and friends
+ }
+ \def\plainnonfrenchspacing{%
+ \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000
+ \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250
+ \def\endofsentencespacefactor{3000}% for @. and friends
+ }
+\catcode`@=\other
+\def\endofsentencespacefactor{3000}% default
+
+\def\t#1{%
+ {\tt \rawbackslash \plainfrenchspacing #1}%
+ \null
+}
+\def\samp#1{`\tclose{#1}'\null}
+\setfont\keyrm\rmshape{8}{1000}
+\font\keysy=cmsy9
+\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{%
+ \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{%
+ \vbox{\hrule\kern-0.4pt
+ \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}%
+ \kern-0.4pt\hrule}%
+ \kern-.06em\raise0.4pt\hbox{\angleright}}}}
+% The old definition, with no lozenge:
+%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+% @file, @option are the same as @samp.
+\let\file=\samp
+\let\option=\samp
+
+% @code is a modification of @t,
+% which makes spaces the same size as normal in the surrounding text.
+\def\tclose#1{%
+ {%
+ % Change normal interword space to be same as for the current font.
+ \spaceskip = \fontdimen2\font
+ %
+ % Switch to typewriter.
+ \tt
+ %
+ % But `\ ' produces the large typewriter interword space.
+ \def\ {{\spaceskip = 0pt{} }}%
+ %
+ % Turn off hyphenation.
+ \nohyphenation
+ %
+ \rawbackslash
+ \plainfrenchspacing
+ #1%
+ }%
+ \null
+}
+
+% We *must* turn on hyphenation at `-' and `_' in @code.
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate at a dash.
+% -- rms.
+{
+ \catcode`\-=\active
+ \catcode`\_=\active
+ %
+ \global\def\code{\begingroup
+ \catcode`\-=\active \catcode`\_=\active
+ \ifallowcodebreaks
+ \let-\codedash
+ \let_\codeunder
+ \else
+ \let-\realdash
+ \let_\realunder
+ \fi
+ \codex
+ }
+}
+
+\def\realdash{-}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{%
+ % this is all so @math{@code{var_name}+1} can work. In math mode, _
+ % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.)
+ % will therefore expand the active definition of _, which is us
+ % (inside @code that is), therefore an endless loop.
+ \ifusingtt{\ifmmode
+ \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_.
+ \else\normalunderscore \fi
+ \discretionary{}{}{}}%
+ {\_}%
+}
+\def\codex #1{\tclose{#1}\endgroup}
+
+% An additional complication: the above will allow breaks after, e.g.,
+% each of the four underscores in __typeof__. This is undesirable in
+% some manuals, especially if they don't have long identifiers in
+% general. @allowcodebreaks provides a way to control this.
+%
+\newif\ifallowcodebreaks \allowcodebreakstrue
+
+\def\keywordtrue{true}
+\def\keywordfalse{false}
+
+\parseargdef\allowcodebreaks{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\keywordtrue
+ \allowcodebreakstrue
+ \else\ifx\txiarg\keywordfalse
+ \allowcodebreaksfalse
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @allowcodebreaks option `\txiarg'}%
+ \fi\fi
+}
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+% `example' (@kbd uses ttsl only inside of @example and friends),
+% or `code' (@kbd uses normal tty font always).
+\parseargdef\kbdinputstyle{%
+ \def\txiarg{#1}%
+ \ifx\txiarg\worddistinct
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+ \else\ifx\txiarg\wordexample
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+ \else\ifx\txiarg\wordcode
+ \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+ \else
+ \errhelp = \EMsimple
+ \errmessage{Unknown @kbdinputstyle option `\txiarg'}%
+ \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is `distinct.'
+\kbdinputstyle distinct
+
+\def\xkey{\key}
+\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
+\ifx\one\xkey\ifx\threex\three \key{#2}%
+\else{\tclose{\kbdfont\look}}\fi
+\else{\tclose{\kbdfont\look}}\fi}
+
+% For @indicateurl, @env, @command quotes seem unnecessary, so use \code.
+\let\indicateurl=\code
+\let\env=\code
+\let\command=\code
+
+% @uref (abbreviation for `urlref') takes an optional (comma-separated)
+% second argument specifying the text to display and an optional third
+% arg as text to display instead of (rather than in addition to) the url
+% itself. First (mandatory) arg is the url. Perhaps eventually put in
+% a hypertex \special here.
+%
+\def\uref#1{\douref #1,,,\finish}
+\def\douref#1,#2,#3,#4\finish{\begingroup
+ \unsepspaces
+ \pdfurl{#1}%
+ \setbox0 = \hbox{\ignorespaces #3}%
+ \ifdim\wd0 > 0pt
+ \unhbox0 % third arg given, show only that
+ \else
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \ifpdf
+ \unhbox0 % PDF: 2nd arg given, show only it
+ \else
+ \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url
+ \fi
+ \else
+ \code{#1}% only url given, so show it
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% @url synonym for @uref, since that's how everyone uses it.
+%
+\let\url=\uref
+
+% rms does not like angle brackets --karl, 17may97.
+% So now @email is just like @uref, unless we are pdf.
+%
+%\def\email#1{\angleleft{\tt #1}\angleright}
+\ifpdf
+ \def\email#1{\doemail#1,,\finish}
+ \def\doemail#1,#2,#3\finish{\begingroup
+ \unsepspaces
+ \pdfurl{mailto:#1}%
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi
+ \endlink
+ \endgroup}
+\else
+ \let\email=\uref
+\fi
+
+% Check if we are currently using a typewriter font. Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Typeset a dimension, e.g., `in' or `pt'. The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par}
+
+% @l was never documented to mean ``switch to the Lisp font'',
+% and it is not used as such in any manual I can find. We need it for
+% Polish suppressed-l. --karl, 22sep96.
+%\def\l#1{{\li #1}\null}
+
+% Explicit font changes: @r, @sc, undocumented @ii.
+\def\r#1{{\rm #1}} % roman font
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+% @acronym for "FBI", "NATO", and the like.
+% We print this one point size smaller, since it's intended for
+% all-uppercase.
+%
+\def\acronym#1{\doacronym #1,,\finish}
+\def\doacronym#1,#2,#3\finish{%
+ {\selectfonts\lsize #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+}
+
+% @abbr for "Comput. J." and the like.
+% No font change, but don't do end-of-sentence spacing.
+%
+\def\abbr#1{\doabbr #1,,\finish}
+\def\doabbr#1,#2,#3\finish{%
+ {\plainfrenchspacing #1}%
+ \def\temp{#2}%
+ \ifx\temp\empty \else
+ \space ({\unsepspaces \ignorespaces \temp \unskip})%
+ \fi
+}
+
+% @pounds{} is a sterling sign, which Knuth put in the CM italic font.
+%
+\def\pounds{{\it\$}}
+
+% @euro{} comes from a separate font, depending on the current style.
+% We use the free feym* fonts from the eurosym package by Henrik
+% Theiling, which support regular, slanted, bold and bold slanted (and
+% "outlined" (blackboard board, sort of) versions, which we don't need).
+% It is available from http://www.ctan.org/tex-archive/fonts/eurosym.
+%
+% Although only regular is the truly official Euro symbol, we ignore
+% that. The Euro is designed to be slightly taller than the regular
+% font height.
+%
+% feymr - regular
+% feymo - slanted
+% feybr - bold
+% feybo - bold slanted
+%
+% There is no good (free) typewriter version, to my knowledge.
+% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide.
+% Hmm.
+%
+% Also doesn't work in math. Do we need to do math with euro symbols?
+% Hope not.
+%
+%
+\def\euro{{\eurofont e}}
+\def\eurofont{%
+ % We set the font at each command, rather than predefining it in
+ % \textfonts and the other font-switching commands, so that
+ % installations which never need the symbol don't have to have the
+ % font installed.
+ %
+ % There is only one designed size (nominal 10pt), so we always scale
+ % that to the current nominal size.
+ %
+ % By the way, simply using "at 1em" works for cmr10 and the like, but
+ % does not work for cmbx10 and other extended/shrunken fonts.
+ %
+ \def\eurosize{\csname\curfontsize nominalsize\endcsname}%
+ %
+ \ifx\curfontstyle\bfstylename
+ % bold:
+ \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize
+ \else
+ % regular:
+ \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize
+ \fi
+ \thiseurofont
+}
+
+% @registeredsymbol - R in a circle. The font for the R should really
+% be smaller yet, but lllsize is the best we can do for now.
+% Adapted from the plain.tex definition of \copyright.
+%
+\def\registeredsymbol{%
+ $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}%
+ \hfil\crcr\Orb}}%
+ }$%
+}
+
+% Laurent Siebenmann reports \Orb undefined with:
+% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38
+% so we'll define it if necessary.
+%
+\ifx\Orb\undefined
+\def\Orb{\mathhexbox20D}
+\fi
+
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+% Do an implicit @contents or @shortcontents after @end titlepage if the
+% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage.
+%
+\newif\ifsetcontentsaftertitlepage
+ \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue
+\newif\ifsetshortcontentsaftertitlepage
+ \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue
+
+\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\envdef\titlepage{%
+ % Open one extra group, as we want to close it in the middle of \Etitlepage.
+ \begingroup
+ \parindent=0pt \textfonts
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+ \def\page{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ \let\page = \oldpage
+ \page
+ \null
+ }%
+}
+
+\def\Etitlepage{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ % It is important to do the page break before ending the group,
+ % because the headline and footline are only empty inside the group.
+ % If we use the new definition of \page, we always get a blank page
+ % after the title page, which we certainly don't want.
+ \oldpage
+ \endgroup
+ %
+ % Need this before the \...aftertitlepage checks so that if they are
+ % in effect the toc pages will come out with page numbers.
+ \HEADINGSon
+ %
+ % If they want short, they certainly want long too.
+ \ifsetshortcontentsaftertitlepage
+ \shortcontents
+ \contents
+ \global\let\shortcontents = \relax
+ \global\let\contents = \relax
+ \fi
+ %
+ \ifsetcontentsaftertitlepage
+ \contents
+ \global\let\contents = \relax
+ \global\let\shortcontents = \relax
+ \fi
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+%%% Macros to be used within @titlepage:
+
+\let\subtitlerm=\tenrm
+\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}
+
+\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines
+ \let\tt=\authortt}
+
+\parseargdef\title{%
+ \checkenv\titlepage
+ \leftline{\titlefonts\rm #1}
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt
+}
+
+\parseargdef\subtitle{%
+ \checkenv\titlepage
+ {\subtitlefont \rightline{#1}}%
+}
+
+% @author should come last, but may come many times.
+% It can also be used inside @quotation.
+%
+\parseargdef\author{%
+ \def\temp{\quotation}%
+ \ifx\thisenv\temp
+ \def\quotationauthor{#1}% printed in \Equotation.
+ \else
+ \checkenv\titlepage
+ \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi
+ {\authorfont \leftline{#1}}%
+ \fi
+}
+
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks\evenheadline % headline on even pages
+\newtoks\oddheadline % headline on odd pages
+\newtoks\evenfootline % footline on even pages
+\newtoks\oddfootline % footline on odd pages
+
+% Now make TeX use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+ \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+ \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish}
+\def\evenheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish}
+\def\oddheadingyyy #1\|#2\|#3\|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish}
+\def\evenfootingyyy #1\|#2\|#3\|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish}
+\def\oddfootingyyy #1\|#2\|#3\|#4\finish{%
+ \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+ %
+ % Leave some space for the footline. Hopefully ok to assume
+ % @evenfooting will not be used by itself.
+ \global\advance\pageheight by -\baselineskip
+ \global\advance\vsize by -\baselineskip
+}
+
+\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}}
+
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% @headings after turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{%
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+\let\contentsalignmacro = \chappager
+
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{%
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+
+% Subroutines used in generating headings
+% This produces Day Month Year style of output.
+% Only define if not already defined, in case a txi-??.tex file has set
+% up a different format (e.g., txi-cs.tex does this).
+\ifx\today\undefined
+\def\today{%
+ \number\day\space
+ \ifcase\month
+ \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr
+ \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug
+ \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec
+ \fi
+ \space\number\year}
+\fi
+
+% @settitle line... specifies the title of the document, for headings.
+% It generates no output of its own.
+\def\thistitle{\putwordNoTitle}
+\def\settitle{\parsearg{\gdef\thistitle}}
+
+
+\message{tables,}
+% Tables -- @table, @ftable, @vtable, @item(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @ftable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemindicate{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % If the item text does not fit in the space we have, put it on a line
+ % by itself, and do not allow a page break either before or after that
+ % line. We do not start a paragraph here because then if the next
+ % command is, e.g., @kindex, the whatsit would get put into the
+ % horizontal list on a line by itself, resulting in extra blank space.
+ \ifdim \wd0>\itemmax
+ %
+ % Make this a paragraph so we get the \parskip glue and wrapping,
+ % but leave it ragged-right.
+ \begingroup
+ \advance\leftskip by-\tableindent
+ \advance\hsize by\tableindent
+ \advance\rightskip by0pt plus1fil
+ \leavevmode\unhbox0\par
+ \endgroup
+ %
+ % We're going to be starting a paragraph, but we don't want the
+ % \parskip glue -- logically it's part of the @item we just started.
+ \nobreak \vskip-\parskip
+ %
+ % Stop a page break at the \parskip glue coming up. However, if
+ % what follows is an environment such as @example, there will be no
+ % \parskip glue; then the negative vskip we just inserted would
+ % cause the example and the item to crash together. So we use this
+ % bizarre value of 10001 as a signal to \aboveenvbreak to insert
+ % \parskip glue after all. Section titles are handled this way also.
+ %
+ \penalty 10001
+ \endgroup
+ \itemxneedsnegativevskipfalse
+ \else
+ % The item text fits into the space. Start a paragraph, so that the
+ % following text (if any) will end up on the same line.
+ \noindent
+ % Do this with kerns and \unhbox so that if there is a footnote in
+ % the item text, it can migrate to the main vertical list and
+ % eventually be printed.
+ \nobreak\kern-\tableindent
+ \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0
+ \unhbox0
+ \nobreak\kern\dimen0
+ \endgroup
+ \itemxneedsnegativevskiptrue
+ \fi
+}
+
+\def\item{\errmessage{@item while not in a list environment}}
+\def\itemx{\errmessage{@itemx while not in a list environment}}
+
+% @table, @ftable, @vtable.
+\envdef\table{%
+ \let\itemindex\gobble
+ \tablecheck{table}%
+}
+\envdef\ftable{%
+ \def\itemindex ##1{\doind {fn}{\code{##1}}}%
+ \tablecheck{ftable}%
+}
+\envdef\vtable{%
+ \def\itemindex ##1{\doind {vr}{\code{##1}}}%
+ \tablecheck{vtable}%
+}
+\def\tablecheck#1{%
+ \ifnum \the\catcode`\^^M=\active
+ \endgroup
+ \errmessage{This command won't work in this context; perhaps the problem is
+ that we are \inenvironment\thisenv}%
+ \def\next{\doignore{#1}}%
+ \else
+ \let\next\tablex
+ \fi
+ \next
+}
+\def\tablex#1{%
+ \def\itemindicate{#1}%
+ \parsearg\tabley
+}
+\def\tabley#1{%
+ {%
+ \makevalueexpandable
+ \edef\temp{\noexpand\tablez #1\space\space\space}%
+ \expandafter
+ }\temp \endtablez
+}
+\def\tablez #1 #2 #3 #4\endtablez{%
+ \aboveenvbreak
+ \ifnum 0#1>0 \advance \leftskip by #1\mil \fi
+ \ifnum 0#2>0 \tableindent=#2\mil \fi
+ \ifnum 0#3>0 \advance \rightskip by #3\mil \fi
+ \itemmax=\tableindent
+ \advance \itemmax by -\itemmargin
+ \advance \leftskip by \tableindent
+ \exdentamount=\tableindent
+ \parindent = 0pt
+ \parskip = \smallskipamount
+ \ifdim \parskip=0pt \parskip=2pt \fi
+ \let\item = \internalBitem
+ \let\itemx = \internalBitemx
+}
+\def\Etable{\endgraf\afterenvbreak}
+\let\Eftable\Etable
+\let\Evtable\Etable
+\let\Eitemize\Etable
+\let\Eenumerate\Etable
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\envdef\itemize{\parsearg\doitemize}
+
+\def\doitemize#1{%
+ \aboveenvbreak
+ \itemmax=\itemindent
+ \advance\itemmax by -\itemmargin
+ \advance\leftskip by \itemindent
+ \exdentamount=\itemindent
+ \parindent=0pt
+ \parskip=\smallskipamount
+ \ifdim\parskip=0pt \parskip=2pt \fi
+ \def\itemcontents{#1}%
+ % @itemize with no arg is equivalent to @itemize @bullet.
+ \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi
+ \let\item=\itemizeitem
+}
+
+% Definition of @item while inside @itemize and @enumerate.
+%
+\def\itemizeitem{%
+ \advance\itemno by 1 % for enumerations
+ {\let\par=\endgraf \smallbreak}% reasonable place to break
+ {%
+ % If the document has an @itemize directly after a section title, a
+ % \nobreak will be last on the list, and \sectionheading will have
+ % done a \vskip-\parskip. In that case, we don't want to zero
+ % parskip, or the item text will crash with the heading. On the
+ % other hand, when there is normal text preceding the item (as there
+ % usually is), we do want to zero parskip, or there would be too much
+ % space. In that case, we won't have a \nobreak before. At least
+ % that's the theory.
+ \ifnum\lastpenalty<10000 \parskip=0in \fi
+ \noindent
+ \hbox to 0pt{\hss \itemcontents \kern\itemmargin}%
+ \vadjust{\penalty 1200}}% not good to break after first line of item.
+ \flushcr
+}
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list. No
+% argument is the same as `1'.
+%
+\envparseargdef\enumerate{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ % If we were given no argument, pretend we were given `1'.
+ \def\thearg{#1}%
+ \ifx\thearg\empty \def\thearg{1}\fi
+ %
+ % Detect if the argument is a single token. If so, it might be a
+ % letter. Otherwise, the only valid thing it can be is a number.
+ % (We will always have one token, because of the test we just made.
+ % This is a good thing, since \splitoff doesn't work given nothing at
+ % all -- the first parameter is undelimited.)
+ \expandafter\splitoff\thearg\endmark
+ \ifx\rest\empty
+ % Only one token in the argument. It could still be anything.
+ % A ``lowercase letter'' is one whose \lccode is nonzero.
+ % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+ % not equal to itself.
+ % Otherwise, we assume it's a number.
+ %
+ % We need the \relax at the end of the \ifnum lines to stop TeX from
+ % continuing to look for a <number>.
+ %
+ \ifnum\lccode\expandafter`\thearg=0\relax
+ \numericenumerate % a number (we hope)
+ \else
+ % It's a letter.
+ \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+ \lowercaseenumerate % lowercase letter
+ \else
+ \uppercaseenumerate % uppercase letter
+ \fi
+ \fi
+ \else
+ % Multiple tokens in the argument. We hope it's a number.
+ \numericenumerate
+ \fi
+}
+
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more lowercase letters in @enumerate; get a bigger
+ alphabet}%
+ \fi
+ \char\lccode\itemno
+ }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more uppercase letters in @enumerate; get a bigger
+ alphabet}
+ \fi
+ \char\uccode\itemno
+ }%
+}
+
+% Call \doitemize, adding a period to the first argument and supplying the
+% common last two arguments. Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+ \advance\itemno by -1
+ \doitemize{#1.}\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble. Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+% @multitable @columnfractions .25 .3 .45
+% @item ...
+%
+% Numbers following @columnfractions are the percent of the total
+% current hsize to be used for each column. You may use as many
+% columns as desired.
+
+
+% Or use a template:
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item ...
+% using the widest term desired in each column.
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab do not need to be on their own lines, but it will not hurt
+% if they are.
+
+% Sample multitable:
+
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item first col stuff @tab second col stuff @tab third col
+% @item
+% first col stuff
+% @tab
+% second col stuff
+% @tab
+% third col
+% @item first col stuff @tab second col stuff
+% @tab Many paragraphs of text may be used in any column.
+%
+% They will wrap at the width determined by the template.
+% @item@tab@tab This will be in third column.
+% @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+% to baseline.
+% 0pt means it depends on current normal line spacing.
+%
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+% Macros used to set up halign preamble:
+%
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+% #1 is the @columnfraction, usually a decimal number like .5, but might
+% be just 1. We just use it, whatever it is.
+%
+\def\pickupwholefraction#1 {%
+ \global\advance\colcount by 1
+ \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}%
+ \setuptable
+}
+
+\newcount\colcount
+\def\setuptable#1{%
+ \def\firstarg{#1}%
+ \ifx\firstarg\xendsetuptable
+ \let\go = \relax
+ \else
+ \ifx\firstarg\xcolumnfractions
+ \global\setpercenttrue
+ \else
+ \ifsetpercent
+ \let\go\pickupwholefraction
+ \else
+ \global\advance\colcount by 1
+ \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a
+ % separator; typically that is always in the input, anyway.
+ \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+ \fi
+ \fi
+ \ifx\go\pickupwholefraction
+ % Put the argument back for the \pickupwholefraction call, so
+ % we'll always have a period there to be parsed.
+ \def\go{\pickupwholefraction#1}%
+ \else
+ \let\go = \setuptable
+ \fi%
+ \fi
+ \go
+}
+
+% multitable-only commands.
+%
+% @headitem starts a heading row, which we typeset in bold.
+% Assignments have to be global since we are inside the implicit group
+% of an alignment entry. Note that \everycr resets \everytab.
+\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}%
+%
+% A \tab used to include \hskip1sp. But then the space in a template
+% line is not enough. That is bad. So let's go back to just `&' until
+% we encounter the problem it was intended to solve again.
+% --karl, nathan@acm.org, 20apr99.
+\def\tab{\checkenv\multitable &\the\everytab}%
+
+% @multitable ... @end multitable definitions:
+%
+\newtoks\everytab % insert after every tab.
+%
+\envdef\multitable{%
+ \vskip\parskip
+ \startsavinginserts
+ %
+ % @item within a multitable starts a normal row.
+ % We use \def instead of \let so that if one of the multitable entries
+ % contains an @itemize, we don't choke on the \item (seen as \crcr aka
+ % \endtemplate) expanding \doitemize.
+ \def\item{\crcr}%
+ %
+ \tolerance=9500
+ \hbadness=9500
+ \setmultitablespacing
+ \parskip=\multitableparskip
+ \parindent=\multitableparindent
+ \overfullrule=0pt
+ \global\colcount=0
+ %
+ \everycr = {%
+ \noalign{%
+ \global\everytab={}%
+ \global\colcount=0 % Reset the column counter.
+ % Check for saved footnotes, etc.
+ \checkinserts
+ % Keeps underfull box messages off when table breaks over pages.
+ %\filbreak
+ % Maybe so, but it also creates really weird page breaks when the
+ % table breaks over pages. Wouldn't \vfil be better? Wait until the
+ % problem manifests itself, so it can be fixed for real --karl.
+ }%
+ }%
+ %
+ \parsearg\domultitable
+}
+\def\domultitable#1{%
+ % To parse everything between @multitable and @item:
+ \setuptable#1 \endsetuptable
+ %
+ % This preamble sets up a generic column definition, which will
+ % be used as many times as user calls for columns.
+ % \vtop will set a single line and will also let text wrap and
+ % continue for many paragraphs if desired.
+ \halign\bgroup &%
+ \global\advance\colcount by 1
+ \multistrut
+ \vtop{%
+ % Use the current \colcount to find the correct column width:
+ \hsize=\expandafter\csname col\the\colcount\endcsname
+ %
+ % In order to keep entries from bumping into each other
+ % we will add a \leftskip of \multitablecolspace to all columns after
+ % the first one.
+ %
+ % If a template has been used, we will add \multitablecolspace
+ % to the width of each template entry.
+ %
+ % If the user has set preamble in terms of percent of \hsize we will
+ % use that dimension as the width of the column, and the \leftskip
+ % will keep entries from bumping into each other. Table will start at
+ % left margin and final column will justify at right margin.
+ %
+ % Make sure we don't inherit \rightskip from the outer environment.
+ \rightskip=0pt
+ \ifnum\colcount=1
+ % The first column will be indented with the surrounding text.
+ \advance\hsize by\leftskip
+ \else
+ \ifsetpercent \else
+ % If user has not set preamble in terms of percent of \hsize
+ % we will advance \hsize by \multitablecolspace.
+ \advance\hsize by \multitablecolspace
+ \fi
+ % In either case we will make \leftskip=\multitablecolspace:
+ \leftskip=\multitablecolspace
+ \fi
+ % Ignoring space at the beginning and end avoids an occasional spurious
+ % blank line, when TeX decides to break the line at the space before the
+ % box from the multistrut, so the strut ends up on a line by itself.
+ % For example:
+ % @multitable @columnfractions .11 .89
+ % @item @code{#}
+ % @tab Legal holiday which is valid in major parts of the whole country.
+ % Is automatically provided with highlighting sequences respectively
+ % marking characters.
+ \noindent\ignorespaces##\unskip\multistrut
+ }\cr
+}
+\def\Emultitable{%
+ \crcr
+ \egroup % end the \halign
+ \global\setpercentfalse
+}
+
+\def\setmultitablespacing{%
+ \def\multistrut{\strut}% just use the standard line spacing
+ %
+ % Compute \multitablelinespace (if not defined by user) for use in
+ % \multitableparskip calculation. We used define \multistrut based on
+ % this, but (ironically) that caused the spacing to be off.
+ % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100.
+\ifdim\multitablelinespace=0pt
+\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip
+\global\advance\multitablelinespace by-\ht0
+\fi
+%% Test to see if parskip is larger than space between lines of
+%% table. If not, do nothing.
+%% If so, set to same dimension as multitablelinespace.
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi}
+
+
+\message{conditionals,}
+
+% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext,
+% @ifnotxml always succeed. They currently do nothing; we don't
+% attempt to check whether the conditionals are properly nested. But we
+% have to remember that they are conditionals, so that @end doesn't
+% attempt to close an environment group.
+%
+\def\makecond#1{%
+ \expandafter\let\csname #1\endcsname = \relax
+ \expandafter\let\csname iscond.#1\endcsname = 1
+}
+\makecond{iftex}
+\makecond{ifnotdocbook}
+\makecond{ifnothtml}
+\makecond{ifnotinfo}
+\makecond{ifnotplaintext}
+\makecond{ifnotxml}
+
+% Ignore @ignore, @ifhtml, @ifinfo, and the like.
+%
+\def\direntry{\doignore{direntry}}
+\def\documentdescription{\doignore{documentdescription}}
+\def\docbook{\doignore{docbook}}
+\def\html{\doignore{html}}
+\def\ifdocbook{\doignore{ifdocbook}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\ifplaintext{\doignore{ifplaintext}}
+\def\ifxml{\doignore{ifxml}}
+\def\ignore{\doignore{ignore}}
+\def\menu{\doignore{menu}}
+\def\xml{\doignore{xml}}
+
+% Ignore text until a line `@end #1', keeping track of nested conditionals.
+%
+% A count to remember the depth of nesting.
+\newcount\doignorecount
+
+\def\doignore#1{\begingroup
+ % Scan in ``verbatim'' mode:
+ \obeylines
+ \catcode`\@ = \other
+ \catcode`\{ = \other
+ \catcode`\} = \other
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \spaceisspace
+ %
+ % Count number of #1's that we've seen.
+ \doignorecount = 0
+ %
+ % Swallow text until we reach the matching `@end #1'.
+ \dodoignore{#1}%
+}
+
+{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source.
+ \obeylines %
+ %
+ \gdef\dodoignore#1{%
+ % #1 contains the command name as a string, e.g., `ifinfo'.
+ %
+ % Define a command to find the next `@end #1'.
+ \long\def\doignoretext##1^^M@end #1{%
+ \doignoretextyyy##1^^M@#1\_STOP_}%
+ %
+ % And this command to find another #1 command, at the beginning of a
+ % line. (Otherwise, we would consider a line `@c @ifset', for
+ % example, to count as an @ifset for nesting.)
+ \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}%
+ %
+ % And now expand that command.
+ \doignoretext ^^M%
+ }%
+}
+
+\def\doignoreyyy#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty % Nothing found.
+ \let\next\doignoretextzzz
+ \else % Found a nested condition, ...
+ \advance\doignorecount by 1
+ \let\next\doignoretextyyy % ..., look for another.
+ % If we're here, #1 ends with ^^M\ifinfo (for example).
+ \fi
+ \next #1% the token \_STOP_ is present just after this macro.
+}
+
+% We have to swallow the remaining "\_STOP_".
+%
+\def\doignoretextzzz#1{%
+ \ifnum\doignorecount = 0 % We have just found the outermost @end.
+ \let\next\enddoignore
+ \else % Still inside a nested condition.
+ \advance\doignorecount by -1
+ \let\next\doignoretext % Look for the next @end.
+ \fi
+ \next
+}
+
+% Finish off ignored text.
+{ \obeylines%
+ % Ignore anything after the last `@end #1'; this matters in verbatim
+ % environments, where otherwise the newline after an ignored conditional
+ % would result in a blank line in the output.
+ \gdef\enddoignore#1^^M{\endgroup\ignorespaces}%
+}
+
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it.
+% We rely on the fact that \parsearg sets \catcode`\ =10.
+%
+\parseargdef\set{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ {%
+ \makevalueexpandable
+ \def\temp{#2}%
+ \edef\next{\gdef\makecsname{SET#1}}%
+ \ifx\temp\empty
+ \next{}%
+ \else
+ \setzzz#2\endsetzzz
+ \fi
+ }%
+}
+% Remove the trailing space \setxxx inserted.
+\def\setzzz#1 \endsetzzz{\next{#1}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\parseargdef\clear{%
+ {%
+ \makevalueexpandable
+ \global\expandafter\let\csname SET#1\endcsname=\relax
+ }%
+}
+
+% @value{foo} gets the text saved in variable foo.
+\def\value{\begingroup\makevalueexpandable\valuexxx}
+\def\valuexxx#1{\expandablevalue{#1}\endgroup}
+{
+ \catcode`\- = \active \catcode`\_ = \active
+ %
+ \gdef\makevalueexpandable{%
+ \let\value = \expandablevalue
+ % We don't want these characters active, ...
+ \catcode`\-=\other \catcode`\_=\other
+ % ..., but we might end up with active ones in the argument if
+ % we're called from @code, as @code{@value{foo-bar_}}, though.
+ % So \let them to their normal equivalents.
+ \let-\realdash \let_\normalunderscore
+ }
+}
+
+% We have this subroutine so that we can handle at least some @value's
+% properly in indexes (we call \makevalueexpandable in \indexdummies).
+% The command has to be fully expandable (if the variable is set), since
+% the result winds up in the index file. This means that if the
+% variable's value contains other Texinfo commands, it's almost certain
+% it will fail (although perhaps we could fix that with sufficient work
+% to do a one-level expansion on the result, instead of complete).
+%
+\def\expandablevalue#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ {[No value for ``#1'']}%
+ \message{Variable `#1', used in @value, is not set.}%
+ \else
+ \csname SET#1\endcsname
+ \fi
+}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+% To get special treatment of `@end ifset,' call \makeond and the redefine.
+%
+\makecond{ifset}
+\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}}
+\def\doifset#1#2{%
+ {%
+ \makevalueexpandable
+ \let\next=\empty
+ \expandafter\ifx\csname SET#2\endcsname\relax
+ #1% If not set, redefine \next.
+ \fi
+ \expandafter
+ }\next
+}
+\def\ifsetfail{\doignore{ifset}}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+% The `\else' inside the `\doifset' parameter is a trick to reuse the
+% above code: if the variable is not set, do nothing, if it is set,
+% then redefine \next to \ifclearfail.
+%
+\makecond{ifclear}
+\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}}
+\def\ifclearfail{\doignore{ifclear}}
+
+% @dircategory CATEGORY -- specify a category of the dir file
+% which this file should belong to. Ignore this in TeX.
+\let\dircategory=\comment
+
+% @defininfoenclose.
+\let\definfoenclose=\comment
+
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within macros and \if's.
+\edef\newwrite{\makecsname{ptexnewwrite}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+%
+\def\newindex#1{%
+ \iflinks
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+ \fi
+ \expandafter\xdef\csname#1index\endcsname{% % Define @#1index
+ \noexpand\doindex{#1}}
+}
+
+% @defindex foo == \newindex{foo}
+%
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+%
+\def\defcodeindex{\parsearg\newcodeindex}
+%
+\def\newcodeindex#1{%
+ \iflinks
+ \expandafter\newwrite \csname#1indfile\endcsname
+ \openout \csname#1indfile\endcsname \jobname.#1
+ \fi
+ \expandafter\xdef\csname#1index\endcsname{%
+ \noexpand\docodeindex{#1}}%
+}
+
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+%
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+%
+\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}}
+\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}}
+
+% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo),
+% #3 the target index (bar).
+\def\dosynindex#1#2#3{%
+ % Only do \closeout if we haven't already done it, else we'll end up
+ % closing the target index.
+ \expandafter \ifx\csname donesynindex#2\endcsname \undefined
+ % The \closeout helps reduce unnecessary open files; the limit on the
+ % Acorn RISC OS is a mere 16 files.
+ \expandafter\closeout\csname#2indfile\endcsname
+ \expandafter\let\csname\donesynindex#2\endcsname = 1
+ \fi
+ % redefine \fooindfile:
+ \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname
+ \expandafter\let\csname#2indfile\endcsname=\temp
+ % redefine \fooindex:
+ \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+% Take care of Texinfo commands that can appear in an index entry.
+% Since there are some commands we want to expand, and others we don't,
+% we have to laboriously prevent expansion for those that we don't.
+%
+\def\indexdummies{%
+ \escapechar = `\\ % use backslash in output files.
+ \def\@{@}% change to @@ when we switch to @ as escape char in index files.
+ \def\ {\realbackslash\space }%
+ % Need these in case \tex is in effect and \{ is a \delimiter again.
+ % But can't use \lbracecmd and \rbracecmd because texindex assumes
+ % braces and backslashes are used only as delimiters.
+ \let\{ = \mylbrace
+ \let\} = \myrbrace
+ %
+ % Do the redefinitions.
+ \commondummies
+}
+
+% For the aux and toc files, @ is the escape character. So we want to
+% redefine everything using @ as the escape character (instead of
+% \realbackslash, still used for index files). When everything uses @,
+% this will be simpler.
+%
+\def\atdummies{%
+ \def\@{@@}%
+ \def\ {@ }%
+ \let\{ = \lbraceatcmd
+ \let\} = \rbraceatcmd
+ %
+ % Do the redefinitions.
+ \commondummies
+ \otherbackslash
+}
+
+% Called from \indexdummies and \atdummies.
+%
+\def\commondummies{%
+ %
+ % \definedummyword defines \#1 as \string\#1\space, thus effectively
+ % preventing its expansion. This is used only for control% words,
+ % not control letters, because the \space would be incorrect for
+ % control characters, but is needed to separate the control word
+ % from whatever follows.
+ %
+ % For control letters, we have \definedummyletter, which omits the
+ % space.
+ %
+ % These can be used both for control words that take an argument and
+ % those that do not. If it is followed by {arg} in the input, then
+ % that will dutifully get written to the index (or wherever).
+ %
+ \def\definedummyword ##1{\def##1{\string##1\space}}%
+ \def\definedummyletter##1{\def##1{\string##1}}%
+ \let\definedummyaccent\definedummyletter
+ %
+ \commondummiesnofonts
+ %
+ \definedummyletter\_%
+ %
+ % Non-English letters.
+ \definedummyword\AA
+ \definedummyword\AE
+ \definedummyword\L
+ \definedummyword\OE
+ \definedummyword\O
+ \definedummyword\aa
+ \definedummyword\ae
+ \definedummyword\l
+ \definedummyword\oe
+ \definedummyword\o
+ \definedummyword\ss
+ \definedummyword\exclamdown
+ \definedummyword\questiondown
+ \definedummyword\ordf
+ \definedummyword\ordm
+ %
+ % Although these internal commands shouldn't show up, sometimes they do.
+ \definedummyword\bf
+ \definedummyword\gtr
+ \definedummyword\hat
+ \definedummyword\less
+ \definedummyword\sf
+ \definedummyword\sl
+ \definedummyword\tclose
+ \definedummyword\tt
+ %
+ \definedummyword\LaTeX
+ \definedummyword\TeX
+ %
+ % Assorted special characters.
+ \definedummyword\bullet
+ \definedummyword\comma
+ \definedummyword\copyright
+ \definedummyword\registeredsymbol
+ \definedummyword\dots
+ \definedummyword\enddots
+ \definedummyword\equiv
+ \definedummyword\error
+ \definedummyword\euro
+ \definedummyword\expansion
+ \definedummyword\minus
+ \definedummyword\pounds
+ \definedummyword\point
+ \definedummyword\print
+ \definedummyword\result
+ %
+ % We want to disable all macros so that they are not expanded by \write.
+ \macrolist
+ %
+ \normalturnoffactive
+ %
+ % Handle some cases of @value -- where it does not contain any
+ % (non-fully-expandable) commands.
+ \makevalueexpandable
+}
+
+% \commondummiesnofonts: common to \commondummies and \indexnofonts.
+%
+\def\commondummiesnofonts{%
+ % Control letters and accents.
+ \definedummyletter\!%
+ \definedummyaccent\"%
+ \definedummyaccent\'%
+ \definedummyletter\*%
+ \definedummyaccent\,%
+ \definedummyletter\.%
+ \definedummyletter\/%
+ \definedummyletter\:%
+ \definedummyaccent\=%
+ \definedummyletter\?%
+ \definedummyaccent\^%
+ \definedummyaccent\`%
+ \definedummyaccent\~%
+ \definedummyword\u
+ \definedummyword\v
+ \definedummyword\H
+ \definedummyword\dotaccent
+ \definedummyword\ringaccent
+ \definedummyword\tieaccent
+ \definedummyword\ubaraccent
+ \definedummyword\udotaccent
+ \definedummyword\dotless
+ %
+ % Texinfo font commands.
+ \definedummyword\b
+ \definedummyword\i
+ \definedummyword\r
+ \definedummyword\sc
+ \definedummyword\t
+ %
+ % Commands that take arguments.
+ \definedummyword\acronym
+ \definedummyword\cite
+ \definedummyword\code
+ \definedummyword\command
+ \definedummyword\dfn
+ \definedummyword\emph
+ \definedummyword\env
+ \definedummyword\file
+ \definedummyword\kbd
+ \definedummyword\key
+ \definedummyword\math
+ \definedummyword\option
+ \definedummyword\pxref
+ \definedummyword\ref
+ \definedummyword\samp
+ \definedummyword\strong
+ \definedummyword\tie
+ \definedummyword\uref
+ \definedummyword\url
+ \definedummyword\var
+ \definedummyword\verb
+ \definedummyword\w
+ \definedummyword\xref
+}
+
+% \indexnofonts is used when outputting the strings to sort the index
+% by, and when constructing control sequence names. It eliminates all
+% control sequences and just writes whatever the best ASCII sort string
+% would be for a given command (usually its argument).
+%
+\def\indexnofonts{%
+ % Accent commands should become @asis.
+ \def\definedummyaccent##1{\let##1\asis}%
+ % We can just ignore other control letters.
+ \def\definedummyletter##1{\let##1\empty}%
+ % Hopefully, all control words can become @asis.
+ \let\definedummyword\definedummyaccent
+ %
+ \commondummiesnofonts
+ %
+ % Don't no-op \tt, since it isn't a user-level command
+ % and is used in the definitions of the active chars like <, >, |, etc.
+ % Likewise with the other plain tex font commands.
+ %\let\tt=\asis
+ %
+ \def\ { }%
+ \def\@{@}%
+ % how to handle braces?
+ \def\_{\normalunderscore}%
+ %
+ % Non-English letters.
+ \def\AA{AA}%
+ \def\AE{AE}%
+ \def\L{L}%
+ \def\OE{OE}%
+ \def\O{O}%
+ \def\aa{aa}%
+ \def\ae{ae}%
+ \def\l{l}%
+ \def\oe{oe}%
+ \def\o{o}%
+ \def\ss{ss}%
+ \def\exclamdown{!}%
+ \def\questiondown{?}%
+ \def\ordf{a}%
+ \def\ordm{o}%
+ %
+ \def\LaTeX{LaTeX}%
+ \def\TeX{TeX}%
+ %
+ % Assorted special characters.
+ % (The following {} will end up in the sort string, but that's ok.)
+ \def\bullet{bullet}%
+ \def\comma{,}%
+ \def\copyright{copyright}%
+ \def\registeredsymbol{R}%
+ \def\dots{...}%
+ \def\enddots{...}%
+ \def\equiv{==}%
+ \def\error{error}%
+ \def\euro{euro}%
+ \def\expansion{==>}%
+ \def\minus{-}%
+ \def\pounds{pounds}%
+ \def\point{.}%
+ \def\print{-|}%
+ \def\result{=>}%
+ %
+ % We need to get rid of all macros, leaving only the arguments (if present).
+ % Of course this is not nearly correct, but it is the best we can do for now.
+ % makeinfo does not expand macros in the argument to @deffn, which ends up
+ % writing an index entry, and texindex isn't prepared for an index sort entry
+ % that starts with \.
+ %
+ % Since macro invocations are followed by braces, we can just redefine them
+ % to take a single TeX argument. The case of a macro invocation that
+ % goes to end-of-line is not handled.
+ %
+ \macrolist
+}
+
+\let\indexbackslash=0 %overridden during \printindex.
+\let\SETmarginindex=\relax % put index entries in margin (undocumented)?
+
+% Most index entries go through here, but \dosubind is the general case.
+% #1 is the index name, #2 is the entry text.
+\def\doind#1#2{\dosubind{#1}{#2}{}}
+
+% Workhorse for all \fooindexes.
+% #1 is name of index, #2 is stuff to put there, #3 is subentry --
+% empty if called from \doind, as we usually are (the main exception
+% is with most defuns, which call us directly).
+%
+\def\dosubind#1#2#3{%
+ \iflinks
+ {%
+ % Store the main index entry text (including the third arg).
+ \toks0 = {#2}%
+ % If third arg is present, precede it with a space.
+ \def\thirdarg{#3}%
+ \ifx\thirdarg\empty \else
+ \toks0 = \expandafter{\the\toks0 \space #3}%
+ \fi
+ %
+ \edef\writeto{\csname#1indfile\endcsname}%
+ %
+ \ifvmode
+ \dosubindsanitize
+ \else
+ \dosubindwrite
+ \fi
+ }%
+ \fi
+}
+
+% Write the entry in \toks0 to the index file:
+%
+\def\dosubindwrite{%
+ % Put the index entry in the margin if desired.
+ \ifx\SETmarginindex\relax\else
+ \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}%
+ \fi
+ %
+ % Remember, we are within a group.
+ \indexdummies % Must do this here, since \bf, etc expand at this stage
+ \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now
+ % so it will be output as is; and it will print as backslash.
+ %
+ % Process the index entry with all font commands turned off, to
+ % get the string to sort by.
+ {\indexnofonts
+ \edef\temp{\the\toks0}% need full expansion
+ \xdef\indexsorttmp{\temp}%
+ }%
+ %
+ % Set up the complete index entry, with both the sort key and
+ % the original text, including any font commands. We write
+ % three arguments to \entry to the .?? file (four in the
+ % subentry case), texindex reduces to two when writing the .??s
+ % sorted result.
+ \edef\temp{%
+ \write\writeto{%
+ \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}%
+ }%
+ \temp
+}
+
+% Take care of unwanted page breaks:
+%
+% If a skip is the last thing on the list now, preserve it
+% by backing up by \lastskip, doing the \write, then inserting
+% the skip again. Otherwise, the whatsit generated by the
+% \write will make \lastskip zero. The result is that sequences
+% like this:
+% @end defun
+% @tindex whatever
+% @defun ...
+% will have extra space inserted, because the \medbreak in the
+% start of the @defun won't see the skip inserted by the @end of
+% the previous defun.
+%
+% But don't do any of this if we're not in vertical mode. We
+% don't want to do a \vskip and prematurely end a paragraph.
+%
+% Avoid page breaks due to these extra skips, too.
+%
+% But wait, there is a catch there:
+% We'll have to check whether \lastskip is zero skip. \ifdim is not
+% sufficient for this purpose, as it ignores stretch and shrink parts
+% of the skip. The only way seems to be to check the textual
+% representation of the skip.
+%
+% The following is almost like \def\zeroskipmacro{0.0pt} except that
+% the ``p'' and ``t'' characters have catcode \other, not 11 (letter).
+%
+\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname}
+%
+% ..., ready, GO:
+%
+\def\dosubindsanitize{%
+ % \lastskip and \lastpenalty cannot both be nonzero simultaneously.
+ \skip0 = \lastskip
+ \edef\lastskipmacro{\the\lastskip}%
+ \count255 = \lastpenalty
+ %
+ % If \lastskip is nonzero, that means the last item was a
+ % skip. And since a skip is discardable, that means this
+ % -\skip0 glue we're inserting is preceded by a
+ % non-discardable item, therefore it is not a potential
+ % breakpoint, therefore no \nobreak needed.
+ \ifx\lastskipmacro\zeroskipmacro
+ \else
+ \vskip-\skip0
+ \fi
+ %
+ \dosubindwrite
+ %
+ \ifx\lastskipmacro\zeroskipmacro
+ % If \lastskip was zero, perhaps the last item was a penalty, and
+ % perhaps it was >=10000, e.g., a \nobreak. In that case, we want
+ % to re-insert the same penalty (values >10000 are used for various
+ % signals); since we just inserted a non-discardable item, any
+ % following glue (such as a \parskip) would be a breakpoint. For example:
+ %
+ % @deffn deffn-whatever
+ % @vindex index-whatever
+ % Description.
+ % would allow a break between the index-whatever whatsit
+ % and the "Description." paragraph.
+ \ifnum\count255>9999 \penalty\count255 \fi
+ \else
+ % On the other hand, if we had a nonzero \lastskip,
+ % this make-up glue would be preceded by a non-discardable item
+ % (the whatsit from the \write), so we must insert a \nobreak.
+ \nobreak\vskip\skip0
+ \fi
+}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\parseargdef\printindex{\begingroup
+ \dobreak \chapheadingskip{10000}%
+ %
+ \smallfonts \rm
+ \tolerance = 9500
+ \everypar = {}% don't want the \kern\-parindent from indentation suppression.
+ %
+ % See if the index file exists and is nonempty.
+ % Change catcode of @ here so that if the index file contains
+ % \initial {@}
+ % as its first line, TeX doesn't complain about mismatched braces
+ % (because it thinks @} is a control sequence).
+ \catcode`\@ = 11
+ \openin 1 \jobname.#1s
+ \ifeof 1
+ % \enddoublecolumns gets confused if there is no text in the index,
+ % and it loses the chapter title and the aux file entries for the
+ % index. The easiest way to prevent this problem is to make sure
+ % there is some text.
+ \putwordIndexNonexistent
+ \else
+ %
+ % If the index file exists but is empty, then \openin leaves \ifeof
+ % false. We have to make TeX try to read something from the file, so
+ % it can discover if there is anything in it.
+ \read 1 to \temp
+ \ifeof 1
+ \putwordIndexIsEmpty
+ \else
+ % Index files are almost Texinfo source, but we use \ as the escape
+ % character. It would be better to use @, but that's too big a change
+ % to make right now.
+ \def\indexbackslash{\backslashcurfont}%
+ \catcode`\\ = 0
+ \escapechar = `\\
+ \begindoublecolumns
+ \input \jobname.#1s
+ \enddoublecolumns
+ \fi
+ \fi
+ \closein 1
+\endgroup}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+\def\initial#1{{%
+ % Some minor font changes for the special characters.
+ \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+ %
+ % Remove any glue we may have, we'll be inserting our own.
+ \removelastskip
+ %
+ % We like breaks before the index initials, so insert a bonus.
+ \nobreak
+ \vskip 0pt plus 3\baselineskip
+ \penalty 0
+ \vskip 0pt plus -3\baselineskip
+ %
+ % Typeset the initial. Making this add up to a whole number of
+ % baselineskips increases the chance of the dots lining up from column
+ % to column. It still won't often be perfect, because of the stretch
+ % we need before each entry, but it's better.
+ %
+ % No shrink because it confuses \balancecolumns.
+ \vskip 1.67\baselineskip plus .5\baselineskip
+ \leftline{\secbf #1}%
+ % Do our best not to break after the initial.
+ \nobreak
+ \vskip .33\baselineskip plus .1\baselineskip
+}}
+
+% \entry typesets a paragraph consisting of the text (#1), dot leaders, and
+% then page number (#2) flushed to the right margin. It is used for index
+% and table of contents entries. The paragraph is indented by \leftskip.
+%
+% A straightforward implementation would start like this:
+% \def\entry#1#2{...
+% But this frozes the catcodes in the argument, and can cause problems to
+% @code, which sets - active. This problem was fixed by a kludge---
+% ``-'' was active throughout whole index, but this isn't really right.
+%
+% The right solution is to prevent \entry from swallowing the whole text.
+% --kasal, 21nov03
+\def\entry{%
+ \begingroup
+ %
+ % Start a new paragraph if necessary, so our assignments below can't
+ % affect previous text.
+ \par
+ %
+ % Do not fill out the last line with white space.
+ \parfillskip = 0in
+ %
+ % No extra space above this paragraph.
+ \parskip = 0in
+ %
+ % Do not prefer a separate line ending with a hyphen to fewer lines.
+ \finalhyphendemerits = 0
+ %
+ % \hangindent is only relevant when the entry text and page number
+ % don't both fit on one line. In that case, bob suggests starting the
+ % dots pretty far over on the line. Unfortunately, a large
+ % indentation looks wrong when the entry text itself is broken across
+ % lines. So we use a small indentation and put up with long leaders.
+ %
+ % \hangafter is reset to 1 (which is the value we want) at the start
+ % of each paragraph, so we need not do anything with that.
+ \hangindent = 2em
+ %
+ % When the entry text needs to be broken, just fill out the first line
+ % with blank space.
+ \rightskip = 0pt plus1fil
+ %
+ % A bit of stretch before each entry for the benefit of balancing
+ % columns.
+ \vskip 0pt plus1pt
+ %
+ % Swallow the left brace of the text (first parameter):
+ \afterassignment\doentry
+ \let\temp =
+}
+\def\doentry{%
+ \bgroup % Instead of the swallowed brace.
+ \noindent
+ \aftergroup\finishentry
+ % And now comes the text of the entry.
+}
+\def\finishentry#1{%
+ % #1 is the page number.
+ %
+ % The following is kludged to not output a line of dots in the index if
+ % there are no page numbers. The next person who breaks this will be
+ % cursed by a Unix daemon.
+ \def\tempa{{\rm }}%
+ \def\tempb{#1}%
+ \edef\tempc{\tempa}%
+ \edef\tempd{\tempb}%
+ \ifx\tempc\tempd
+ \ %
+ \else
+ %
+ % If we must, put the page number on a line of its own, and fill out
+ % this line with blank space. (The \hfil is overwhelmed with the
+ % fill leaders glue in \indexdotfill if the page number does fit.)
+ \hfil\penalty50
+ \null\nobreak\indexdotfill % Have leaders before the page number.
+ %
+ % The `\ ' here is removed by the implicit \unskip that TeX does as
+ % part of (the primitive) \par. Without it, a spurious underfull
+ % \hbox ensues.
+ \ifpdf
+ \pdfgettoks#1.%
+ \ \the\toksA
+ \else
+ \ #1%
+ \fi
+ \fi
+ \par
+ \endgroup
+}
+
+% Like \dotfill except takes at least 1 em.
+\def\indexdotfill{\cleaders
+ \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+\def\secondary#1#2{{%
+ \parfillskip=0in
+ \parskip=0in
+ \hangindent=1in
+ \hangafter=1
+ \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill
+ \ifpdf
+ \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph.
+ \else
+ #2
+ \fi
+ \par
+}}
+
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11
+
+\newbox\partialpage
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
+ % Grab any single-column material above us.
+ \output = {%
+ %
+ % Here is a possibility not foreseen in manmac: if we accumulate a
+ % whole lot of material, we might end up calling this \output
+ % routine twice in a row (see the doublecol-lose test, which is
+ % essentially a couple of indexes with @setchapternewpage off). In
+ % that case we just ship out what is in \partialpage with the normal
+ % output routine. Generally, \partialpage will be empty when this
+ % runs and this will be a no-op. See the indexspread.tex test case.
+ \ifvoid\partialpage \else
+ \onepageout{\pagecontents\partialpage}%
+ \fi
+ %
+ \global\setbox\partialpage = \vbox{%
+ % Unvbox the main output page.
+ \unvbox\PAGE
+ \kern-\topskip \kern\baselineskip
+ }%
+ }%
+ \eject % run that output routine to set \partialpage
+ %
+ % Use the double-column output routine for subsequent pages.
+ \output = {\doublecolumnout}%
+ %
+ % Change the page size parameters. We could do this once outside this
+ % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+ % format, but then we repeat the same computation. Repeating a couple
+ % of assignments once per index is clearly meaningless for the
+ % execution time, so we may as well do it in one place.
+ %
+ % First we halve the line length, less a little for the gutter between
+ % the columns. We compute the gutter based on the line length, so it
+ % changes automatically with the paper format. The magic constant
+ % below is chosen so that the gutter has the same value (well, +-<1pt)
+ % as it did when we hard-coded it.
+ %
+ % We put the result in a separate register, \doublecolumhsize, so we
+ % can restore it in \pagesofar, after \hsize itself has (potentially)
+ % been clobbered.
+ %
+ \doublecolumnhsize = \hsize
+ \advance\doublecolumnhsize by -.04154\hsize
+ \divide\doublecolumnhsize by 2
+ \hsize = \doublecolumnhsize
+ %
+ % Double the \vsize as well. (We don't need a separate register here,
+ % since nobody clobbers \vsize.)
+ \vsize = 2\vsize
+}
+
+% The double-column output routine for all double-column pages except
+% the last.
+%
+\def\doublecolumnout{%
+ \splittopskip=\topskip \splitmaxdepth=\maxdepth
+ % Get the available space for the double columns -- the normal
+ % (undoubled) page height minus any material left over from the
+ % previous page.
+ \dimen@ = \vsize
+ \divide\dimen@ by 2
+ \advance\dimen@ by -\ht\partialpage
+ %
+ % box0 will be the left-hand column, box2 the right.
+ \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+ \onepageout\pagesofar
+ \unvbox255
+ \penalty\outputpenalty
+}
+%
+% Re-output the contents of the output page -- any previous material,
+% followed by the two boxes we just split, in box0 and box2.
+\def\pagesofar{%
+ \unvbox\partialpage
+ %
+ \hsize = \doublecolumnhsize
+ \wd0=\hsize \wd2=\hsize
+ \hbox to\pagewidth{\box0\hfil\box2}%
+}
+%
+% All done with double columns.
+\def\enddoublecolumns{%
+ \output = {%
+ % Split the last of the double-column material. Leave it on the
+ % current page, no automatic page break.
+ \balancecolumns
+ %
+ % If we end up splitting too much material for the current page,
+ % though, there will be another page break right after this \output
+ % invocation ends. Having called \balancecolumns once, we do not
+ % want to call it again. Therefore, reset \output to its normal
+ % definition right away. (We hope \balancecolumns will never be
+ % called on to balance too much material, but if it is, this makes
+ % the output somewhat more palatable.)
+ \global\output = {\onepageout{\pagecontents\PAGE}}%
+ }%
+ \eject
+ \endgroup % started in \begindoublecolumns
+ %
+ % \pagegoal was set to the doubled \vsize above, since we restarted
+ % the current page. We're now back to normal single-column
+ % typesetting, so reset \pagegoal to the normal \vsize (after the
+ % \endgroup where \vsize got restored).
+ \pagegoal = \vsize
+}
+%
+% Called at the end of the double column material.
+\def\balancecolumns{%
+ \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120.
+ \dimen@ = \ht0
+ \advance\dimen@ by \topskip
+ \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by 2 % target to split to
+ %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}%
+ \splittopskip = \topskip
+ % Loop until we get a decent breakpoint.
+ {%
+ \vbadness = 10000
+ \loop
+ \global\setbox3 = \copy0
+ \global\setbox1 = \vsplit3 to \dimen@
+ \ifdim\ht3>\dimen@
+ \global\advance\dimen@ by 1pt
+ \repeat
+ }%
+ %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}%
+ \setbox0=\vbox to\dimen@{\unvbox1}%
+ \setbox2=\vbox to\dimen@{\unvbox3}%
+ %
+ \pagesofar
+}
+\catcode`\@ = \other
+
+
+\message{sectioning,}
+% Chapters, sections, etc.
+
+% \unnumberedno is an oxymoron, of course. But we count the unnumbered
+% sections so that we can refer to them unambiguously in the pdf
+% outlines by their "section number". We avoid collisions with chapter
+% numbers by starting them at 10000. (If a document ever has 10000
+% chapters, we're in trouble anyway, I'm sure.)
+\newcount\unnumberedno \unnumberedno = 10000
+\newcount\chapno
+\newcount\secno \secno=0
+\newcount\subsecno \subsecno=0
+\newcount\subsubsecno \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount\appendixno \appendixno = `\@
+%
+% \def\appendixletter{\char\the\appendixno}
+% We do the following ugly conditional instead of the above simple
+% construct for the sake of pdftex, which needs the actual
+% letter in the expansion, not just typeset.
+%
+\def\appendixletter{%
+ \ifnum\appendixno=`A A%
+ \else\ifnum\appendixno=`B B%
+ \else\ifnum\appendixno=`C C%
+ \else\ifnum\appendixno=`D D%
+ \else\ifnum\appendixno=`E E%
+ \else\ifnum\appendixno=`F F%
+ \else\ifnum\appendixno=`G G%
+ \else\ifnum\appendixno=`H H%
+ \else\ifnum\appendixno=`I I%
+ \else\ifnum\appendixno=`J J%
+ \else\ifnum\appendixno=`K K%
+ \else\ifnum\appendixno=`L L%
+ \else\ifnum\appendixno=`M M%
+ \else\ifnum\appendixno=`N N%
+ \else\ifnum\appendixno=`O O%
+ \else\ifnum\appendixno=`P P%
+ \else\ifnum\appendixno=`Q Q%
+ \else\ifnum\appendixno=`R R%
+ \else\ifnum\appendixno=`S S%
+ \else\ifnum\appendixno=`T T%
+ \else\ifnum\appendixno=`U U%
+ \else\ifnum\appendixno=`V V%
+ \else\ifnum\appendixno=`W W%
+ \else\ifnum\appendixno=`X X%
+ \else\ifnum\appendixno=`Y Y%
+ \else\ifnum\appendixno=`Z Z%
+ % The \the is necessary, despite appearances, because \appendixletter is
+ % expanded while writing the .toc file. \char\appendixno is not
+ % expandable, thus it is written literally, thus all appendixes come out
+ % with the same letter (or @) in the toc without it.
+ \else\char\the\appendixno
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+
+% Each @chapter defines this as the name of the chapter.
+% page headings and footings can use it. @section does likewise.
+% However, they are not reliable, because we don't use marks.
+\def\thischapter{}
+\def\thissection{}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% we only have subsub.
+\chardef\maxseclevel = 3
+%
+% A numbered section within an unnumbered changes to unnumbered too.
+% To achieve this, remember the "biggest" unnum. sec. we are currently in:
+\chardef\unmlevel = \maxseclevel
+%
+% Trace whether the current chapter is an appendix or not:
+% \chapheadtype is "N" or "A", unnumbered chapters are ignored.
+\def\chapheadtype{N}
+
+% Choose a heading macro
+% #1 is heading type
+% #2 is heading level
+% #3 is text for heading
+\def\genhead#1#2#3{%
+ % Compute the abs. sec. level:
+ \absseclevel=#2
+ \advance\absseclevel by \secbase
+ % Make sure \absseclevel doesn't fall outside the range:
+ \ifnum \absseclevel < 0
+ \absseclevel = 0
+ \else
+ \ifnum \absseclevel > 3
+ \absseclevel = 3
+ \fi
+ \fi
+ % The heading type:
+ \def\headtype{#1}%
+ \if \headtype U%
+ \ifnum \absseclevel < \unmlevel
+ \chardef\unmlevel = \absseclevel
+ \fi
+ \else
+ % Check for appendix sections:
+ \ifnum \absseclevel = 0
+ \edef\chapheadtype{\headtype}%
+ \else
+ \if \headtype A\if \chapheadtype N%
+ \errmessage{@appendix... within a non-appendix chapter}%
+ \fi\fi
+ \fi
+ % Check for numbered within unnumbered:
+ \ifnum \absseclevel > \unmlevel
+ \def\headtype{U}%
+ \else
+ \chardef\unmlevel = 3
+ \fi
+ \fi
+ % Now print the heading:
+ \if \headtype U%
+ \ifcase\absseclevel
+ \unnumberedzzz{#3}%
+ \or \unnumberedseczzz{#3}%
+ \or \unnumberedsubseczzz{#3}%
+ \or \unnumberedsubsubseczzz{#3}%
+ \fi
+ \else
+ \if \headtype A%
+ \ifcase\absseclevel
+ \appendixzzz{#3}%
+ \or \appendixsectionzzz{#3}%
+ \or \appendixsubseczzz{#3}%
+ \or \appendixsubsubseczzz{#3}%
+ \fi
+ \else
+ \ifcase\absseclevel
+ \chapterzzz{#3}%
+ \or \seczzz{#3}%
+ \or \numberedsubseczzz{#3}%
+ \or \numberedsubsubseczzz{#3}%
+ \fi
+ \fi
+ \fi
+ \suppressfirstparagraphindent
+}
+
+% an interface:
+\def\numhead{\genhead N}
+\def\apphead{\genhead A}
+\def\unnmhead{\genhead U}
+
+% @chapter, @appendix, @unnumbered. Increment top-level counter, reset
+% all lower-level sectioning counters to zero.
+%
+% Also set \chaplevelprefix, which we prepend to @float sequence numbers
+% (e.g., figures), q.v. By default (before any chapter), that is empty.
+\let\chaplevelprefix = \empty
+%
+\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz#1{%
+ % section resetting is \global in case the chapter is in a group, such
+ % as an @include file.
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\chapno by 1
+ %
+ % Used for \float.
+ \gdef\chaplevelprefix{\the\chapno.}%
+ \resetallfloatnos
+ %
+ \message{\putwordChapter\space \the\chapno}%
+ %
+ % Write the actual heading.
+ \chapmacro{#1}{Ynumbered}{\the\chapno}%
+ %
+ % So @section and the like are numbered underneath this chapter.
+ \global\let\section = \numberedsec
+ \global\let\subsection = \numberedsubsec
+ \global\let\subsubsection = \numberedsubsubsec
+}
+
+\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\appendixno by 1
+ \gdef\chaplevelprefix{\appendixletter.}%
+ \resetallfloatnos
+ %
+ \def\appendixnum{\putwordAppendix\space \appendixletter}%
+ \message{\appendixnum}%
+ %
+ \chapmacro{#1}{Yappendix}{\appendixletter}%
+ %
+ \global\let\section = \appendixsec
+ \global\let\subsection = \appendixsubsec
+ \global\let\subsubsection = \appendixsubsubsec
+}
+
+\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz#1{%
+ \global\secno=0 \global\subsecno=0 \global\subsubsecno=0
+ \global\advance\unnumberedno by 1
+ %
+ % Since an unnumbered has no number, no prefix for figures.
+ \global\let\chaplevelprefix = \empty
+ \resetallfloatnos
+ %
+ % This used to be simply \message{#1}, but TeX fully expands the
+ % argument to \message. Therefore, if #1 contained @-commands, TeX
+ % expanded them. For example, in `@unnumbered The @cite{Book}', TeX
+ % expanded @cite (which turns out to cause errors because \cite is meant
+ % to be executed, not expanded).
+ %
+ % Anyway, we don't want the fully-expanded definition of @cite to appear
+ % as a result of the \message, we just want `@cite' itself. We use
+ % \the<toks register> to achieve this: TeX expands \the<toks> only once,
+ % simply yielding the contents of <toks register>. (We also do this for
+ % the toc entries.)
+ \toks0 = {#1}%
+ \message{(\the\toks0)}%
+ %
+ \chapmacro{#1}{Ynothing}{\the\unnumberedno}%
+ %
+ \global\let\section = \unnumberedsec
+ \global\let\subsection = \unnumberedsubsec
+ \global\let\subsubsection = \unnumberedsubsubsec
+}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\parseargdef\centerchap{%
+ % Well, we could do the following in a group, but that would break
+ % an assumption that \chapmacro is called at the outermost level.
+ % Thus we are safer this way: --kasal, 24feb04
+ \let\centerparametersmaybe = \centerparameters
+ \unnmhead0{#1}%
+ \let\centerparametersmaybe = \relax
+}
+
+% @top is like @unnumbered.
+\let\top\unnumbered
+
+% Sections.
+\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz
+\def\seczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}%
+}
+
+\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}%
+}
+\let\appendixsec\appendixsection
+
+\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz#1{%
+ \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1
+ \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}%
+}
+
+% Subsections.
+\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz#1{%
+ \global\subsubsecno=0 \global\advance\subsecno by 1
+ \sectionheading{#1}{subsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno}%
+}
+
+% Subsubsections.
+\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynumbered}%
+ {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Yappendix}%
+ {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz#1{%
+ \global\advance\subsubsecno by 1
+ \sectionheading{#1}{subsubsec}{Ynothing}%
+ {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}%
+}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\let\section = \numberedsec
+\let\subsection = \numberedsubsec
+\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+% NOTE on use of \vbox for chapter headings, section headings, and such:
+% 1) We use \vbox rather than the earlier \line to permit
+% overlong headings to fold.
+% 2) \hyphenpenalty is set to 10000 because hyphenation in a
+% heading is obnoxious; this forbids it.
+% 3) Likewise, headings look best if no \parindent is used, and
+% if justification is not attempted. Hence \raggedright.
+
+
+\def\majorheading{%
+ {\advance\chapheadingskip by 10pt \chapbreak }%
+ \parsearg\chapheadingzzz
+}
+
+\def\chapheading{\chapbreak \parsearg\chapheadingzzz}
+\def\chapheadingzzz#1{%
+ {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}%
+ \bigskip \par\penalty 200\relax
+ \suppressfirstparagraphindent
+}
+
+% @heading, @subheading, @subsubheading.
+\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{}
+ \suppressfirstparagraphindent}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip\chapheadingskip
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{%
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{%
+\global\let\contentsalignmacro = \chapoddpage
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+% Chapter opening.
+%
+% #1 is the text, #2 is the section type (Ynumbered, Ynothing,
+% Yappendix, Yomitfromtoc), #3 the chapter number.
+%
+% To test against our argument.
+\def\Ynothingkeyword{Ynothing}
+\def\Yomitfromtockeyword{Yomitfromtoc}
+\def\Yappendixkeyword{Yappendix}
+%
+\def\chapmacro#1#2#3{%
+ \pchapsepmacro
+ {%
+ \chapfonts \rm
+ %
+ % Have to define \thissection before calling \donoderef, because the
+ % xref code eventually uses it. On the other hand, it has to be called
+ % after \pchapsepmacro, or the headline will change too soon.
+ \gdef\thissection{#1}%
+ \gdef\thischaptername{#1}%
+ %
+ % Only insert the separating space if we have a chapter/appendix
+ % number, and don't print the unnumbered ``number''.
+ \def\temptype{#2}%
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unnchap}%
+ \gdef\thischapter{#1}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ \setbox0 = \hbox{}% contents like unnumbered, but no toc entry
+ \def\toctype{omit}%
+ \gdef\thischapter{}%
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{\putwordAppendix{} #3\enspace}%
+ \def\toctype{app}%
+ % We don't substitute the actual chapter name into \thischapter
+ % because we don't want its macros evaluated now. And we don't
+ % use \thissection because that changes with each section.
+ %
+ \xdef\thischapter{\putwordAppendix{} \appendixletter:
+ \noexpand\thischaptername}%
+ \else
+ \setbox0 = \hbox{#3\enspace}%
+ \def\toctype{numchap}%
+ \xdef\thischapter{\putwordChapter{} \the\chapno:
+ \noexpand\thischaptername}%
+ \fi\fi\fi
+ %
+ % Write the toc entry for this chapter. Must come before the
+ % \donoderef, because we include the current node name in the toc
+ % entry, and \donoderef resets it to empty.
+ \writetocentry{\toctype}{#1}{#3}%
+ %
+ % For pdftex, we have to write out the node definition (aka, make
+ % the pdfdest) after any page break, but before the actual text has
+ % been typeset. If the destination for the pdf outline is after the
+ % text, then jumping from the outline may wind up with the text not
+ % being visible, for instance under high magnification.
+ \donoderef{#2}%
+ %
+ % Typeset the actual heading.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+ \hangindent=\wd0 \centerparametersmaybe
+ \unhbox0 #1\par}%
+ }%
+ \nobreak\bigskip % no page break after a chapter title
+ \nobreak
+}
+
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerparameters{%
+ \advance\rightskip by 3\rightskip
+ \leftskip = \rightskip
+ \parfillskip = 0pt
+}
+
+
+% I don't think this chapter style is supported any more, so I'm not
+% updating it with the new noderef stuff. We'll see. --karl, 11aug03.
+%
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+%
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\nobreak
+}
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+\def\centerchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt
+ \hfill {\rm #1}\hfill}}\bigskip \par\nobreak
+}
+\def\CHAPFopen{%
+ \global\let\chapmacro=\chfopen
+ \global\let\centerchapmacro=\centerchfopen}
+
+
+% Section titles. These macros combine the section number parts and
+% call the generic \sectionheading to do the printing.
+%
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip{-1000}}
+
+% Subsection titles.
+\newskip\subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}}
+
+% Subsubsection titles.
+\def\subsubsecheadingskip{\subsecheadingskip}
+\def\subsubsecheadingbreak{\subsecheadingbreak}
+
+
+% Print any size, any type, section title.
+%
+% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is
+% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the
+% section number.
+%
+\def\sectionheading#1#2#3#4{%
+ {%
+ % Switch to the right set of fonts.
+ \csname #2fonts\endcsname \rm
+ %
+ % Insert space above the heading.
+ \csname #2headingbreak\endcsname
+ %
+ % Only insert the space after the number if we have a section number.
+ \def\sectionlevel{#2}%
+ \def\temptype{#3}%
+ %
+ \ifx\temptype\Ynothingkeyword
+ \setbox0 = \hbox{}%
+ \def\toctype{unn}%
+ \gdef\thissection{#1}%
+ \else\ifx\temptype\Yomitfromtockeyword
+ % for @headings -- no section number, don't include in toc,
+ % and don't redefine \thissection.
+ \setbox0 = \hbox{}%
+ \def\toctype{omit}%
+ \let\sectionlevel=\empty
+ \else\ifx\temptype\Yappendixkeyword
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{app}%
+ \gdef\thissection{#1}%
+ \else
+ \setbox0 = \hbox{#4\enspace}%
+ \def\toctype{num}%
+ \gdef\thissection{#1}%
+ \fi\fi\fi
+ %
+ % Write the toc entry (before \donoderef). See comments in \chapmacro.
+ \writetocentry{\toctype\sectionlevel}{#1}{#4}%
+ %
+ % Write the node reference (= pdf destination for pdftex).
+ % Again, see comments in \chapmacro.
+ \donoderef{#3}%
+ %
+ % Interline glue will be inserted when the vbox is completed.
+ % That glue will be a valid breakpoint for the page, since it'll be
+ % preceded by a whatsit (usually from the \donoderef, or from the
+ % \writetocentry if there was no node). We don't want to allow that
+ % break, since then the whatsits could end up on page n while the
+ % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000.
+ \nobreak
+ %
+ % Output the actual section heading.
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+ \hangindent=\wd0 % zero if no section number
+ \unhbox0 #1}%
+ }%
+ % Add extra space after the heading -- half of whatever came above it.
+ % Don't allow stretch, though.
+ \kern .5 \csname #2headingskip\endcsname
+ %
+ % Do not let the kern be a potential breakpoint, as it would be if it
+ % was followed by glue.
+ \nobreak
+ %
+ % We'll almost certainly start a paragraph next, so don't let that
+ % glue accumulate. (Not a breakpoint because it's preceded by a
+ % discardable item.)
+ \vskip-\parskip
+ %
+ % This is purely so the last item on the list is a known \penalty >
+ % 10000. This is so \startdefun can avoid allowing breakpoints after
+ % section headings. Otherwise, it would insert a valid breakpoint between:
+ %
+ % @section sec-whatever
+ % @deffn def-whatever
+ \penalty 10001
+}
+
+
+\message{toc,}
+% Table of contents.
+\newwrite\tocfile
+
+% Write an entry to the toc file, opening it if necessary.
+% Called from @chapter, etc.
+%
+% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno}
+% We append the current node name (if any) and page number as additional
+% arguments for the \{chap,sec,...}entry macros which will eventually
+% read this. The node name is used in the pdf outlines as the
+% destination to jump to.
+%
+% We open the .toc file for writing here instead of at @setfilename (or
+% any other fixed time) so that @contents can be anywhere in the document.
+% But if #1 is `omit', then we don't do anything. This is used for the
+% table of contents chapter openings themselves.
+%
+\newif\iftocfileopened
+\def\omitkeyword{omit}%
+%
+\def\writetocentry#1#2#3{%
+ \edef\writetoctype{#1}%
+ \ifx\writetoctype\omitkeyword \else
+ \iftocfileopened\else
+ \immediate\openout\tocfile = \jobname.toc
+ \global\tocfileopenedtrue
+ \fi
+ %
+ \iflinks
+ {\atdummies
+ \edef\temp{%
+ \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}%
+ \temp
+ }%
+ \fi
+ \fi
+ %
+ % Tell \shipout to create a pdf destination on each page, if we're
+ % writing pdf. These are used in the table of contents. We can't
+ % just write one on every page because the title pages are numbered
+ % 1 and 2 (the page numbers aren't printed), and so are the first
+ % two pages of the document. Thus, we'd have two destinations named
+ % `1', and two named `2'.
+ \ifpdf \global\pdfmakepagedesttrue \fi
+}
+
+
+% These characters do not print properly in the Computer Modern roman
+% fonts, so we must take special care. This is more or less redundant
+% with the Texinfo input format setup at the end of this file.
+%
+\def\activecatcodes{%
+ \catcode`\"=\active
+ \catcode`\$=\active
+ \catcode`\<=\active
+ \catcode`\>=\active
+ \catcode`\\=\active
+ \catcode`\^=\active
+ \catcode`\_=\active
+ \catcode`\|=\active
+ \catcode`\~=\active
+}
+
+
+% Read the toc file, which is essentially Texinfo input.
+\def\readtocfile{%
+ \setupdatafile
+ \activecatcodes
+ \input \jobname.toc
+}
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\newcount\savepageno
+\newcount\lastnegativepageno \lastnegativepageno = -1
+
+% Prepare to read what we've written to \tocfile.
+%
+\def\startcontents#1{%
+ % If @setchapternewpage on, and @headings double, the contents should
+ % start on an odd page, unlike chapters. Thus, we maintain
+ % \contentsalignmacro in parallel with \pagealignmacro.
+ % From: Torbjorn Granlund <tege@matematik.su.se>
+ \contentsalignmacro
+ \immediate\closeout\tocfile
+ %
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \def\thischapter{}%
+ \chapmacro{#1}{Yomitfromtoc}{}%
+ %
+ \savepageno = \pageno
+ \begingroup % Set up to handle contents files properly.
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+ %
+ % Roman numerals for page numbers.
+ \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi
+}
+
+
+% Normal (long) toc.
+\def\contents{%
+ \startcontents{\putwordTOC}%
+ \openin 1 \jobname.toc
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \ifeof 1 \else
+ \pdfmakeoutlines
+ \fi
+ \closein 1
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
+}
+
+% And just the chapters.
+\def\summarycontents{%
+ \startcontents{\putwordShortTOC}%
+ %
+ \let\numchapentry = \shortchapentry
+ \let\appentry = \shortchapentry
+ \let\unnchapentry = \shortunnchapentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf
+ \let\sl=\shortcontsl \let\tt=\shortconttt
+ \rm
+ \hyphenpenalty = 10000
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\numsecentry##1##2##3##4{}
+ \let\appsecentry = \numsecentry
+ \let\unnsecentry = \numsecentry
+ \let\numsubsecentry = \numsecentry
+ \let\appsubsecentry = \numsecentry
+ \let\unnsubsecentry = \numsecentry
+ \let\numsubsubsecentry = \numsecentry
+ \let\appsubsubsecentry = \numsecentry
+ \let\unnsubsubsecentry = \numsecentry
+ \openin 1 \jobname.toc
+ \ifeof 1 \else
+ \readtocfile
+ \fi
+ \closein 1
+ \vfill \eject
+ \contentsalignmacro % in case @setchapternewpage odd is in effect
+ \endgroup
+ \lastnegativepageno = \pageno
+ \global\pageno = \savepageno
+}
+\let\shortcontents = \summarycontents
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g., `A' for an appendix, or `3' for a chapter.
+%
+\def\shortchaplabel#1{%
+ % This space should be enough, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % But use \hss just in case.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in by \shortchapentry above.)
+ %
+ % We'd like to right-justify chapter numbers, but that looks strange
+ % with appendix letters. And right-justifying numbers and
+ % left-justifying letters looks strange when there is less than 10
+ % chapters. Have to read the whole toc once to know how many chapters
+ % there are before deciding ...
+ \hbox to 1em{#1\hss}%
+}
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapters, in the main contents.
+\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}}
+%
+% Chapters, in the short toc.
+% See comments in \dochapentry re vbox and related settings.
+\def\shortchapentry#1#2#3#4{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}%
+}
+
+% Appendices, in the main contents.
+% Need the word Appendix, and a fixed-size box.
+%
+\def\appendixbox#1{%
+ % We use M since it's probably the widest letter.
+ \setbox0 = \hbox{\putwordAppendix{} M}%
+ \hbox to \wd0{\putwordAppendix{} #1\hss}}
+%
+\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}}
+
+% Unnumbered chapters.
+\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}}
+\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}}
+
+% Sections.
+\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}}
+\let\appsecentry=\numsecentry
+\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}}
+
+% Subsections.
+\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsecentry=\numsubsecentry
+\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}}
+
+% And subsubsections.
+\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}}
+\let\appsubsubsecentry=\numsubsubsecentry
+\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}}
+
+% This parameter controls the indentation of the various levels.
+% Same as \defaultparindent.
+\newdimen\tocindent \tocindent = 15pt
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we want it to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
+ \begingroup
+ \chapentryfonts
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip plus.1\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno\bgroup#2\egroup}%
+\endgroup}
+
+% We use the same \entry macro as for the index entries.
+\let\tocentry = \entry
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\def\subsecentryfonts{\textfonts}
+\def\subsubsecentryfonts{\textfonts}
+
+
+\message{environments,}
+% @foo ... @end foo.
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+%
+% Since these characters are used in examples, it should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+%
+\def\point{$\star$}
+\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% The @error{} command.
+% Adapted from the TeXbook's \boxit.
+%
+\newbox\errorbox
+%
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt}
+%
+\setbox\errorbox=\hbox to \dimen0{\hfil
+ \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+ \advance\hsize by -2\dimen2 % Rules.
+ \vbox{%
+ \hrule height\dimen2
+ \hbox{\vrule width\dimen2 \kern3pt % Space to left of text.
+ \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+ \kern3pt\vrule width\dimen2}% Space to right.
+ \hrule height\dimen2}
+ \hfil}
+%
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @tex ... @end tex escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\envdef\tex{%
+ \catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+ \catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+ \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie
+ \catcode `\%=14
+ \catcode `\+=\other
+ \catcode `\"=\other
+ \catcode `\|=\other
+ \catcode `\<=\other
+ \catcode `\>=\other
+ \escapechar=`\\
+ %
+ \let\b=\ptexb
+ \let\bullet=\ptexbullet
+ \let\c=\ptexc
+ \let\,=\ptexcomma
+ \let\.=\ptexdot
+ \let\dots=\ptexdots
+ \let\equiv=\ptexequiv
+ \let\!=\ptexexclam
+ \let\i=\ptexi
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \let\{=\ptexlbrace
+ \let\+=\tabalign
+ \let\}=\ptexrbrace
+ \let\/=\ptexslash
+ \let\*=\ptexstar
+ \let\t=\ptext
+ \let\frenchspacing=\plainfrenchspacing
+ %
+ \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+ \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+ \def\@{@}%
+}
+% There is no need to define \Etex.
+
+% Define @lisp ... @end lisp.
+% @lisp environment forms a group so it can rebind things,
+% including the definition of @end lisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments. \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical. We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip.
+%
+\def\aboveenvbreak{{%
+ % =10000 instead of <10000 because of a special case in \itemzzz and
+ % \sectionheading, q.v.
+ \ifnum \lastpenalty=10000 \else
+ \advance\envskipamount by \parskip
+ \endgraf
+ \ifdim\lastskip<\envskipamount
+ \removelastskip
+ % it's not a good place to break if the last penalty was \nobreak
+ % or better ...
+ \ifnum\lastpenalty<10000 \penalty-50 \fi
+ \vskip\envskipamount
+ \fi
+ \fi
+}}
+
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will
+% also clear it, so that its embedded environments do the narrowing again.
+\let\nonarrowing=\relax
+
+% @cartouche ... @end cartouche: draw rectangle w/rounded corners around
+% environment contents.
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+ \ctl\leaders\hrule height\circthick\hfil\ctr
+ \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+ \cbl\leaders\hrule height\circthick\hfil\cbr
+ \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\envdef\cartouche{%
+ \ifhmode\par\fi % can't be in the midst of a paragraph.
+ \startsavinginserts
+ \lskip=\leftskip \rskip=\rightskip
+ \leftskip=0pt\rightskip=0pt % we want these *outside*.
+ \cartinner=\hsize \advance\cartinner by-\lskip
+ \advance\cartinner by-\rskip
+ \cartouter=\hsize
+ \advance\cartouter by 18.4pt % allow for 3pt kerns on either
+ % side, and for 6pt waste from
+ % each corner char, and rule thickness
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ % Flag to tell @lisp, etc., not to narrow margin.
+ \let\nonarrowing = t%
+ \vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \kern3pt
+ \hsize=\cartinner
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+ \comment % For explanation, see the end of \def\group.
+}
+\def\Ecartouche{%
+ \ifhmode\par\fi
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+ \checkinserts
+}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\def\nonfillstart{%
+ \aboveenvbreak
+ \hfuzz = 12pt % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \let\par = \lisppar % don't ignore blank lines
+ \obeylines % each line of input is a line of output
+ \parskip = 0pt
+ \parindent = 0pt
+ \emergencystretch = 0pt % don't try to avoid overfull boxes
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \let\exdent=\nofillexdent
+}
+
+% If you want all examples etc. small: @set dispenvsize small.
+% If you want even small examples the full size: @set dispenvsize nosmall.
+% This affects the following displayed environments:
+% @example, @display, @format, @lisp
+%
+\def\smallword{small}
+\def\nosmallword{nosmall}
+\let\SETdispenvsize\relax
+\def\setnormaldispenv{%
+ \ifx\SETdispenvsize\smallword
+ \smallexamplefonts \rm
+ \fi
+}
+\def\setsmalldispenv{%
+ \ifx\SETdispenvsize\nosmallword
+ \else
+ \smallexamplefonts \rm
+ \fi
+}
+
+% We often define two environments, @foo and @smallfoo.
+% Let's do it by one command:
+\def\makedispenv #1#2{
+ \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}
+ \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}
+ \expandafter\let\csname E#1\endcsname \afterenvbreak
+ \expandafter\let\csname Esmall#1\endcsname \afterenvbreak
+}
+
+% Define two synonyms:
+\def\maketwodispenvs #1#2#3{
+ \makedispenv{#1}{#3}
+ \makedispenv{#2}{#3}
+}
+
+% @lisp: indented, narrowed, typewriter font; @example: same as @lisp.
+%
+% @smallexample and @smalllisp: use smaller fonts.
+% Originally contributed by Pavel@xerox.
+%
+\maketwodispenvs {lisp}{example}{%
+ \nonfillstart
+ \tt
+ \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special.
+ \gobble % eat return
+}
+
+% @display/@smalldisplay: same as @lisp except keep current font.
+%
+\makedispenv {display}{%
+ \nonfillstart
+ \gobble
+}
+
+% @format/@smallformat: same as @display except don't narrow margins.
+%
+\makedispenv{format}{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+
+% @flushleft: same as @format, but doesn't obey \SETdispenvsize.
+\envdef\flushleft{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \gobble
+}
+\let\Eflushleft = \afterenvbreak
+
+% @flushright.
+%
+\envdef\flushright{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ \advance\leftskip by 0pt plus 1fill
+ \gobble
+}
+\let\Eflushright = \afterenvbreak
+
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins. We keep \parskip nonzero in general, since
+% we're doing normal filling. So, when using \aboveenvbreak and
+% \afterenvbreak, temporarily make \parskip 0.
+%
+\envdef\quotation{%
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \parindent=0pt
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \advance\rightskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \else
+ \let\nonarrowing = \relax
+ \fi
+ \parsearg\quotationlabel
+}
+
+% We have retained a nonzero parskip for the environment, since we're
+% doing normal filling.
+%
+\def\Equotation{%
+ \par
+ \ifx\quotationauthor\undefined\else
+ % indent a bit.
+ \leftline{\kern 2\leftskip \sl ---\quotationauthor}%
+ \fi
+ {\parskip=0pt \afterenvbreak}%
+}
+
+% If we're given an argument, typeset it in bold with a colon after.
+\def\quotationlabel#1{%
+ \def\temp{#1}%
+ \ifx\temp\empty \else
+ {\bf #1: }%
+ \fi
+}
+
+
+% LaTeX-like @verbatim...@end verbatim and @verb{<char>...<char>}
+% If we want to allow any <char> as delimiter,
+% we need the curly braces so that makeinfo sees the @verb command, eg:
+% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org
+%
+% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook.
+%
+% [Knuth] p.344; only we need to do the other characters Texinfo sets
+% active too. Otherwise, they get lost as the first character on a
+% verbatim line.
+\def\dospecials{%
+ \do\ \do\\\do\{\do\}\do\$\do\&%
+ \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~%
+ \do\<\do\>\do\|\do\@\do+\do\"%
+}
+%
+% [Knuth] p. 380
+\def\uncatcodespecials{%
+ \def\do##1{\catcode`##1=\other}\dospecials}
+%
+% [Knuth] pp. 380,381,391
+% Disable Spanish ligatures ?` and !` of \tt font
+\begingroup
+ \catcode`\`=\active\gdef`{\relax\lq}
+\endgroup
+%
+% Setup for the @verb command.
+%
+% Eight spaces for a tab
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }}
+\endgroup
+%
+\def\setupverb{%
+ \tt % easiest (and conventionally used) font for verbatim
+ \def\par{\leavevmode\endgraf}%
+ \catcode`\`=\active
+ \tabeightspaces
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+}
+
+% Setup for the @verbatim environment
+%
+% Real tab expansion
+\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount
+%
+\def\starttabbox{\setbox0=\hbox\bgroup}
+\begingroup
+ \catcode`\^^I=\active
+ \gdef\tabexpand{%
+ \catcode`\^^I=\active
+ \def^^I{\leavevmode\egroup
+ \dimen0=\wd0 % the width so far, or since the previous tab
+ \divide\dimen0 by\tabw
+ \multiply\dimen0 by\tabw % compute previous multiple of \tabw
+ \advance\dimen0 by\tabw % advance to next multiple of \tabw
+ \wd0=\dimen0 \box0 \starttabbox
+ }%
+ }
+\endgroup
+\def\setupverbatim{%
+ \let\nonarrowing = t%
+ \nonfillstart
+ % Easiest (and conventionally used) font for verbatim
+ \tt
+ \def\par{\leavevmode\egroup\box0\endgraf}%
+ \catcode`\`=\active
+ \tabexpand
+ % Respect line breaks,
+ % print special symbols as themselves, and
+ % make each space count
+ % must do in this order:
+ \obeylines \uncatcodespecials \sepspaces
+ \everypar{\starttabbox}%
+}
+
+% Do the @verb magic: verbatim text is quoted by unique
+% delimiter characters. Before first delimiter expect a
+% right brace, after last delimiter expect closing brace:
+%
+% \def\doverb'{'<char>#1<char>'}'{#1}
+%
+% [Knuth] p. 382; only eat outer {}
+\begingroup
+ \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other
+ \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next]
+\endgroup
+%
+\def\verb{\begingroup\setupverb\doverb}
+%
+%
+% Do the @verbatim magic: define the macro \doverbatim so that
+% the (first) argument ends when '@end verbatim' is reached, ie:
+%
+% \def\doverbatim#1@end verbatim{#1}
+%
+% For Texinfo it's a lot easier than for LaTeX,
+% because texinfo's \verbatim doesn't stop at '\end{verbatim}':
+% we need not redefine '\', '{' and '}'.
+%
+% Inspired by LaTeX's verbatim command set [latex.ltx]
+%
+\begingroup
+ \catcode`\ =\active
+ \obeylines %
+ % ignore everything up to the first ^^M, that's the newline at the end
+ % of the @verbatim input line itself. Otherwise we get an extra blank
+ % line in the output.
+ \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}%
+ % We really want {...\end verbatim} in the body of the macro, but
+ % without the active space; thus we have to use \xdef and \gobble.
+\endgroup
+%
+\envdef\verbatim{%
+ \setupverbatim\doverbatim
+}
+\let\Everbatim = \afterenvbreak
+
+
+% @verbatiminclude FILE - insert text of file in verbatim environment.
+%
+\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude}
+%
+\def\doverbatiminclude#1{%
+ {%
+ \makevalueexpandable
+ \setupverbatim
+ \input #1
+ \afterenvbreak
+ }%
+}
+
+% @copying ... @end copying.
+% Save the text away for @insertcopying later.
+%
+% We save the uninterpreted tokens, rather than creating a box.
+% Saving the text in a box would be much easier, but then all the
+% typesetting commands (@smallbook, font changes, etc.) have to be done
+% beforehand -- and a) we want @copying to be done first in the source
+% file; b) letting users define the frontmatter in as flexible order as
+% possible is very desirable.
+%
+\def\copying{\checkenv{}\begingroup\scanargctxt\docopying}
+\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}}
+%
+\def\insertcopying{%
+ \begingroup
+ \parindent = 0pt % paragraph indentation looks wrong on title page
+ \scanexp\copyingtext
+ \endgroup
+}
+
+\message{defuns,}
+% @defun etc.
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+
+% Start the processing of @deffn:
+\def\startdefun{%
+ \ifnum\lastpenalty<10000
+ \medbreak
+ \else
+ % If there are two @def commands in a row, we'll have a \nobreak,
+ % which is there to keep the function description together with its
+ % header. But if there's nothing but headers, we need to allow a
+ % break somewhere. Check specifically for penalty 10002, inserted
+ % by \defargscommonending, instead of 10000, since the sectioning
+ % commands also insert a nobreak penalty, and we don't want to allow
+ % a break between a section heading and a defun.
+ %
+ \ifnum\lastpenalty=10002 \penalty2000 \fi
+ %
+ % Similarly, after a section heading, do not allow a break.
+ % But do insert the glue.
+ \medskip % preceded by discardable penalty, so not a breakpoint
+ \fi
+ %
+ \parindent=0in
+ \advance\leftskip by \defbodyindent
+ \exdentamount=\defbodyindent
+}
+
+\def\dodefunx#1{%
+ % First, check whether we are in the right environment:
+ \checkenv#1%
+ %
+ % As above, allow line break if we have multiple x headers in a row.
+ % It's not a great place, though.
+ \ifnum\lastpenalty=10002 \penalty3000 \fi
+ %
+ % And now, it's time to reuse the body of the original defun:
+ \expandafter\gobbledefun#1%
+}
+\def\gobbledefun#1\startdefun{}
+
+% \printdefunline \deffnheader{text}
+%
+\def\printdefunline#1#2{%
+ \begingroup
+ % call \deffnheader:
+ #1#2 \endheader
+ % common ending:
+ \interlinepenalty = 10000
+ \advance\rightskip by 0pt plus 1fil
+ \endgraf
+ \nobreak\vskip -\parskip
+ \penalty 10002 % signal to \startdefun and \dodefunx
+ % Some of the @defun-type tags do not enable magic parentheses,
+ % rendering the following check redundant. But we don't optimize.
+ \checkparencounts
+ \endgroup
+}
+
+\def\Edefun{\endgraf\medbreak}
+
+% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn;
+% the only thing remainnig is to define \deffnheader.
+%
+\def\makedefun#1{%
+ \expandafter\let\csname E#1\endcsname = \Edefun
+ \edef\temp{\noexpand\domakedefun
+ \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}%
+ \temp
+}
+
+% \domakedefun \deffn \deffnx \deffnheader
+%
+% Define \deffn and \deffnx, without parameters.
+% \deffnheader has to be defined explicitly.
+%
+\def\domakedefun#1#2#3{%
+ \envdef#1{%
+ \startdefun
+ \parseargusing\activeparens{\printdefunline#3}%
+ }%
+ \def#2{\dodefunx#1}%
+ \def#3%
+}
+
+%%% Untyped functions:
+
+% @deffn category name args
+\makedefun{deffn}{\deffngeneral{}}
+
+% @deffn category class name args
+\makedefun{defop}#1 {\defopon{#1\ \putwordon}}
+
+% \defopon {category on}class name args
+\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deffngeneral {subind}category name args
+%
+\def\deffngeneral#1#2 #3 #4\endheader{%
+ % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}.
+ \dosubind{fn}{\code{#3}}{#1}%
+ \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}%
+}
+
+%%% Typed functions:
+
+% @deftypefn category type name args
+\makedefun{deftypefn}{\deftypefngeneral{}}
+
+% @deftypeop category class type name args
+\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}}
+
+% \deftypeopon {category on}class type name args
+\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypefngeneral {subind}category type name args
+%
+\def\deftypefngeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{fn}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+%%% Typed variables:
+
+% @deftypevr category type var args
+\makedefun{deftypevr}{\deftypecvgeneral{}}
+
+% @deftypecv category class type var args
+\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}}
+
+% \deftypecvof {category of}class type var args
+\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} }
+
+% \deftypecvgeneral {subind}category type var args
+%
+\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{%
+ \dosubind{vr}{\code{#4}}{#1}%
+ \defname{#2}{#3}{#4}\defunargs{#5\unskip}%
+}
+
+%%% Untyped variables:
+
+% @defvr category var args
+\makedefun{defvr}#1 {\deftypevrheader{#1} {} }
+
+% @defcv category class var args
+\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}}
+
+% \defcvof {category of}class var args
+\def\defcvof#1#2 {\deftypecvof{#1}#2 {} }
+
+%%% Type:
+% @deftp category name args
+\makedefun{deftp}#1 #2 #3\endheader{%
+ \doind{tp}{\code{#2}}%
+ \defname{#1}{}{#2}\defunargs{#3\unskip}%
+}
+
+% Remaining @defun-like shortcuts:
+\makedefun{defun}{\deffnheader{\putwordDeffunc} }
+\makedefun{defmac}{\deffnheader{\putwordDefmac} }
+\makedefun{defspec}{\deffnheader{\putwordDefspec} }
+\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} }
+\makedefun{defvar}{\defvrheader{\putwordDefvar} }
+\makedefun{defopt}{\defvrheader{\putwordDefopt} }
+\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} }
+\makedefun{defmethod}{\defopon\putwordMethodon}
+\makedefun{deftypemethod}{\deftypeopon\putwordMethodon}
+\makedefun{defivar}{\defcvof\putwordInstanceVariableof}
+\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof}
+
+% \defname, which formats the name of the @def (not the args).
+% #1 is the category, such as "Function".
+% #2 is the return type, if any.
+% #3 is the function name.
+%
+% We are followed by (but not passed) the arguments, if any.
+%
+\def\defname#1#2#3{%
+ % Get the values of \leftskip and \rightskip as they were outside the @def...
+ \advance\leftskip by -\defbodyindent
+ %
+ % How we'll format the type name. Putting it in brackets helps
+ % distinguish it from the body text that may end up on the next line
+ % just below it.
+ \def\temp{#1}%
+ \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi}
+ %
+ % Figure out line sizes for the paragraph shape.
+ % The first line needs space for \box0; but if \rightskip is nonzero,
+ % we need only space for the part of \box0 which exceeds it:
+ \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip
+ % The continuations:
+ \dimen2=\hsize \advance\dimen2 by -\defargsindent
+ % (plain.tex says that \dimen1 should be used only as global.)
+ \parshape 2 0in \dimen0 \defargsindent \dimen2
+ %
+ % Put the type name to the right margin.
+ \noindent
+ \hbox to 0pt{%
+ \hfil\box0 \kern-\hsize
+ % \hsize has to be shortened this way:
+ \kern\leftskip
+ % Intentionally do not respect \rightskip, since we need the space.
+ }%
+ %
+ % Allow all lines to be underfull without complaint:
+ \tolerance=10000 \hbadness=10000
+ \exdentamount=\defbodyindent
+ {%
+ % defun fonts. We use typewriter by default (used to be bold) because:
+ % . we're printing identifiers, they should be in tt in principle.
+ % . in languages with many accents, such as Czech or French, it's
+ % common to leave accents off identifiers. The result looks ok in
+ % tt, but exceedingly strange in rm.
+ % . we don't want -- and --- to be treated as ligatures.
+ % . this still does not fix the ?` and !` ligatures, but so far no
+ % one has made identifiers using them :).
+ \df \tt
+ \def\temp{#2}% return value type
+ \ifx\temp\empty\else \tclose{\temp} \fi
+ #3% output function name
+ }%
+ {\rm\enskip}% hskip 0.5 em of \tenrm
+ %
+ \boldbrax
+ % arguments will be output next, if any.
+}
+
+% Print arguments in slanted roman (not ttsl), inconsistently with using
+% tt for the name. This is because literal text is sometimes needed in
+% the argument list (groff manual), and ttsl and tt are not very
+% distinguishable. Prevent hyphenation at `-' chars.
+%
+\def\defunargs#1{%
+ % use sl by default (not ttsl),
+ % tt for the names.
+ \df \sl \hyphenchar\font=0
+ %
+ % On the other hand, if an argument has two dashes (for instance), we
+ % want a way to get ttsl. Let's try @var for that.
+ \let\var=\ttslanted
+ #1%
+ \sl\hyphenchar\font=45
+}
+
+% We want ()&[] to print specially on the defun line.
+%
+\def\activeparens{%
+ \catcode`\(=\active \catcode`\)=\active
+ \catcode`\[=\active \catcode`\]=\active
+ \catcode`\&=\active
+}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+% Be sure that we always have a definition for `(', etc. For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+{
+ \activeparens
+ \global\let(=\lparen \global\let)=\rparen
+ \global\let[=\lbrack \global\let]=\rbrack
+ \global\let& = \&
+
+ \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+ \gdef\magicamp{\let&=\amprm}
+}
+
+\newcount\parencount
+
+% If we encounter &foo, then turn on ()-hacking afterwards
+\newif\ifampseen
+\def\amprm#1 {\ampseentrue{\bf\&#1 }}
+
+\def\parenfont{%
+ \ifampseen
+ % At the first level, print parens in roman,
+ % otherwise use the default font.
+ \ifnum \parencount=1 \rm \fi
+ \else
+ % The \sf parens (in \boldbrax) actually are a little bolder than
+ % the contained text. This is especially needed for [ and ] .
+ \sf
+ \fi
+}
+\def\infirstlevel#1{%
+ \ifampseen
+ \ifnum\parencount=1
+ #1%
+ \fi
+ \fi
+}
+\def\bfafterword#1 {#1 \bf}
+
+\def\opnr{%
+ \global\advance\parencount by 1
+ {\parenfont(}%
+ \infirstlevel \bfafterword
+}
+\def\clnr{%
+ {\parenfont)}%
+ \infirstlevel \sl
+ \global\advance\parencount by -1
+}
+
+\newcount\brackcount
+\def\lbrb{%
+ \global\advance\brackcount by 1
+ {\bf[}%
+}
+\def\rbrb{%
+ {\bf]}%
+ \global\advance\brackcount by -1
+}
+
+\def\checkparencounts{%
+ \ifnum\parencount=0 \else \badparencount \fi
+ \ifnum\brackcount=0 \else \badbrackcount \fi
+}
+\def\badparencount{%
+ \errmessage{Unbalanced parentheses in @def}%
+ \global\parencount=0
+}
+\def\badbrackcount{%
+ \errmessage{Unbalanced square braces in @def}%
+ \global\brackcount=0
+}
+
+
+\message{macros,}
+% @macro.
+
+% To do this right we need a feature of e-TeX, \scantokens,
+% which we arrange to emulate with a temporary file in ordinary TeX.
+\ifx\eTeXversion\undefined
+ \newwrite\macscribble
+ \def\scantokens#1{%
+ \toks0={#1}%
+ \immediate\openout\macscribble=\jobname.tmp
+ \immediate\write\macscribble{\the\toks0}%
+ \immediate\closeout\macscribble
+ \input \jobname.tmp
+ }
+\fi
+
+\def\scanmacro#1{%
+ \begingroup
+ \newlinechar`\^^M
+ \let\xeatspaces\eatspaces
+ % Undo catcode changes of \startcontents and \doprintindex
+ % When called from @insertcopying or (short)caption, we need active
+ % backslash to get it printed correctly. Previously, we had
+ % \catcode`\\=\other instead. We'll see whether a problem appears
+ % with macro expansion. --kasal, 19aug04
+ \catcode`\@=0 \catcode`\\=\active \escapechar=`\@
+ % ... and \example
+ \spaceisspace
+ %
+ % Append \endinput to make sure that TeX does not see the ending newline.
+ %
+ % I've verified that it is necessary both for e-TeX and for ordinary TeX
+ % --kasal, 29nov03
+ \scantokens{#1\endinput}%
+ \endgroup
+}
+
+\def\scanexp#1{%
+ \edef\temp{\noexpand\scanmacro{#1}}%
+ \temp
+}
+
+\newcount\paramno % Count of parameters
+\newtoks\macname % Macro name
+\newif\ifrecursive % Is it recursive?
+
+% List of all defined macros in the form
+% \definedummyword\macro1\definedummyword\macro2...
+% Currently is also contains all @aliases; the list can be split
+% if there is a need.
+\def\macrolist{}
+
+% Add the macro to \macrolist
+\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname}
+\def\addtomacrolistxxx#1{%
+ \toks0 = \expandafter{\macrolist\definedummyword#1}%
+ \xdef\macrolist{\the\toks0}%
+}
+
+% Utility routines.
+% This does \let #1 = #2, with \csnames; that is,
+% \let \csname#1\endcsname = \csname#2\endcsname
+% (except of course we have to play expansion games).
+%
+\def\cslet#1#2{%
+ \expandafter\let
+ \csname#1\expandafter\endcsname
+ \csname#2\endcsname
+}
+
+% Trim leading and trailing spaces off a string.
+% Concepts from aro-bend problem 15 (see CTAN).
+{\catcode`\@=11
+\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }}
+\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@}
+\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @}
+\def\unbrace#1{#1}
+\unbrace{\gdef\trim@@@ #1 } #2@{#1}
+}
+
+% Trim a single trailing ^^M off a string.
+{\catcode`\^^M=\other \catcode`\Q=3%
+\gdef\eatcr #1{\eatcra #1Q^^MQ}%
+\gdef\eatcra#1^^MQ{\eatcrb#1Q}%
+\gdef\eatcrb#1Q#2Q{#1}%
+}
+
+% Macro bodies are absorbed as an argument in a context where
+% all characters are catcode 10, 11 or 12, except \ which is active
+% (as in normal texinfo). It is necessary to change the definition of \.
+
+% It's necessary to have hard CRs when the macro is executed. This is
+% done by making ^^M (\endlinechar) catcode 12 when reading the macro
+% body, and then making it the \newlinechar in \scanmacro.
+
+\def\scanctxt{%
+ \catcode`\"=\other
+ \catcode`\+=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\@=\other
+ \catcode`\^=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\~=\other
+}
+
+\def\scanargctxt{%
+ \scanctxt
+ \catcode`\\=\other
+ \catcode`\^^M=\other
+}
+
+\def\macrobodyctxt{%
+ \scanctxt
+ \catcode`\{=\other
+ \catcode`\}=\other
+ \catcode`\^^M=\other
+ \usembodybackslash
+}
+
+\def\macroargctxt{%
+ \scanctxt
+ \catcode`\\=\other
+}
+
+% \mbodybackslash is the definition of \ in @macro bodies.
+% It maps \foo\ => \csname macarg.foo\endcsname => #N
+% where N is the macro parameter number.
+% We define \csname macarg.\endcsname to be \realbackslash, so
+% \\ in macro replacement text gets you a backslash.
+
+{\catcode`@=0 @catcode`@\=@active
+ @gdef@usembodybackslash{@let\=@mbodybackslash}
+ @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname}
+}
+\expandafter\def\csname macarg.\endcsname{\realbackslash}
+
+\def\macro{\recursivefalse\parsearg\macroxxx}
+\def\rmacro{\recursivetrue\parsearg\macroxxx}
+
+\def\macroxxx#1{%
+ \getargs{#1}% now \macname is the macname and \argl the arglist
+ \ifx\argl\empty % no arguments
+ \paramno=0%
+ \else
+ \expandafter\parsemargdef \argl;%
+ \fi
+ \if1\csname ismacro.\the\macname\endcsname
+ \message{Warning: redefining \the\macname}%
+ \else
+ \expandafter\ifx\csname \the\macname\endcsname \relax
+ \else \errmessage{Macro name \the\macname\space already defined}\fi
+ \global\cslet{macsave.\the\macname}{\the\macname}%
+ \global\expandafter\let\csname ismacro.\the\macname\endcsname=1%
+ \addtomacrolist{\the\macname}%
+ \fi
+ \begingroup \macrobodyctxt
+ \ifrecursive \expandafter\parsermacbody
+ \else \expandafter\parsemacbody
+ \fi}
+
+\parseargdef\unmacro{%
+ \if1\csname ismacro.#1\endcsname
+ \global\cslet{#1}{macsave.#1}%
+ \global\expandafter\let \csname ismacro.#1\endcsname=0%
+ % Remove the macro name from \macrolist:
+ \begingroup
+ \expandafter\let\csname#1\endcsname \relax
+ \let\definedummyword\unmacrodo
+ \xdef\macrolist{\macrolist}%
+ \endgroup
+ \else
+ \errmessage{Macro #1 not defined}%
+ \fi
+}
+
+% Called by \do from \dounmacro on each macro. The idea is to omit any
+% macro definitions that have been changed to \relax.
+%
+\def\unmacrodo#1{%
+ \ifx #1\relax
+ % remove this
+ \else
+ \noexpand\definedummyword \noexpand#1%
+ \fi
+}
+
+% This makes use of the obscure feature that if the last token of a
+% <parameter list> is #, then the preceding argument is delimited by
+% an opening brace, and that opening brace is not consumed.
+\def\getargs#1{\getargsxxx#1{}}
+\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs}
+\def\getmacname #1 #2\relax{\macname={#1}}
+\def\getmacargs#1{\def\argl{#1}}
+
+% Parse the optional {params} list. Set up \paramno and \paramlist
+% so \defmacro knows what to do. Define \macarg.blah for each blah
+% in the params list, to be ##N where N is the position in that list.
+% That gets used by \mbodybackslash (above).
+
+% We need to get `macro parameter char #' into several definitions.
+% The technique used is stolen from LaTeX: let \hash be something
+% unexpandable, insert that wherever you need a #, and then redefine
+% it to # just before using the token list produced.
+%
+% The same technique is used to protect \eatspaces till just before
+% the macro is used.
+
+\def\parsemargdef#1;{\paramno=0\def\paramlist{}%
+ \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,}
+\def\parsemargdefxxx#1,{%
+ \if#1;\let\next=\relax
+ \else \let\next=\parsemargdefxxx
+ \advance\paramno by 1%
+ \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname
+ {\xeatspaces{\hash\the\paramno}}%
+ \edef\paramlist{\paramlist\hash\the\paramno,}%
+ \fi\next}
+
+% These two commands read recursive and nonrecursive macro bodies.
+% (They're different since rec and nonrec macros end differently.)
+
+\long\def\parsemacbody#1@end macro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+\long\def\parsermacbody#1@end rmacro%
+{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}%
+
+% This defines the macro itself. There are six cases: recursive and
+% nonrecursive macros of zero, one, and many arguments.
+% Much magic with \expandafter here.
+% \xdef is used so that macro definitions will survive the file
+% they're defined in; @include reads the file inside a group.
+\def\defmacro{%
+ \let\hash=##% convert placeholders to macro parameter chars
+ \ifrecursive
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\scanmacro{\temp}}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+ \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+ \egroup\noexpand\scanmacro{\temp}}%
+ \else % many
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\csname\the\macname xx\endcsname}%
+ \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname xxx\endcsname
+ \paramlist{\egroup\noexpand\scanmacro{\temp}}%
+ \fi
+ \else
+ \ifcase\paramno
+ % 0
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \or % 1
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \noexpand\braceorline
+ \expandafter\noexpand\csname\the\macname xxx\endcsname}%
+ \expandafter\xdef\csname\the\macname xxx\endcsname##1{%
+ \egroup
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \else % many
+ \expandafter\xdef\csname\the\macname\endcsname{%
+ \bgroup\noexpand\macroargctxt
+ \expandafter\noexpand\csname\the\macname xx\endcsname}%
+ \expandafter\xdef\csname\the\macname xx\endcsname##1{%
+ \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}%
+ \expandafter\expandafter
+ \expandafter\xdef
+ \expandafter\expandafter
+ \csname\the\macname xxx\endcsname
+ \paramlist{%
+ \egroup
+ \noexpand\norecurse{\the\macname}%
+ \noexpand\scanmacro{\temp}\egroup}%
+ \fi
+ \fi}
+
+\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}}
+
+% \braceorline decides whether the next nonwhitespace character is a
+% {. If so it reads up to the closing }, if not, it reads the whole
+% line. Whatever was read is then fed to the next control sequence
+% as an argument (by \parsebrace or \parsearg)
+\def\braceorline#1{\let\next=#1\futurelet\nchar\braceorlinexxx}
+\def\braceorlinexxx{%
+ \ifx\nchar\bgroup\else
+ \expandafter\parsearg
+ \fi \next}
+
+
+% @alias.
+% We need some trickery to remove the optional spaces around the equal
+% sign. Just make them active and then expand them all to nothing.
+\def\alias{\parseargusing\obeyspaces\aliasxxx}
+\def\aliasxxx #1{\aliasyyy#1\relax}
+\def\aliasyyy #1=#2\relax{%
+ {%
+ \expandafter\let\obeyedspace=\empty
+ \addtomacrolist{#1}%
+ \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}%
+ }%
+ \next
+}
+
+
+\message{cross references,}
+
+\newwrite\auxfile
+
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
+
+% @inforef is relatively simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+% @node's only job in TeX is to define \lastnode, which is used in
+% cross-references. The @node line might or might not have commas, and
+% might or might not have spaces before the first comma, like:
+% @node foo , bar , ...
+% We don't want such trailing spaces in the node name.
+%
+\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse}
+%
+% also remove a trailing comma, in case of something like this:
+% @node Help-Cross, , , Cross-refs
+\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse}
+\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}}
+
+\let\nwnode=\node
+\let\lastnode=\empty
+
+% Write a cross-reference definition for the current node. #1 is the
+% type (Ynumbered, Yappendix, Ynothing).
+%
+\def\donoderef#1{%
+ \ifx\lastnode\empty\else
+ \setref{\lastnode}{#1}%
+ \global\let\lastnode=\empty
+ \fi
+}
+
+% @anchor{NAME} -- define xref target at arbitrary point.
+%
+\newcount\savesfregister
+%
+\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi}
+\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi}
+\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces}
+
+% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an
+% anchor), which consists of three parts:
+% 1) NAME-title - the current sectioning name taken from \thissection,
+% or the anchor name.
+% 2) NAME-snt - section number and type, passed as the SNT arg, or
+% empty for anchors.
+% 3) NAME-pg - the page number.
+%
+% This is called from \donoderef, \anchor, and \dofloat. In the case of
+% floats, there is an additional part, which is not written here:
+% 4) NAME-lof - the text as it should appear in a @listoffloats.
+%
+\def\setref#1#2{%
+ \pdfmkdest{#1}%
+ \iflinks
+ {%
+ \atdummies % preserve commands, but don't expand them
+ \edef\writexrdef##1##2{%
+ \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef
+ ##1}{##2}}% these are parameters of \writexrdef
+ }%
+ \toks0 = \expandafter{\thissection}%
+ \immediate \writexrdef{title}{\the\toks0 }%
+ \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc.
+ \writexrdef{pg}{\folio}% will be written later, during \shipout
+ }%
+ \fi
+}
+
+% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is
+% the node name, #2 the name of the Info cross-reference, #3 the printed
+% node name, #4 the name of the Info file, #5 the name of the printed
+% manual. All but the node name can be omitted.
+%
+\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
+\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
+\def\ref#1{\xrefX[#1,,,,,,,]}
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+ \unsepspaces
+ \def\printedmanual{\ignorespaces #5}%
+ \def\printedrefname{\ignorespaces #3}%
+ \setbox1=\hbox{\printedmanual\unskip}%
+ \setbox0=\hbox{\printedrefname\unskip}%
+ \ifdim \wd0 = 0pt
+ % No printed node name was explicitly given.
+ \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
+ % Use the node name inside the square brackets.
+ \def\printedrefname{\ignorespaces #1}%
+ \else
+ % Use the actual chapter/section title appear inside
+ % the square brackets. Use the real section title if we have it.
+ \ifdim \wd1 > 0pt
+ % It is in another manual, so we don't have it.
+ \def\printedrefname{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We know the real title if we have the xref values.
+ \def\printedrefname{\refx{#1-title}{}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printedrefname{\ignorespaces #1}%
+ \fi%
+ \fi
+ \fi
+ \fi
+ %
+ % Make link in pdf output.
+ \ifpdf
+ \leavevmode
+ \getfilename{#4}%
+ {\turnoffactive
+ % See comments at \activebackslashdouble.
+ {\activebackslashdouble \xdef\pdfxrefdest{#1}%
+ \backslashparens\pdfxrefdest}%
+ %
+ \ifnum\filenamelength>0
+ \startlink attr{/Border [0 0 0]}%
+ goto file{\the\filename.pdf} name{\pdfxrefdest}%
+ \else
+ \startlink attr{/Border [0 0 0]}%
+ goto name{\pdfmkpgn{\pdfxrefdest}}%
+ \fi
+ }%
+ \linkcolor
+ \fi
+ %
+ % Float references are printed completely differently: "Figure 1.2"
+ % instead of "[somenode], p.3". We distinguish them by the
+ % LABEL-title being set to a magic string.
+ {%
+ % Have to otherify everything special to allow the \csname to
+ % include an _ in the xref name, etc.
+ \indexnofonts
+ \turnoffactive
+ \expandafter\global\expandafter\let\expandafter\Xthisreftitle
+ \csname XR#1-title\endcsname
+ }%
+ \iffloat\Xthisreftitle
+ % If the user specified the print name (third arg) to the ref,
+ % print it instead of our usual "Figure 1.2".
+ \ifdim\wd0 = 0pt
+ \refx{#1-snt}{}%
+ \else
+ \printedrefname
+ \fi
+ %
+ % if the user also gave the printed manual name (fifth arg), append
+ % "in MANUALNAME".
+ \ifdim \wd1 > 0pt
+ \space \putwordin{} \cite{\printedmanual}%
+ \fi
+ \else
+ % node/anchor (non-float) references.
+ %
+ % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+ % insert empty discretionaries after hyphens, which means that it will
+ % not find a line break at a hyphen in a node names. Since some manuals
+ % are best written with fairly long node names, containing hyphens, this
+ % is a loss. Therefore, we give the text of the node name again, so it
+ % is as if TeX is seeing it for the first time.
+ \ifdim \wd1 > 0pt
+ \putwordsection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}%
+ \else
+ % _ (for example) has to be the character _ for the purposes of the
+ % control sequence corresponding to the node, but it has to expand
+ % into the usual \leavevmode...\vrule stuff for purposes of
+ % printing. So we \turnoffactive for the \refx-snt, back on for the
+ % printing, back off for the \refx-pg.
+ {\turnoffactive
+ % Only output a following space if the -snt ref is nonempty; for
+ % @unnumbered and @anchor, it won't be.
+ \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}%
+ \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi
+ }%
+ % output the `[mynode]' via a macro so it can be overridden.
+ \xrefprintnodename\printedrefname
+ %
+ % But we always want a comma and a space:
+ ,\space
+ %
+ % output the `page 3'.
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ \fi
+ \fi
+ \endlink
+\endgroup}
+
+% This macro is called from \xrefX for the `[nodename]' part of xref
+% output. It's a separate macro only so it can be changed more easily,
+% since square brackets don't work well in some documents. Particularly
+% one that Bob is working on :).
+%
+\def\xrefprintnodename#1{[#1]}
+
+% Things referred to by \setref.
+%
+\def\Ynothing{}
+\def\Yomitfromtoc{}
+\def\Ynumbered{%
+ \ifnum\secno=0
+ \putwordChapter@tie \the\chapno
+ \else \ifnum\subsecno=0
+ \putwordSection@tie \the\chapno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+\def\Yappendix{%
+ \ifnum\secno=0
+ \putwordAppendix@tie @char\the\appendixno{}%
+ \else \ifnum\subsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno
+ \else \ifnum\subsubsecno=0
+ \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno
+ \else
+ \putwordSection@tie
+ @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno
+ \fi\fi\fi
+}
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+%
+\def\refx#1#2{%
+ {%
+ \indexnofonts
+ \otherbackslash
+ \expandafter\global\expandafter\let\expandafter\thisrefX
+ \csname XR#1\endcsname
+ }%
+ \ifx\thisrefX\relax
+ % If not defined, say something at least.
+ \angleleft un\-de\-fined\angleright
+ \iflinks
+ \ifhavexrefs
+ \message{\linenumber Undefined cross reference `#1'.}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \thisrefX
+ \fi
+ #2% Output the suffix in any case.
+}
+
+% This is the macro invoked by entries in the aux file. Usually it's
+% just a \def (we prepend XR to the control sequence name to avoid
+% collisions). But if this is a float type, we have more work to do.
+%
+\def\xrdef#1#2{%
+ \expandafter\gdef\csname XR#1\endcsname{#2}% remember this xref value.
+ %
+ % Was that xref control sequence that we just defined for a float?
+ \expandafter\iffloat\csname XR#1\endcsname
+ % it was a float, and we have the (safe) float type in \iffloattype.
+ \expandafter\let\expandafter\floatlist
+ \csname floatlist\iffloattype\endcsname
+ %
+ % Is this the first time we've seen this float type?
+ \expandafter\ifx\floatlist\relax
+ \toks0 = {\do}% yes, so just \do
+ \else
+ % had it before, so preserve previous elements in list.
+ \toks0 = \expandafter{\floatlist\do}%
+ \fi
+ %
+ % Remember this xref in the control sequence \floatlistFLOATTYPE,
+ % for later use in \listoffloats.
+ \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0{#1}}%
+ \fi
+}
+
+% Read the last existing aux file, if any. No error if none exists.
+%
+\def\tryauxfile{%
+ \openin 1 \jobname.aux
+ \ifeof 1 \else
+ \readdatafile{aux}%
+ \global\havexrefstrue
+ \fi
+ \closein 1
+}
+
+\def\setupdatafile{%
+ \catcode`\^^@=\other
+ \catcode`\^^A=\other
+ \catcode`\^^B=\other
+ \catcode`\^^C=\other
+ \catcode`\^^D=\other
+ \catcode`\^^E=\other
+ \catcode`\^^F=\other
+ \catcode`\^^G=\other
+ \catcode`\^^H=\other
+ \catcode`\^^K=\other
+ \catcode`\^^L=\other
+ \catcode`\^^N=\other
+ \catcode`\^^P=\other
+ \catcode`\^^Q=\other
+ \catcode`\^^R=\other
+ \catcode`\^^S=\other
+ \catcode`\^^T=\other
+ \catcode`\^^U=\other
+ \catcode`\^^V=\other
+ \catcode`\^^W=\other
+ \catcode`\^^X=\other
+ \catcode`\^^Z=\other
+ \catcode`\^^[=\other
+ \catcode`\^^\=\other
+ \catcode`\^^]=\other
+ \catcode`\^^^=\other
+ \catcode`\^^_=\other
+ % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc.
+ % in xref tags, i.e., node names. But since ^^e4 notation isn't
+ % supported in the main text, it doesn't seem desirable. Furthermore,
+ % that is not enough: for node names that actually contain a ^
+ % character, we would end up writing a line like this: 'xrdef {'hat
+ % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
+ % argument, and \hat is not an expandable control sequence. It could
+ % all be worked out, but why? Either we support ^^ or we don't.
+ %
+ % The other change necessary for this was to define \auxhat:
+ % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
+ % and then to call \auxhat in \setq.
+ %
+ \catcode`\^=\other
+ %
+ % Special characters. Should be turned off anyway, but...
+ \catcode`\~=\other
+ \catcode`\[=\other
+ \catcode`\]=\other
+ \catcode`\"=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\$=\other
+ \catcode`\#=\other
+ \catcode`\&=\other
+ \catcode`\%=\other
+ \catcode`+=\other % avoid \+ for paranoia even though we've turned it off
+ %
+ % This is to support \ in node names and titles, since the \
+ % characters end up in a \csname. It's easier than
+ % leaving it active and making its active definition an actual \
+ % character. What I don't understand is why it works in the *value*
+ % of the xrdef. Seems like it should be a catcode12 \, and that
+ % should not typeset properly. But it works, so I'm moving on for
+ % now. --karl, 15jan04.
+ \catcode`\\=\other
+ %
+ % Make the characters 128-255 be printing characters.
+ {%
+ \count1=128
+ \def\loop{%
+ \catcode\count1=\other
+ \advance\count1 by 1
+ \ifnum \count1<256 \loop \fi
+ }%
+ }%
+ %
+ % @ is our escape character in .aux files, and we need braces.
+ \catcode`\{=1
+ \catcode`\}=2
+ \catcode`\@=0
+}
+
+\def\readdatafile#1{%
+\begingroup
+ \setupdatafile
+ \input\jobname.#1
+\endgroup}
+
+\message{insertions,}
+% including footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for info output only.
+\let\footnotestyle=\comment
+
+{\catcode `\@=11
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \let\indent=\ptexindent
+ \let\noindent=\ptexnoindent
+ \global\advance\footnoteno by \@ne
+ \edef\thisfootno{$^{\the\footnoteno}$}%
+ %
+ % In case the footnote comes at the end of a sentence, preserve the
+ % extra spacing after we do the footnote number.
+ \let\@sf\empty
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \dofootnote
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter. Our footnotes don't need to be so general.
+%
+% Oh yes, they do; otherwise, @ifset (and anything else that uses
+% \parseargline) fails inside footnotes because the tokens are fixed when
+% the footnote is read. --karl, 16nov96.
+%
+\gdef\dofootnote{%
+ \insert\footins\bgroup
+ % We want to typeset this text as a normal paragraph, even if the
+ % footnote reference occurs in (for example) a display environment.
+ % So reset some parameters.
+ \hsize=\pagewidth
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM
+ \leftskip\z@skip
+ \rightskip\z@skip
+ \spaceskip\z@skip
+ \xspaceskip\z@skip
+ \parindent\defaultparindent
+ %
+ \smallfonts \rm
+ %
+ % Because we use hanging indentation in footnotes, a @noindent appears
+ % to exdent this text, so make it be a no-op. makeinfo does not use
+ % hanging indentation so @noindent can still be needed within footnote
+ % text after an @example or the like (not that this is good style).
+ \let\noindent = \relax
+ %
+ % Hang the footnote text off the number. Use \everypar in case the
+ % footnote extends for more than one paragraph.
+ \everypar = {\hang}%
+ \textindent{\thisfootno}%
+ %
+ % Don't crash into the line above the footnote text. Since this
+ % expands into a box, it must come within the paragraph, lest it
+ % provide a place where TeX can split the footnote.
+ \footstrut
+ \futurelet\next\fo@t
+}
+}%end \catcode `\@=11
+
+% In case a @footnote appears in a vbox, save the footnote text and create
+% the real \insert just after the vbox finished. Otherwise, the insertion
+% would be lost.
+% Similarly, if a @footnote appears inside an alignment, save the footnote
+% text to a box and make the \insert when a row of the table is finished.
+% And the same can be done for other insert classes. --kasal, 16nov03.
+
+% Replace the \insert primitive by a cheating macro.
+% Deeper inside, just make sure that the saved insertions are not spilled
+% out prematurely.
+%
+\def\startsavinginserts{%
+ \ifx \insert\ptexinsert
+ \let\insert\saveinsert
+ \else
+ \let\checkinserts\relax
+ \fi
+}
+
+% This \insert replacement works for both \insert\footins{foo} and
+% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}.
+%
+\def\saveinsert#1{%
+ \edef\next{\noexpand\savetobox \makeSAVEname#1}%
+ \afterassignment\next
+ % swallow the left brace
+ \let\temp =
+}
+\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}}
+\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1}
+
+\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi}
+
+\def\placesaveins#1{%
+ \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname
+ {\box#1}%
+}
+
+% eat @SAVE -- beware, all of them have catcode \other:
+{
+ \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-)
+ \gdef\gobblesave @SAVE{}
+}
+
+% initialization:
+\def\newsaveins #1{%
+ \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}%
+ \next
+}
+\def\newsaveinsX #1{%
+ \csname newbox\endcsname #1%
+ \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts
+ \checksaveins #1}%
+}
+
+% initialize:
+\let\checkinserts\empty
+\newsaveins\footins
+\newsaveins\margin
+
+
+% @image. We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front. If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = epsf.tex
+\ifeof 1 \else
+ % Do not bother showing banner with epsf.tex v2.7k (available in
+ % doc/epsf.tex and on ctan).
+ \def\epsfannounce{\toks0 = }%
+ \input epsf.tex
+\fi
+\closein 1
+%
+% We will only complain once about lack of epsf.tex.
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+ work. It is also included in the Texinfo distribution, or you can get
+ it from ftp://tug.org/tex/epsf.tex.}
+%
+\def\image#1{%
+ \ifx\epsfbox\undefined
+ \ifwarnednoepsf \else
+ \errhelp = \noepsfhelp
+ \errmessage{epsf.tex not found, images will be ignored}%
+ \global\warnednoepsftrue
+ \fi
+ \else
+ \imagexxx #1,,,,,\finish
+ \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is (ignored optional) html alt text.
+% #5 is (ignored optional) extension.
+% #6 is just the usual extra ignored arg for parsing this stuff.
+\newif\ifimagevmode
+\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup
+ \catcode`\^^M = 5 % in case we're inside an example
+ \normalturnoffactive % allow _ et al. in names
+ % If the image is by itself, center it.
+ \ifvmode
+ \imagevmodetrue
+ \nobreak\bigskip
+ % Usually we'll have text after the image which will insert
+ % \parskip glue, so insert it here too to equalize the space
+ % above and below.
+ \nobreak\vskip\parskip
+ \nobreak
+ \line\bgroup
+ \fi
+ %
+ % Output the image.
+ \ifpdf
+ \dopdfimage{#1}{#2}{#3}%
+ \else
+ % \epsfbox itself resets \epsf?size at each figure.
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+ \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+ \epsfbox{#1.eps}%
+ \fi
+ %
+ \ifimagevmode \egroup \bigbreak \fi % space after the image
+\endgroup}
+
+
+% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables,
+% etc. We don't actually implement floating yet, we always include the
+% float "here". But it seemed the best name for the future.
+%
+\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish}
+
+% There may be a space before second and/or third parameter; delete it.
+\def\eatcommaspace#1, {#1,}
+
+% #1 is the optional FLOATTYPE, the text label for this float, typically
+% "Figure", "Table", "Example", etc. Can't contain commas. If omitted,
+% this float will not be numbered and cannot be referred to.
+%
+% #2 is the optional xref label. Also must be present for the float to
+% be referable.
+%
+% #3 is the optional positioning argument; for now, it is ignored. It
+% will somehow specify the positions allowed to float to (here, top, bottom).
+%
+% We keep a separate counter for each FLOATTYPE, which we reset at each
+% chapter-level command.
+\let\resetallfloatnos=\empty
+%
+\def\dofloat#1,#2,#3,#4\finish{%
+ \let\thiscaption=\empty
+ \let\thisshortcaption=\empty
+ %
+ % don't lose footnotes inside @float.
+ %
+ % BEWARE: when the floats start float, we have to issue warning whenever an
+ % insert appears inside a float which could possibly float. --kasal, 26may04
+ %
+ \startsavinginserts
+ %
+ % We can't be used inside a paragraph.
+ \par
+ %
+ \vtop\bgroup
+ \def\floattype{#1}%
+ \def\floatlabel{#2}%
+ \def\floatloc{#3}% we do nothing with this yet.
+ %
+ \ifx\floattype\empty
+ \let\safefloattype=\empty
+ \else
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ \fi
+ %
+ % If label is given but no type, we handle that as the empty type.
+ \ifx\floatlabel\empty \else
+ % We want each FLOATTYPE to be numbered separately (Figure 1,
+ % Table 1, Figure 2, ...). (And if no label, no number.)
+ %
+ \expandafter\getfloatno\csname\safefloattype floatno\endcsname
+ \global\advance\floatno by 1
+ %
+ {%
+ % This magic value for \thissection is output by \setref as the
+ % XREFLABEL-title value. \xrefX uses it to distinguish float
+ % labels (which have a completely different output format) from
+ % node and anchor labels. And \xrdef uses it to construct the
+ % lists of floats.
+ %
+ \edef\thissection{\floatmagic=\safefloattype}%
+ \setref{\floatlabel}{Yfloat}%
+ }%
+ \fi
+ %
+ % start with \parskip glue, I guess.
+ \vskip\parskip
+ %
+ % Don't suppress indentation if a float happens to start a section.
+ \restorefirstparagraphindent
+}
+
+% we have these possibilities:
+% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap
+% @float Foo,lbl & no caption: Foo 1.1
+% @float Foo & @caption{Cap}: Foo: Cap
+% @float Foo & no caption: Foo
+% @float ,lbl & Caption{Cap}: 1.1: Cap
+% @float ,lbl & no caption: 1.1
+% @float & @caption{Cap}: Cap
+% @float & no caption:
+%
+\def\Efloat{%
+ \let\floatident = \empty
+ %
+ % In all cases, if we have a float type, it comes first.
+ \ifx\floattype\empty \else \def\floatident{\floattype}\fi
+ %
+ % If we have an xref label, the number comes next.
+ \ifx\floatlabel\empty \else
+ \ifx\floattype\empty \else % if also had float type, need tie first.
+ \appendtomacro\floatident{\tie}%
+ \fi
+ % the number.
+ \appendtomacro\floatident{\chaplevelprefix\the\floatno}%
+ \fi
+ %
+ % Start the printed caption with what we've constructed in
+ % \floatident, but keep it separate; we need \floatident again.
+ \let\captionline = \floatident
+ %
+ \ifx\thiscaption\empty \else
+ \ifx\floatident\empty \else
+ \appendtomacro\captionline{: }% had ident, so need a colon between
+ \fi
+ %
+ % caption text.
+ \appendtomacro\captionline{\scanexp\thiscaption}%
+ \fi
+ %
+ % If we have anything to print, print it, with space before.
+ % Eventually this needs to become an \insert.
+ \ifx\captionline\empty \else
+ \vskip.5\parskip
+ \captionline
+ %
+ % Space below caption.
+ \vskip\parskip
+ \fi
+ %
+ % If have an xref label, write the list of floats info. Do this
+ % after the caption, to avoid chance of it being a breakpoint.
+ \ifx\floatlabel\empty \else
+ % Write the text that goes in the lof to the aux file as
+ % \floatlabel-lof. Besides \floatident, we include the short
+ % caption if specified, else the full caption if specified, else nothing.
+ {%
+ \atdummies
+ %
+ % since we read the caption text in the macro world, where ^^M
+ % is turned into a normal character, we have to scan it back, so
+ % we don't write the literal three characters "^^M" into the aux file.
+ \scanexp{%
+ \xdef\noexpand\gtemp{%
+ \ifx\thisshortcaption\empty
+ \thiscaption
+ \else
+ \thisshortcaption
+ \fi
+ }%
+ }%
+ \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident
+ \ifx\gtemp\empty \else : \gtemp \fi}}%
+ }%
+ \fi
+ \egroup % end of \vtop
+ %
+ % place the captured inserts
+ %
+ % BEWARE: when the floats start floating, we have to issue warning
+ % whenever an insert appears inside a float which could possibly
+ % float. --kasal, 26may04
+ %
+ \checkinserts
+}
+
+% Append the tokens #2 to the definition of macro #1, not expanding either.
+%
+\def\appendtomacro#1#2{%
+ \expandafter\def\expandafter#1\expandafter{#1#2}%
+}
+
+% @caption, @shortcaption
+%
+\def\caption{\docaption\thiscaption}
+\def\shortcaption{\docaption\thisshortcaption}
+\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption}
+\def\defcaption#1#2{\egroup \def#1{#2}}
+
+% The parameter is the control sequence identifying the counter we are
+% going to use. Create it if it doesn't exist and assign it to \floatno.
+\def\getfloatno#1{%
+ \ifx#1\relax
+ % Haven't seen this figure type before.
+ \csname newcount\endcsname #1%
+ %
+ % Remember to reset this floatno at the next chap.
+ \expandafter\gdef\expandafter\resetallfloatnos
+ \expandafter{\resetallfloatnos #1=0 }%
+ \fi
+ \let\floatno#1%
+}
+
+% \setref calls this to get the XREFLABEL-snt value. We want an @xref
+% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we
+% first read the @float command.
+%
+\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}%
+
+% Magic string used for the XREFLABEL-title value, so \xrefX can
+% distinguish floats from other xref types.
+\def\floatmagic{!!float!!}
+
+% #1 is the control sequence we are passed; we expand into a conditional
+% which is true if #1 represents a float ref. That is, the magic
+% \thissection value which we \setref above.
+%
+\def\iffloat#1{\expandafter\doiffloat#1==\finish}
+%
+% #1 is (maybe) the \floatmagic string. If so, #2 will be the
+% (safe) float type for this float. We set \iffloattype to #2.
+%
+\def\doiffloat#1=#2=#3\finish{%
+ \def\temp{#1}%
+ \def\iffloattype{#2}%
+ \ifx\temp\floatmagic
+}
+
+% @listoffloats FLOATTYPE - print a list of floats like a table of contents.
+%
+\parseargdef\listoffloats{%
+ \def\floattype{#1}% floattype
+ {%
+ % the floattype might have accents or other special characters,
+ % but we need to use it in a control sequence name.
+ \indexnofonts
+ \turnoffactive
+ \xdef\safefloattype{\floattype}%
+ }%
+ %
+ % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE.
+ \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax
+ \ifhavexrefs
+ % if the user said @listoffloats foo but never @float foo.
+ \message{\linenumber No `\safefloattype' floats to list.}%
+ \fi
+ \else
+ \begingroup
+ \leftskip=\tocindent % indent these entries like a toc
+ \let\do=\listoffloatsdo
+ \csname floatlist\safefloattype\endcsname
+ \endgroup
+ \fi
+}
+
+% This is called on each entry in a list of floats. We're passed the
+% xref label, in the form LABEL-title, which is how we save it in the
+% aux file. We strip off the -title and look up \XRLABEL-lof, which
+% has the text we're supposed to typeset here.
+%
+% Figures without xref labels will not be included in the list (since
+% they won't appear in the aux file).
+%
+\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish}
+\def\listoffloatsdoentry#1-title\finish{{%
+ % Can't fully expand XR#1-lof because it can contain anything. Just
+ % pass the control sequence. On the other hand, XR#1-pg is just the
+ % page number, and we want to fully expand that so we can get a link
+ % in pdf output.
+ \toksA = \expandafter{\csname XR#1-lof\endcsname}%
+ %
+ % use the same \entry macro we use to generate the TOC and index.
+ \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}%
+ \writeentry
+}}
+
+\message{localization,}
+% and i18n.
+
+% @documentlanguage is usually given very early, just after
+% @setfilename. If done too late, it may not override everything
+% properly. Single argument is the language abbreviation.
+% It would be nice if we could set up a hyphenation file here.
+%
+\parseargdef\documentlanguage{%
+ \tex % read txi-??.tex file in plain TeX.
+ % Read the file if it exists.
+ \openin 1 txi-#1.tex
+ \ifeof 1
+ \errhelp = \nolanghelp
+ \errmessage{Cannot read language file txi-#1.tex}%
+ \else
+ \input txi-#1.tex
+ \fi
+ \closein 1
+ \endgroup
+}
+\newhelp\nolanghelp{The given language definition file cannot be found or
+is empty. Maybe you need to install it? In the current directory
+should work if nowhere else does.}
+
+
+% @documentencoding should change something in TeX eventually, most
+% likely, but for now just recognize it.
+\let\documentencoding = \comment
+
+
+% Page size parameters.
+%
+\newdimen\defaultparindent \defaultparindent = 15pt
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
+
+% Prevent underfull vbox error messages.
+\vbadness = 10000
+
+% Don't be so finicky about underfull hboxes, either.
+\hbadness = 2000
+
+% Following George Bush, just get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything. We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize. We call this whenever the paper size is set.
+%
+\def\setemergencystretch{%
+ \ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+ \else
+ \emergencystretch = .15\hsize
+ \fi
+}
+
+% Parameters in order: 1) textheight; 2) textwidth;
+% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip;
+% 7) physical page height; 8) physical page width.
+%
+% We also call \setleading{\textleading}, so the caller should define
+% \textleading. The caller should also set \parskip.
+%
+\def\internalpagesizes#1#2#3#4#5#6#7#8{%
+ \voffset = #3\relax
+ \topskip = #6\relax
+ \splittopskip = \topskip
+ %
+ \vsize = #1\relax
+ \advance\vsize by \topskip
+ \outervsize = \vsize
+ \advance\outervsize by 2\topandbottommargin
+ \pageheight = \vsize
+ %
+ \hsize = #2\relax
+ \outerhsize = \hsize
+ \advance\outerhsize by 0.5in
+ \pagewidth = \hsize
+ %
+ \normaloffset = #4\relax
+ \bindingoffset = #5\relax
+ %
+ \ifpdf
+ \pdfpageheight #7\relax
+ \pdfpagewidth #8\relax
+ \fi
+ %
+ \setleading{\textleading}
+ %
+ \parindent = \defaultparindent
+ \setemergencystretch
+}
+
+% @letterpaper (the default).
+\def\letterpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % If page is nothing but text, make it come out even.
+ \internalpagesizes{46\baselineskip}{6in}%
+ {\voffset}{.25in}%
+ {\bindingoffset}{36pt}%
+ {11in}{8.5in}%
+}}
+
+% Use @smallbook to reset parameters for 7x9.25 trim size.
+\def\smallbook{{\globaldefs = 1
+ \parskip = 2pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.5in}{5in}%
+ {\voffset}{.25in}%
+ {\bindingoffset}{16pt}%
+ {9.25in}{7in}%
+ %
+ \lispnarrowing = 0.3in
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = .5cm
+}}
+
+% Use @smallerbook to reset parameters for 6x9 trim size.
+% (Just testing, parameters still in flux.)
+\def\smallerbook{{\globaldefs = 1
+ \parskip = 1.5pt plus 1pt
+ \textleading = 12pt
+ %
+ \internalpagesizes{7.4in}{4.8in}%
+ {-.2in}{-.4in}%
+ {0pt}{14pt}%
+ {9in}{6in}%
+ %
+ \lispnarrowing = 0.25in
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = .4cm
+}}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{{\globaldefs = 1
+ \parskip = 3pt plus 2pt minus 1pt
+ \textleading = 13.2pt
+ %
+ % Double-side printing via postscript on Laserjet 4050
+ % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm.
+ % To change the settings for a different printer or situation, adjust
+ % \normaloffset until the front-side and back-side texts align. Then
+ % do the same for \bindingoffset. You can set these for testing in
+ % your texinfo source file like this:
+ % @tex
+ % \global\normaloffset = -6mm
+ % \global\bindingoffset = 10mm
+ % @end tex
+ \internalpagesizes{51\baselineskip}{160mm}
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{44pt}%
+ {297mm}{210mm}%
+ %
+ \tolerance = 700
+ \hfuzz = 1pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = 5mm
+}}
+
+% Use @afivepaper to print on European A5 paper.
+% From romildo@urano.iceb.ufop.br, 2 July 2000.
+% He also recommends making @example and @lisp be small.
+\def\afivepaper{{\globaldefs = 1
+ \parskip = 2pt plus 1pt minus 0.1pt
+ \textleading = 12.5pt
+ %
+ \internalpagesizes{160mm}{120mm}%
+ {\voffset}{\hoffset}%
+ {\bindingoffset}{8pt}%
+ {210mm}{148mm}%
+ %
+ \lispnarrowing = 0.2in
+ \tolerance = 800
+ \hfuzz = 1.2pt
+ \contentsrightmargin = 0pt
+ \defbodyindent = 2mm
+ \tableindent = 12mm
+}}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper.
+\def\afourlatex{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{237mm}{150mm}%
+ {\voffset}{4.6mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ %
+ % Must explicitly reset to 0 because we call \afourpaper.
+ \globaldefs = 0
+}}
+
+% Use @afourwide to print on A4 paper in landscape format.
+\def\afourwide{{\globaldefs = 1
+ \afourpaper
+ \internalpagesizes{241mm}{165mm}%
+ {\voffset}{-2.95mm}%
+ {\bindingoffset}{7mm}%
+ {297mm}{210mm}%
+ \globaldefs = 0
+}}
+
+% @pagesizes TEXTHEIGHT[,TEXTWIDTH]
+% Perhaps we should allow setting the margins, \topskip, \parskip,
+% and/or leading, also. Or perhaps we should compute them somehow.
+%
+\parseargdef\pagesizes{\pagesizesyyy #1,,\finish}
+\def\pagesizesyyy#1,#2,#3\finish{{%
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi
+ \globaldefs = 1
+ %
+ \parskip = 3pt plus 2pt minus 1pt
+ \setleading{\textleading}%
+ %
+ \dimen0 = #1
+ \advance\dimen0 by \voffset
+ %
+ \dimen2 = \hsize
+ \advance\dimen2 by \normaloffset
+ %
+ \internalpagesizes{#1}{\hsize}%
+ {\voffset}{\normaloffset}%
+ {\bindingoffset}{44pt}%
+ {\dimen0}{\dimen2}%
+}}
+
+% Set default to letter.
+%
+\letterpaper
+
+
+\message{and turning on texinfo input format.}
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other
+\catcode`\~=\other
+\catcode`\^=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode`\+=\other
+\catcode`\$=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+\def\normaldollar{$}%$ font-lock fix
+
+% This macro is used to make a character print one way in \tt
+% (where it can probably be output as-is), and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise. Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi}
+
+% Same as above, but check for italic font. Actually this also catches
+% non-italic slanted fonts since it is impossible to distinguish them from
+% italic fonts. But since this is only used by $ and it uses \sl anyway
+% this is not a problem.
+\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary).
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt\char34}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt\char126}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def^{{\tt \hat}}
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+\let\realunder=_
+% Subroutine for the previous macro.
+\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }
+
+\catcode`\|=\active
+\def|{{\tt\char124}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+\catcode`\$=\active
+\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have \everyjob (or @setfilename) turn them on.
+% \otherifyactive is called near the end of this file.
+\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
+
+% Used sometimes to turn off (effectively) the active characters even after
+% parsing them.
+\def\turnoffactive{%
+ \normalturnoffactive
+ \otherbackslash
+}
+
+\catcode`\@=0
+
+% \backslashcurfont outputs one backslash character in current font,
+% as in \char`\\.
+\global\chardef\backslashcurfont=`\\
+\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work
+
+% \realbackslash is an actual character `\' with catcode other, and
+% \doublebackslash is two of them (for the pdf outlines).
+{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}}
+
+% In texinfo, backslash is an active character; it prints the backslash
+% in fixed width font.
+\catcode`\\=\active
+@def@normalbackslash{{@tt@backslashcurfont}}
+% On startup, @fixbackslash assigns:
+% @let \ = @normalbackslash
+
+% \rawbackslash defines an active \ to do \backslashcurfont.
+% \otherbackslash defines an active \ to be a literal `\' character with
+% catcode other.
+@gdef@rawbackslash{@let\=@backslashcurfont}
+@gdef@otherbackslash{@let\=@realbackslash}
+
+% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of
+% the literal character `\'.
+%
+@def@normalturnoffactive{%
+ @let\=@normalbackslash
+ @let"=@normaldoublequote
+ @let~=@normaltilde
+ @let^=@normalcaret
+ @let_=@normalunderscore
+ @let|=@normalverticalbar
+ @let<=@normalless
+ @let>=@normalgreater
+ @let+=@normalplus
+ @let$=@normaldollar %$ font-lock fix
+ @unsepspaces
+}
+
+% Make _ and + \other characters, temporarily.
+% This is canceled by @fixbackslash.
+@otherifyactive
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\' in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+% Also turn back on active characters that might appear in the input
+% file name, in case not using a pre-dumped format.
+%
+@gdef@fixbackslash{%
+ @ifx\@eatinput @let\ = @normalbackslash @fi
+ @catcode`+=@active
+ @catcode`@_=@active
+}
+
+% Say @foo, not \foo, in error messages.
+@escapechar = `@@
+
+% These look ok in all fonts, so just make them not special.
+@catcode`@& = @other
+@catcode`@# = @other
+@catcode`@% = @other
+
+
+@c Local variables:
+@c eval: (add-hook 'write-file-hooks 'time-stamp)
+@c page-delimiter: "^\\\\message"
+@c time-stamp-start: "def\\\\texinfoversion{"
+@c time-stamp-format: "%:y-%02m-%02d.%02H"
+@c time-stamp-end: "}"
+@c End:
+
+@c vim:sw=2:
+
+@ignore
+ arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115
+@end ignore
diff --git a/lib/et/vfprintf.c b/lib/et/vfprintf.c
new file mode 100644
index 0000000..e3fcd78
--- /dev/null
+++ b/lib/et/vfprintf.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)vfprintf.c 5.2 (Berkeley) 6/27/88";
+#endif /* LIBC_SCCS and not lint */
+
+#include "config.h"
+#if !HAVE_VPRINTF && HAVE_DOPRNT
+#include <stdio.h>
+#include <varargs.h>
+
+int
+vfprintf(iop, fmt, ap)
+ FILE *iop;
+ char *fmt;
+ va_list ap;
+{
+ int len;
+ char localbuf[BUFSIZ];
+
+ if (iop->_flag & _IONBF) {
+ iop->_flag &= ~_IONBF;
+ iop->_ptr = iop->_base = localbuf;
+ len = _doprnt(fmt, ap, iop);
+ (void) fflush(iop);
+ iop->_flag |= _IONBF;
+ iop->_base = NULL;
+ iop->_bufsiz = 0;
+ iop->_cnt = 0;
+ } else
+ len = _doprnt(fmt, ap, iop);
+
+ return (ferror(iop) ? EOF : len);
+}
+#endif /* !HAVE_VPRINTF */
diff --git a/lib/ext2fs/Android.bp b/lib/ext2fs/Android.bp
new file mode 100644
index 0000000..db8b3b7
--- /dev/null
+++ b/lib/ext2fs/Android.bp
@@ -0,0 +1,129 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-GPL
+ // SPDX-license-identifier-GPL-2.0
+ // SPDX-license-identifier-LGPL
+ // SPDX-license-identifier-LGPL-2.1
+ // SPDX-license-identifier-LGPL-3.0
+ // legacy_unencumbered
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library {
+ name: "libext2fs",
+ host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "ext2_err.c",
+ "alloc.c",
+ "alloc_sb.c",
+ "alloc_stats.c",
+ "alloc_tables.c",
+ "atexit.c",
+ "badblocks.c",
+ "bb_inode.c",
+ "bitmaps.c",
+ "bitops.c",
+ "blkmap64_ba.c",
+ "blkmap64_rb.c",
+ "blknum.c",
+ "block.c",
+ "bmap.c",
+ "check_desc.c",
+ "crc16.c",
+ "crc32c.c",
+ "csum.c",
+ "closefs.c",
+ "dblist.c",
+ "dblist_dir.c",
+ "digest_encode.c",
+ "dirblock.c",
+ "dirhash.c",
+ "dir_iterate.c",
+ "dupfs.c",
+ "expanddir.c",
+ "ext_attr.c",
+ "extent.c",
+ "fallocate.c",
+ "fileio.c",
+ "finddev.c",
+ "flushb.c",
+ "freefs.c",
+ "gen_bitmap.c",
+ "gen_bitmap64.c",
+ "get_num_dirs.c",
+ "get_pathname.c",
+ "getsize.c",
+ "getsectsize.c",
+ "hashmap.c",
+ "i_block.c",
+ "icount.c",
+ "imager.c",
+ "ind_block.c",
+ "initialize.c",
+ "inline.c",
+ "inline_data.c",
+ "inode.c",
+ "io_manager.c",
+ "ismounted.c",
+ "link.c",
+ "llseek.c",
+ "lookup.c",
+ "mmp.c",
+ "mkdir.c",
+ "mkjournal.c",
+ "namei.c",
+ "native.c",
+ "newdir.c",
+ "nls_utf8.c",
+ "openfs.c",
+ "progress.c",
+ "punch.c",
+ "qcow2.c",
+ "rbtree.c",
+ "read_bb.c",
+ "read_bb_file.c",
+ "res_gdt.c",
+ "rw_bitmaps.c",
+ "sha256.c",
+ "sha512.c",
+ "swapfs.c",
+ "symlink.c",
+ "undo_io.c",
+ "unix_io.c",
+ "sparse_io.c",
+ "unlink.c",
+ "valid_blk.c",
+ "version.c",
+ // get rid of this?!
+ "test_io.c",
+ ],
+ shared_libs: [
+ "libext2_com_err",
+ "libsparse",
+ "libz",
+ ],
+ target: {
+ android: {
+ shared_libs: ["libext2_uuid"],
+ },
+ windows: {
+ enabled: true,
+ srcs: ["windows_io.c"],
+ exclude_srcs: ["unix_io.c"],
+ },
+ },
+
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
new file mode 100644
index 0000000..798ff60
--- /dev/null
+++ b/lib/ext2fs/Makefile.in
@@ -0,0 +1,1459 @@
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/ext2fs
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+DEPEND_CFLAGS = -I$(top_srcdir)/debugfs -I$(srcdir)/../../e2fsck -DDEBUGFS
+# This nastiness is needed because of jfs_user.h hackery; when we finally
+# clean up this mess, we should be able to drop it
+DEBUGFS_CFLAGS = -I$(srcdir)/../../e2fsck $(ALL_CFLAGS) -DDEBUGFS
+
+@MCONFIG@
+
+@DEBUGFS_CMT@DEBUGFS_LIB_OBJS = bb_compat.o inode_io.o write_bb_file.o
+
+MK_CMDS= _SS_DIR_OVERRIDE=$(srcdir)/../ss ../ss/mk_cmds
+COMPILE_ET= _ET_DIR_OVERRIDE=$(srcdir)/../et ../et/compile_et
+
+@RESIZER_CMT@RESIZE_LIB_OBJS = dupfs.o
+@TEST_IO_CMT@TEST_IO_LIB_OBJS = test_io.o
+@IMAGER_CMT@E2IMAGE_LIB_OBJS = imager.o
+
+DEBUG_OBJS= debug_cmds.o extent_cmds.o tst_cmds.o debugfs.o util.o \
+ ncheck.o icheck.o ls.o lsdel.o dump.o set_fields.o logdump.o \
+ htree.o unused.o e2freefrag.o filefrag.o extent_inode.o zap.o \
+ xattrs.o quota.o tst_libext2fs.o create_inode.o journal.o \
+ revoke.o recovery.o do_journal.o
+
+DEBUG_SRCS= debug_cmds.c extent_cmds.c tst_cmds.c \
+ $(top_srcdir)/debugfs/debugfs.c \
+ $(top_srcdir)/debugfs/util.c \
+ $(top_srcdir)/debugfs/ncheck.c \
+ $(top_srcdir)/debugfs/icheck.c \
+ $(top_srcdir)/debugfs/ls.c \
+ $(top_srcdir)/debugfs/lsdel.c \
+ $(top_srcdir)/debugfs/dump.c \
+ $(top_srcdir)/debugfs/set_fields.c \
+ $(top_srcdir)/debugfs/logdump.c \
+ $(top_srcdir)/debugfs/htree.c \
+ $(top_srcdir)/debugfs/unused.c \
+ $(top_srcdir)/debugfs/filefrag.c \
+ $(top_srcdir)/debugfs/extent_inode.c \
+ $(top_srcdir)/debugfs/zap.c \
+ $(top_srcdir)/debugfs/quota.c \
+ $(top_srcdir)/debugfs/xattrs.c \
+ $(top_srcdir)/misc/e2freefrag.c \
+ $(top_srcdir)/misc/create_inode.c \
+ $(top_srcdir)/debugfs/journal.c \
+ $(top_srcdir)/e2fsck/revoke.c \
+ $(top_srcdir)/e2fsck/recovery.c \
+ $(top_srcdir)/debugfs/do_journal.c
+
+@TDB_CMT@TDB_OBJ= tdb.o
+
+OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
+ $(TEST_IO_LIB_OBJS) \
+ ext2_err.o \
+ alloc.o \
+ alloc_sb.o \
+ alloc_stats.o \
+ alloc_tables.o \
+ atexit.o \
+ badblocks.o \
+ bb_inode.o \
+ bitmaps.o \
+ bitops.o \
+ blkmap64_ba.o \
+ blkmap64_rb.o \
+ blknum.o \
+ block.o \
+ bmap.o \
+ check_desc.o \
+ closefs.o \
+ crc16.o \
+ crc32c.o \
+ csum.o \
+ dblist.o \
+ dblist_dir.o \
+ dirblock.o \
+ dirhash.o \
+ dir_iterate.o \
+ expanddir.o \
+ ext_attr.o \
+ extent.o \
+ fallocate.o \
+ fileio.o \
+ finddev.o \
+ flushb.o \
+ freefs.o \
+ gen_bitmap.o \
+ gen_bitmap64.o \
+ get_num_dirs.o \
+ get_pathname.o \
+ getsize.o \
+ getsectsize.o \
+ hashmap.o \
+ i_block.o \
+ icount.o \
+ ind_block.o \
+ initialize.o \
+ inline.o \
+ inline_data.o \
+ inode.o \
+ io_manager.o \
+ ismounted.o \
+ link.o \
+ llseek.o \
+ lookup.o \
+ mkdir.o \
+ mkjournal.o \
+ mmp.o \
+ namei.o \
+ native.o \
+ newdir.o \
+ nls_utf8.o \
+ openfs.o \
+ orphan.o \
+ progress.o \
+ punch.o \
+ qcow2.o \
+ read_bb.o \
+ read_bb_file.o \
+ res_gdt.o \
+ rw_bitmaps.o \
+ sha512.o \
+ swapfs.o \
+ symlink.o \
+ $(TDB_OBJ) \
+ undo_io.o \
+ @OS_IO_FILE@.o \
+ sparse_io.o \
+ unlink.o \
+ valid_blk.o \
+ version.o \
+ rbtree.o
+
+SRCS= ext2_err.c \
+ $(srcdir)/alloc.c \
+ $(srcdir)/alloc_sb.c \
+ $(srcdir)/alloc_stats.c \
+ $(srcdir)/alloc_tables.c \
+ $(srcdir)/atexit.c \
+ $(srcdir)/badblocks.c \
+ $(srcdir)/bb_compat.c \
+ $(srcdir)/bb_inode.c \
+ $(srcdir)/bitmaps.c \
+ $(srcdir)/bitops.c \
+ $(srcdir)/blkmap64_ba.c \
+ $(srcdir)/blkmap64_rb.c \
+ $(srcdir)/block.c \
+ $(srcdir)/bmap.c \
+ $(srcdir)/check_desc.c \
+ $(srcdir)/closefs.c \
+ $(srcdir)/crc16.c \
+ $(srcdir)/crc32c.c \
+ $(srcdir)/gen_crc32ctable.c \
+ $(srcdir)/csum.c \
+ $(srcdir)/dblist.c \
+ $(srcdir)/dblist_dir.c \
+ $(srcdir)/digest_encode.c \
+ $(srcdir)/dirblock.c \
+ $(srcdir)/dirhash.c \
+ $(srcdir)/dir_iterate.c \
+ $(srcdir)/dupfs.c \
+ $(srcdir)/expanddir.c \
+ $(srcdir)/ext_attr.c \
+ $(srcdir)/extent.c \
+ $(srcdir)/fileio.c \
+ $(srcdir)/finddev.c \
+ $(srcdir)/flushb.c \
+ $(srcdir)/freefs.c \
+ $(srcdir)/gen_bitmap.c \
+ $(srcdir)/gen_bitmap64.c \
+ $(srcdir)/get_num_dirs.c \
+ $(srcdir)/get_pathname.c \
+ $(srcdir)/getsize.c \
+ $(srcdir)/getsectsize.c \
+ $(srcdir)/hashmap.c \
+ $(srcdir)/i_block.c \
+ $(srcdir)/icount.c \
+ $(srcdir)/ind_block.c \
+ $(srcdir)/initialize.c \
+ $(srcdir)/inline.c \
+ $(srcdir)/inline_data.c \
+ $(srcdir)/inode.c \
+ $(srcdir)/inode_io.c \
+ $(srcdir)/imager.c \
+ $(srcdir)/io_manager.c \
+ $(srcdir)/ismounted.c \
+ $(srcdir)/link.c \
+ $(srcdir)/llseek.c \
+ $(srcdir)/lookup.c \
+ $(srcdir)/mkdir.c \
+ $(srcdir)/mkjournal.c \
+ $(srcdir)/mmp.c \
+ $(srcdir)/namei.c \
+ $(srcdir)/native.c \
+ $(srcdir)/newdir.c \
+ $(srcdir)/nls_utf8.c \
+ $(srcdir)/openfs.c \
+ $(srcdir)/orphan.c \
+ $(srcdir)/progress.c \
+ $(srcdir)/punch.c \
+ $(srcdir)/qcow2.c \
+ $(srcdir)/read_bb.c \
+ $(srcdir)/read_bb_file.c \
+ $(srcdir)/res_gdt.c \
+ $(srcdir)/rw_bitmaps.c \
+ $(srcdir)/sha256.c \
+ $(srcdir)/sha512.c \
+ $(srcdir)/swapfs.c \
+ $(srcdir)/symlink.c \
+ $(srcdir)/tdb.c \
+ $(srcdir)/test_io.c \
+ $(srcdir)/tst_badblocks.c \
+ $(srcdir)/tst_bitops.c \
+ $(srcdir)/tst_byteswap.c \
+ $(srcdir)/tst_getsize.c \
+ $(srcdir)/tst_iscan.c \
+ $(srcdir)/undo_io.c \
+ $(srcdir)/@OS_IO_FILE@.c \
+ $(srcdir)/sparse_io.c \
+ $(srcdir)/unlink.c \
+ $(srcdir)/valid_blk.c \
+ $(srcdir)/version.c \
+ $(srcdir)/write_bb_file.c \
+ $(srcdir)/rbtree.c \
+ $(srcdir)/tst_libext2fs.c \
+ $(DEBUG_SRCS)
+
+HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \
+ tdb.h qcow2.h hashmap.h
+HFILES_IN= ext2_err.h ext2_types.h
+
+LIBRARY= libext2fs
+LIBDIR= ext2fs
+
+ELF_VERSION = 2.4
+ELF_SO_VERSION = 2
+ELF_IMAGE = libext2fs
+ELF_MYDIR = ext2fs
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS = -lcom_err
+
+BSDLIB_VERSION = 2.1
+BSDLIB_IMAGE = libext2fs
+BSDLIB_MYDIR = ext2fs
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+
+all:: ext2fs.pc
+
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+DISTFILES= Makefile *.c *.h image
+
+ext2_err.et: $(DEP_SUBSTITUTE) $(srcdir)/ext2_err.et.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE) $(srcdir)/ext2_err.et.in ext2_err.et
+
+ext2_err.c ext2_err.h: ext2_err.et
+ $(E) " COMPILE_ET ext2_err.et"
+ $(Q) $(COMPILE_ET) ext2_err.et
+
+ext2fs.pc: $(srcdir)/ext2fs.pc.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); CONFIG_FILES=lib/ext2fs/ext2fs.pc ./config.status
+
+tst_badblocks: tst_badblocks.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_badblocks tst_badblocks.o $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_digest_encode: $(srcdir)/digest_encode.c $(srcdir)/ext2_fs.h
+ $(E) " CC $@"
+ $(Q) $(CC) $(ALL_LDFLAGS) $(ALL_CFLAGS) -o tst_digest_encode \
+ $(srcdir)/digest_encode.c -DUNITTEST $(SYSLIBS)
+
+tst_icount: $(srcdir)/icount.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_icount $(srcdir)/icount.c -DDEBUG \
+ $(ALL_CFLAGS) $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_iscan: tst_iscan.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_iscan tst_iscan.o $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_getsize: tst_getsize.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_getsize tst_getsize.o $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_ismounted: $(srcdir)/ismounted.c $(STATIC_LIBEXT2FS) \
+ $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_ismounted $(srcdir)/ismounted.c \
+ $(STATIC_LIBEXT2FS) -DDEBUG $(ALL_CFLAGS) $(ALL_LDFLAGS) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_byteswap: tst_byteswap.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_byteswap tst_byteswap.o $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_bitops: tst_bitops.o $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_bitops tst_bitops.o $(ALL_CFLAGS) $(ALL_LDFLAGS) \
+ $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_getsectsize: tst_getsectsize.o getsectsize.o $(STATIC_LIBEXT2FS) \
+ $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_getsectsize tst_getsectsize.o getsectsize.o \
+ $(ALL_LDFLAGS) $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) \
+ $(SYSLIBS)
+
+tst_types.o: $(srcdir)/tst_types.c ext2_types.h
+
+tst_types: tst_types.o ext2_types.h
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_types tst_types.o $(ALL_LDFLAGS) $(SYSLIBS)
+
+tst_super_size.o: $(srcdir)/tst_super_size.c $(srcdir)/ext2_fs.h
+
+tst_super_size: tst_super_size.o
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_super_size tst_super_size.o $(ALL_LDFLAGS) $(SYSLIBS)
+
+tst_fs_struct.o: $(srcdir)/tst_fs_struct.c $(srcdir)/ext2fs.h
+
+tst_fs_struct: tst_fs_struct.o
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_fs_struct tst_fs_struct.o $(ALL_LDFLAGS) $(SYSLIBS)
+
+tst_inode_size.o: $(srcdir)/tst_inode_size.c $(srcdir)/ext2_fs.h
+
+tst_inode_size: tst_inode_size.o
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_inode_size tst_inode_size.o $(ALL_LDFLAGS) $(SYSLIBS)
+
+tst_sha256: $(srcdir)/sha256.c $(srcdir)/ext2_fs.h
+ $(E) " CC $@"
+ $(Q) $(CC) $(ALL_LDFLAGS) $(ALL_CFLAGS) -o tst_sha256 \
+ $(srcdir)/sha256.c -DUNITTEST $(SYSLIBS)
+
+tst_sha512: $(srcdir)/sha512.c $(srcdir)/ext2_fs.h
+ $(E) " CC $@"
+ $(Q) $(CC) $(ALL_LDFLAGS) $(ALL_CFLAGS) -o tst_sha512 \
+ $(srcdir)/sha512.c -DUNITTEST $(SYSLIBS)
+
+ext2_tdbtool: tdbtool.o
+ $(E) " LD $@"
+ $(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o $(ALL_LDFLAGS) $(SYSLIBS)
+
+tst_cmds.c tst_cmds.h: tst_cmds.ct
+ $(E) " MK_CMDS $@"
+ $(Q) $(MK_CMDS) $(srcdir)/tst_cmds.ct
+
+debug_cmds.c debug_cmds.h: $(top_srcdir)/debugfs/debug_cmds.ct
+ $(E) " MK_CMDS $<"
+ $(Q) $(MK_CMDS) $(top_srcdir)/debugfs/debug_cmds.ct
+
+extent_cmds.c extent_cmds.h: $(top_srcdir)/debugfs/extent_cmds.ct
+ $(E) " MK_CMDS $<"
+ $(Q) $(MK_CMDS) $(top_srcdir)/debugfs/extent_cmds.ct
+
+debugfs.o: $(top_srcdir)/debugfs/debugfs.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -DSKIP_GLOBDEFS -c $< -o $@
+
+extent_inode.o: $(top_srcdir)/debugfs/extent_inode.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+util.o: $(top_srcdir)/debugfs/util.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+ncheck.o: $(top_srcdir)/debugfs/ncheck.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+icheck.o: $(top_srcdir)/debugfs/icheck.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+ls.o: $(top_srcdir)/debugfs/ls.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+lsdel.o: $(top_srcdir)/debugfs/lsdel.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+dump.o: $(top_srcdir)/debugfs/dump.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+set_fields.o: $(top_srcdir)/debugfs/set_fields.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+logdump.o: $(top_srcdir)/debugfs/logdump.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+htree.o: $(top_srcdir)/debugfs/htree.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+unused.o: $(top_srcdir)/debugfs/unused.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+zap.o: $(top_srcdir)/debugfs/zap.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+quota.o: $(top_srcdir)/debugfs/quota.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+journal.o: $(top_srcdir)/debugfs/journal.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+revoke.o: $(top_srcdir)/e2fsck/revoke.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+recovery.o: $(top_srcdir)/e2fsck/recovery.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+do_journal.o: $(top_srcdir)/debugfs/do_journal.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+xattrs.o: $(top_srcdir)/debugfs/xattrs.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(DEBUGFS_CFLAGS) -c $< -o $@
+
+e2freefrag.o: $(top_srcdir)/misc/e2freefrag.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) -DDEBUGFS -I$(top_srcdir)/debugfs -c $< -o $@
+
+create_inode.o: $(top_srcdir)/misc/create_inode.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) -DDEBUGFS -c $< -o $@
+
+filefrag.o: $(top_srcdir)/debugfs/filefrag.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+
+tst_libext2fs.o: $(srcdir)/tst_libext2fs.c
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(DEPEND_CFLAGS) -c $< -o $@
+
+tst_bitmaps_cmd.c: tst_bitmaps_cmd.ct
+ $(E) " MK_CMDS $@"
+ $(Q) DIR=$(srcdir) $(MK_CMDS) $(srcdir)/tst_bitmaps_cmd.ct
+
+tst_bitmaps: tst_bitmaps.o tst_bitmaps_cmd.o $(srcdir)/blkmap64_rb.c \
+ $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBSS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o $@ tst_bitmaps.o tst_bitmaps_cmd.o \
+ -DDEBUG_RB $(srcdir)/blkmap64_rb.c $(ALL_CFLAGS) \
+ $(ALL_LDFLAGS) $(STATIC_LIBEXT2FS) $(STATIC_LIBSS) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_extents: $(srcdir)/extent.c $(DEBUG_OBJS) $(DEPSTATIC_LIBSS) libext2fs.a \
+ $(STATIC_LIBE2P) $(DEPLIBUUID) $(DEPLIBBLKID) $(DEPSTATIC_LIBCOM_ERR) \
+ $(DEPLIBSUPPORT)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_extents $(srcdir)/extent.c \
+ $(ALL_CFLAGS) $(ALL_LDFLAGS) -DDEBUG $(DEBUG_OBJS) \
+ $(STATIC_LIBSS) $(STATIC_LIBE2P) $(LIBSUPPORT) \
+ $(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS) -I $(top_srcdir)/debugfs
+
+tst_libext2fs: $(DEBUG_OBJS) \
+ $(DEPSTATIC_LIBSS) $(STATIC_LIBE2P) $(DEPLIBUUID) libext2fs.a \
+ $(DEPLIBBLKID) $(DEPSTATIC_LIBCOM_ERR) $(DEPLIBSUPPORT)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_libext2fs $(ALL_LDFLAGS) -DDEBUG $(DEBUG_OBJS) \
+ $(STATIC_LIBSS) $(STATIC_LIBE2P) $(LIBSUPPORT) \
+ $(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) $(LIBMAGIC) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS) -I $(top_srcdir)/debugfs
+
+tst_inline: $(srcdir)/inline.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_inline $(srcdir)/inline.c $(ALL_CFLAGS) \
+ $(ALL_LDFLAGS) -DDEBUG $(STATIC_LIBEXT2FS) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_inline_data: inline_data.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_inline_data $(srcdir)/inline_data.c $(ALL_CFLAGS) \
+ $(ALL_LDFLAGS) -DDEBUG $(STATIC_LIBEXT2FS) \
+ $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+
+tst_csum: csum.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR) $(STATIC_LIBE2P) \
+ $(top_srcdir)/lib/e2p/e2p.h
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_csum $(srcdir)/csum.c -DDEBUG \
+ $(ALL_CFLAGS) $(ALL_LDFLAGS) $(STATIC_LIBEXT2FS) \
+ $(STATIC_LIBCOM_ERR) $(STATIC_LIBE2P) $(SYSLIBS)
+
+tst_crc32c: $(srcdir)/crc32c.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(Q) $(CC) $(ALL_LDFLAGS) $(ALL_CFLAGS) -o tst_crc32c $(srcdir)/crc32c.c \
+ -DUNITTEST $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) \
+ $(SYSLIBS)
+
+mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG \
+ $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS) $(SYSLIBS)
+
+fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
+ tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
+ tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
+ tst_digest_encode tst_getsize tst_getsectsize
+ $(TESTENV) ./tst_bitops
+ $(TESTENV) ./tst_badblocks
+ $(TESTENV) ./tst_iscan
+ $(TESTENV) ./tst_types
+ $(TESTENV) ./tst_icount
+ $(TESTENV) ./tst_super_size
+ $(TESTENV) ./tst_inode_size
+ $(TESTENV) ./tst_csum
+ $(TESTENV) ./tst_inline
+ $(TESTENV) ./tst_inline_data
+ $(TESTENV) ./tst_crc32c
+ $(TESTENV) ./tst_sha256
+ $(TESTENV) ./tst_sha512
+ $(TESTENV) ./tst_bitmaps -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
+ diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
+ $(TESTENV) ./tst_bitmaps -t 2 -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
+ diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
+ $(TESTENV) ./tst_bitmaps -t 3 -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
+ diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
+ $(TESTENV) ./tst_bitmaps -l -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
+ diff $(srcdir)/tst_bitmaps_exp tst_bitmaps_out
+ $(TESTENV) ./tst_digest_encode
+
+installdirs::
+ $(E) " MKDIR_P $(libdir) $(includedir)/ext2fs"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(includedir)/ext2fs $(DESTDIR)$(pkgconfigdir)
+
+install:: all $(HFILES) $(HFILES_IN) installdirs ext2fs.pc
+ $(E) " INSTALL_DATA $(libdir)/libext2fs.a"
+ $(Q) $(INSTALL_DATA) libext2fs.a $(DESTDIR)$(libdir)/libext2fs.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libext2fs.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libext2fs.a
+ $(Q) for i in $(HFILES); do \
+ echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/ext2fs/$$i; \
+ done
+ $(Q) for i in $(HFILES_IN); do \
+ echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/ext2fs/$$i; \
+ done
+ $(E) " INSTALL_DATA $(pkgconfigdir)/ext2fs.pc"
+ $(Q) $(INSTALL_DATA) ext2fs.pc $(DESTDIR)$(pkgconfigdir)/ext2fs.pc
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/libext2fs.a \
+ $(DESTDIR)$(pkgconfigdir)/ext2fs.pc
+ $(RM) -rf $(DESTDIR)$(includedir)/ext2fs
+
+clean::
+ $(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/* \
+ tst_badblocks tst_iscan ext2_err.et ext2_err.c ext2_err.h \
+ tst_byteswap tst_ismounted tst_getsize tst_getsectsize \
+ tst_bitops tst_types tst_icount tst_super_size tst_csum \
+ tst_bitmaps tst_bitmaps_out tst_extents tst_inline \
+ tst_inline_data tst_inode_size tst_bitmaps_cmd.c \
+ tst_digest_encode tst_sha256 tst_sha512 \
+ ext2_tdbtool mkjournal debug_cmds.c tst_cmds.c extent_cmds.c \
+ ../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a \
+ crc32c_table.h gen_crc32ctable tst_crc32c tst_libext2fs \
+ ext2fs.pc ext2_types.h
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend ext2_err.c ext2_err.h Makefile ext2fs.pc \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+#
+# Hack to parallel makes recognize dependencies correctly.
+#
+$(top_builddir)/lib/ext2fs/ext2_err.h: ext2_err.h
+
+$(OBJS): subdirs
+
+gen_crc32ctable: $(srcdir)/gen_crc32ctable.c
+ $(E) " CC $@"
+ $(Q) $(BUILD_CC) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) -o gen_crc32ctable \
+ $(srcdir)/gen_crc32ctable.c
+
+crc32c_table.h: gen_crc32ctable
+ $(E) " GEN32CTABLE $@"
+ $(Q) ./gen_crc32ctable > crc32c_table.h
+
+$(top_builddir)/$(my_dir)/ext2_types.h: $(srcdir)/ext2_types.h.in \
+ $(top_builddir)/config.status
+ cd $(top_builddir); CONFIG_FILES=$(my_dir)/ext2_types.h ./config.status
+
+$(srcdir)/utf8data.h:
+ $(Q) $(MAKE) $(MKUTF8DATA)
+ $(E) "MKUTF8DATA $@"
+ $(Q) $(MKUTF8DATA) -a $(top_srcdir)/util/ucd/DerivedAge-11.0.0.txt \
+ -c $(top_srcdir)/util/ucd/DerivedCombiningClass-11.0.0.txt \
+ -p $(top_srcdir)/util/ucd/DerivedCoreProperties-11.0.0.txt \
+ -d $(top_srcdir)/util/ucd/UnicodeData-11.0.0.txt \
+ -f $(top_srcdir)/util/ucd/CaseFolding-11.0.0.txt \
+ -n $(top_srcdir)/util/ucd/NormalizationCorrections-11.0.0.txt \
+ -t $(top_srcdir)/util/ucd/NormalizationTest-11.0.0.txt \
+ -o $@
+
+#
+# This needs to be manually maintained since "make depend" on a
+# Linux system is going to blow up due to the lack of windows.h
+# header file. If someone on Windows tries to run "make depend",
+# they will need to comment this chunk below.
+#
+windows_io.o: $(srcdir)/windows_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+ext2_err.o: ext2_err.c
+alloc.o: $(srcdir)/alloc.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+alloc_sb.o: $(srcdir)/alloc_sb.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+alloc_stats.o: $(srcdir)/alloc_stats.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+alloc_tables.o: $(srcdir)/alloc_tables.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+atexit.o: $(srcdir)/atexit.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+badblocks.o: $(srcdir)/badblocks.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+bb_compat.o: $(srcdir)/bb_compat.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+bb_inode.o: $(srcdir)/bb_inode.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+bitmaps.o: $(srcdir)/bitmaps.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h $(srcdir)/bmap64.h
+bitops.o: $(srcdir)/bitops.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+blkmap64_ba.o: $(srcdir)/blkmap64_ba.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/bmap64.h
+blkmap64_rb.o: $(srcdir)/blkmap64_rb.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/bmap64.h $(srcdir)/rbtree.h \
+ $(srcdir)/compiler.h
+block.o: $(srcdir)/block.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+bmap.o: $(srcdir)/bmap.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+check_desc.o: $(srcdir)/check_desc.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+closefs.o: $(srcdir)/closefs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+crc16.o: $(srcdir)/crc16.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/crc16.h
+crc32c.o: $(srcdir)/crc32c.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/crc32c_defs.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h crc32c_table.h
+gen_crc32ctable.o: $(srcdir)/gen_crc32ctable.c $(srcdir)/crc32c_defs.h
+csum.o: $(srcdir)/csum.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/crc16.h
+dblist.o: $(srcdir)/dblist.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+dblist_dir.o: $(srcdir)/dblist_dir.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+digest_encode.o: $(srcdir)/digest_encode.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+dirblock.o: $(srcdir)/dirblock.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+dirhash.o: $(srcdir)/dirhash.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+dir_iterate.o: $(srcdir)/dir_iterate.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+dupfs.o: $(srcdir)/dupfs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+expanddir.o: $(srcdir)/expanddir.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+ext_attr.o: $(srcdir)/ext_attr.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/ext4_acl.h $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+extent.o: $(srcdir)/extent.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/e2image.h
+fileio.o: $(srcdir)/fileio.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+finddev.o: $(srcdir)/finddev.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+flushb.o: $(srcdir)/flushb.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+freefs.o: $(srcdir)/freefs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+gen_bitmap.o: $(srcdir)/gen_bitmap.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+gen_bitmap64.o: $(srcdir)/gen_bitmap64.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/bmap64.h
+get_num_dirs.o: $(srcdir)/get_num_dirs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+get_pathname.o: $(srcdir)/get_pathname.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+getsize.o: $(srcdir)/getsize.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+getsectsize.o: $(srcdir)/getsectsize.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+hashmap.o: $(srcdir)/hashmap.c $(srcdir)/hashmap.h
+i_block.o: $(srcdir)/i_block.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+icount.o: $(srcdir)/icount.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/tdb.h
+ind_block.o: $(srcdir)/ind_block.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+initialize.o: $(srcdir)/initialize.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+inline.o: $(srcdir)/inline.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+inline_data.o: $(srcdir)/inline_data.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
+inode.o: $(srcdir)/inode.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/e2image.h
+inode_io.o: $(srcdir)/inode_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+imager.o: $(srcdir)/imager.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+io_manager.o: $(srcdir)/io_manager.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+ismounted.o: $(srcdir)/ismounted.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+link.o: $(srcdir)/link.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+llseek.o: $(srcdir)/llseek.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_types.h
+lookup.o: $(srcdir)/lookup.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+mkdir.o: $(srcdir)/mkdir.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+mkjournal.o: $(srcdir)/mkjournal.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext2fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/kernel-jbd.h \
+ $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h $(srcdir)/compiler.h
+mmp.o: $(srcdir)/mmp.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+namei.o: $(srcdir)/namei.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+native.o: $(srcdir)/native.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+newdir.o: $(srcdir)/newdir.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+nls_utf8.o: $(srcdir)/nls_utf8.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h $(srcdir)/utf8data.h
+openfs.o: $(srcdir)/openfs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/e2image.h
+progress.o: $(srcdir)/progress.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
+punch.o: $(srcdir)/punch.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+qcow2.o: $(srcdir)/qcow2.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/qcow2.h
+read_bb.o: $(srcdir)/read_bb.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+read_bb_file.o: $(srcdir)/read_bb_file.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+res_gdt.o: $(srcdir)/res_gdt.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+rw_bitmaps.o: $(srcdir)/rw_bitmaps.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/e2image.h
+sha256.o: $(srcdir)/sha256.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+sha512.o: $(srcdir)/sha512.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h
+swapfs.o: $(srcdir)/swapfs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+symlink.o: $(srcdir)/symlink.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tdb.o: $(srcdir)/tdb.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/tdb.h
+test_io.o: $(srcdir)/test_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_badblocks.o: $(srcdir)/tst_badblocks.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_bitops.o: $(srcdir)/tst_bitops.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_byteswap.o: $(srcdir)/tst_byteswap.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_getsize.o: $(srcdir)/tst_getsize.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+tst_iscan.o: $(srcdir)/tst_iscan.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+undo_io.o: $(srcdir)/undo_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+unix_io.o: $(srcdir)/unix_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/ext2fsP.h
+sparse_io.o: $(srcdir)/sparse_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+unlink.o: $(srcdir)/unlink.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+valid_blk.o: $(srcdir)/valid_blk.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+version.o: $(srcdir)/version.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/version.h
+write_bb_file.o: $(srcdir)/write_bb_file.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+rbtree.o: $(srcdir)/rbtree.c $(srcdir)/rbtree.h $(srcdir)/compiler.h
+tst_libext2fs.o: $(srcdir)/tst_libext2fs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/debugfs/debugfs.h \
+ $(srcdir)/ext2fs.h $(top_srcdir)/debugfs/../misc/create_inode.h \
+ $(top_srcdir)/lib/e2p/e2p.h $(top_srcdir)/lib/support/quotaio.h \
+ $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+debug_cmds.o: debug_cmds.c $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+extent_cmds.o: extent_cmds.c $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+tst_cmds.o: tst_cmds.c $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+debugfs.o: $(top_srcdir)/debugfs/debugfs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h $(top_srcdir)/debugfs/../version.h \
+ $(srcdir)/../../e2fsck/jfs_user.h $(srcdir)/kernel-jbd.h \
+ $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h $(srcdir)/compiler.h \
+ $(top_srcdir)/lib/support/plausible.h
+util.o: $(top_srcdir)/debugfs/util.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/debugfs/debugfs.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+ncheck.o: $(top_srcdir)/debugfs/ncheck.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+icheck.o: $(top_srcdir)/debugfs/icheck.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+ls.o: $(top_srcdir)/debugfs/ls.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+lsdel.o: $(top_srcdir)/debugfs/lsdel.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+dump.o: $(top_srcdir)/debugfs/dump.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+set_fields.o: $(top_srcdir)/debugfs/set_fields.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+logdump.o: $(top_srcdir)/debugfs/logdump.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h $(srcdir)/../../e2fsck/jfs_user.h \
+ $(srcdir)/kernel-jbd.h $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h \
+ $(srcdir)/compiler.h $(srcdir)/fast_commit.h
+htree.o: $(top_srcdir)/debugfs/htree.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+unused.o: $(top_srcdir)/debugfs/unused.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+filefrag.o: $(top_srcdir)/debugfs/filefrag.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+extent_inode.o: $(top_srcdir)/debugfs/extent_inode.c \
+ $(top_builddir)/lib/config.h $(top_builddir)/lib/dirpaths.h \
+ $(top_srcdir)/debugfs/debugfs.h $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+zap.o: $(top_srcdir)/debugfs/zap.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+quota.o: $(top_srcdir)/debugfs/quota.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+xattrs.o: $(top_srcdir)/debugfs/xattrs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/support/cstring.h \
+ $(top_srcdir)/debugfs/debugfs.h $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+e2freefrag.o: $(top_srcdir)/misc/e2freefrag.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(top_srcdir)/misc/e2freefrag.h \
+ $(top_srcdir)/debugfs/debugfs.h $(top_srcdir)/lib/ss/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/debugfs/../misc/create_inode.h \
+ $(top_srcdir)/lib/e2p/e2p.h $(top_srcdir)/lib/support/quotaio.h \
+ $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h
+create_inode.o: $(top_srcdir)/misc/create_inode.c \
+ $(top_builddir)/lib/config.h $(top_builddir)/lib/dirpaths.h \
+ $(srcdir)/ext2fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/fiemap.h $(top_srcdir)/misc/create_inode.h \
+ $(top_srcdir)/lib/e2p/e2p.h $(top_srcdir)/lib/support/nls-enable.h
+journal.o: $(top_srcdir)/debugfs/journal.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/journal.h \
+ $(srcdir)/../../e2fsck/jfs_user.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h $(srcdir)/kernel-jbd.h \
+ $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h $(srcdir)/compiler.h
+revoke.o: $(top_srcdir)/e2fsck/revoke.c $(top_srcdir)/e2fsck/jfs_user.h \
+ $(top_builddir)/lib/config.h $(top_builddir)/lib/dirpaths.h \
+ $(srcdir)/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/kernel-jbd.h $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h \
+ $(srcdir)/compiler.h
+recovery.o: $(top_srcdir)/e2fsck/recovery.c $(top_srcdir)/e2fsck/jfs_user.h \
+ $(top_builddir)/lib/config.h $(top_builddir)/lib/dirpaths.h \
+ $(srcdir)/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(srcdir)/ext2fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/kernel-jbd.h $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h \
+ $(srcdir)/compiler.h
+do_journal.o: $(top_srcdir)/debugfs/do_journal.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/debugfs/debugfs.h \
+ $(top_srcdir)/lib/ss/ss.h $(top_builddir)/lib/ss/ss_err.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext3_extents.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \
+ $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(top_srcdir)/debugfs/../misc/create_inode.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(top_srcdir)/lib/support/quotaio.h $(top_srcdir)/lib/support/dqblk_v2.h \
+ $(top_srcdir)/lib/support/quotaio_tree.h $(srcdir)/kernel-jbd.h \
+ $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h $(srcdir)/compiler.h \
+ $(top_srcdir)/debugfs/journal.h $(srcdir)/../../e2fsck/jfs_user.h
diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c
new file mode 100644
index 0000000..3fd9216
--- /dev/null
+++ b/lib/ext2fs/alloc.c
@@ -0,0 +1,554 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
+#else
+# define dbg_printf(f, a...)
+#endif
+
+/*
+ * Clear the uninit block bitmap flag if necessary
+ */
+void ext2fs_clear_block_uninit(ext2_filsys fs, dgrp_t group)
+{
+ if (group >= fs->group_desc_count ||
+ !ext2fs_has_group_desc_csum(fs) ||
+ !(ext2fs_bg_flags_test(fs, group, EXT2_BG_BLOCK_UNINIT)))
+ return;
+
+ /* uninit block bitmaps are now initialized in read_bitmaps() */
+
+ ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
+ ext2fs_group_desc_csum_set(fs, group);
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+}
+
+/*
+ * 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 (group >= fs->group_desc_count ||
+ !ext2fs_has_group_desc_csum(fs) ||
+ !(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);
+ /* Mimics what the kernel does */
+ ext2fs_bg_flags_clear(fs, group, EXT2_BG_BLOCK_UNINIT);
+ ext2fs_group_desc_csum_set(fs, group);
+ ext2fs_mark_ib_dirty(fs);
+ ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * 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 start_inode = 0;
+ ext2_ino_t i, ino_in_group, upto, first_zero;
+ errcode_t retval;
+ dgrp_t group;
+
+ 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) {
+ group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
+ start_inode = (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 {
+ ino_in_group = (i - 1) % EXT2_INODES_PER_GROUP(fs->super);
+ group = (i - 1) / EXT2_INODES_PER_GROUP(fs->super);
+
+ check_inode_uninit(fs, map, group);
+ upto = i + (EXT2_INODES_PER_GROUP(fs->super) - ino_in_group);
+ if (i < start_inode && upto >= start_inode)
+ upto = start_inode - 1;
+ if (upto > fs->super->s_inodes_count)
+ upto = fs->super->s_inodes_count;
+
+ retval = ext2fs_find_first_zero_inode_bitmap2(map, i, upto,
+ &first_zero);
+ if (retval == 0) {
+ i = first_zero;
+ break;
+ }
+ if (retval != ENOENT)
+ return EXT2_ET_INODE_ALLOC_FAIL;
+ i = upto + 1;
+ 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_block3(ext2_filsys fs, blk64_t goal,
+ ext2fs_block_bitmap map, blk64_t *ret,
+ struct blk_alloc_ctx *ctx)
+{
+ errcode_t retval;
+ blk64_t b = 0;
+ errcode_t (*gab)(ext2_filsys fs, blk64_t goal, blk64_t *ret);
+ errcode_t (*gab2)(ext2_filsys, blk64_t, blk64_t *,
+ struct blk_alloc_ctx *);
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (!map) {
+ /*
+ * In case there are clients out there whose get_alloc_block
+ * handlers call ext2fs_new_block2 with a NULL block map,
+ * temporarily swap out the function pointer so that we don't
+ * end up in an infinite loop.
+ */
+ if (fs->get_alloc_block2) {
+ gab2 = fs->get_alloc_block2;
+ fs->get_alloc_block2 = NULL;
+ retval = gab2(fs, goal, &b, ctx);
+ fs->get_alloc_block2 = gab2;
+ goto allocated;
+ } else if (fs->get_alloc_block) {
+ gab = fs->get_alloc_block;
+ fs->get_alloc_block = NULL;
+ retval = gab(fs, goal, &b);
+ fs->get_alloc_block = gab;
+ goto allocated;
+ }
+ }
+ 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;
+ goal &= ~EXT2FS_CLUSTER_MASK(fs);
+
+ retval = ext2fs_find_first_zero_block_bitmap2(map,
+ goal, ext2fs_blocks_count(fs->super) - 1, &b);
+ if ((retval == ENOENT) && (goal != fs->super->s_first_data_block))
+ retval = ext2fs_find_first_zero_block_bitmap2(map,
+ fs->super->s_first_data_block, goal - 1, &b);
+allocated:
+ if (retval == ENOENT)
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+ if (retval)
+ return retval;
+
+ ext2fs_clear_block_uninit(fs, ext2fs_group_of_blk2(fs, b));
+ *ret = b;
+ return 0;
+}
+
+errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal,
+ ext2fs_block_bitmap map, blk64_t *ret)
+{
+ return ext2fs_new_block3(fs, goal, map, ret, NULL);
+}
+
+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_block3(ext2_filsys fs, blk64_t goal, char *block_buf,
+ blk64_t *ret, struct blk_alloc_ctx *ctx)
+{
+ errcode_t retval;
+ blk64_t block;
+
+ if (fs->get_alloc_block2) {
+ retval = (fs->get_alloc_block2)(fs, goal, &block, ctx);
+ if (retval)
+ goto fail;
+ } else 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_block3(fs, goal, 0, &block, ctx);
+ if (retval)
+ goto fail;
+ }
+
+ if (block_buf) {
+ memset(block_buf, 0, fs->blocksize);
+ retval = io_channel_write_blk64(fs->io, block, 1, block_buf);
+ } else
+ retval = ext2fs_zero_blocks2(fs, block, 1, NULL, NULL);
+ if (retval)
+ goto fail;
+
+ ext2fs_block_alloc_stats2(fs, block, +1);
+ *ret = block;
+
+fail:
+ return retval;
+}
+
+errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal,
+ char *block_buf, blk64_t *ret)
+{
+ return ext2fs_alloc_block3(fs, goal, block_buf, ret, NULL);
+}
+
+errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+ char *block_buf, blk_t *ret)
+{
+ errcode_t retval;
+ blk64_t ret64, goal64 = goal;
+ retval = ext2fs_alloc_block3(fs, goal64, block_buf, &ret64, NULL);
+ if (!retval)
+ *ret = (blk_t)ret64;
+ 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;
+ int c_ratio;
+
+ 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;
+ c_ratio = 1 << ext2fs_get_bitmap_granularity(map);
+ b &= ~(c_ratio - 1);
+ finish &= ~(c_ratio -1);
+ do {
+ if (b + num - 1 >= ext2fs_blocks_count(fs->super)) {
+ if (finish > start)
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+ b = fs->super->s_first_data_block;
+ }
+ if (ext2fs_fast_test_block_bitmap_range2(map, b, num)) {
+ *ret = b;
+ return 0;
+ }
+ b += c_ratio;
+ } 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;
+}
+
+blk64_t ext2fs_find_inode_goal(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t lblk)
+{
+ dgrp_t group;
+ __u8 log_flex;
+ struct ext2fs_extent extent;
+ ext2_extent_handle_t handle = NULL;
+ errcode_t err;
+
+ /* Make sure data stored in inode->i_block is neither fast symlink nor
+ * inline data.
+ */
+ if (inode == NULL || ext2fs_is_fast_symlink(inode) ||
+ inode->i_flags & EXT4_INLINE_DATA_FL)
+ goto no_blocks;
+
+ if (inode->i_flags & EXT4_EXTENTS_FL) {
+ err = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (err)
+ goto no_blocks;
+ err = ext2fs_extent_goto2(handle, 0, lblk);
+ if (err)
+ goto no_blocks;
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
+ if (err)
+ goto no_blocks;
+ ext2fs_extent_free(handle);
+ return extent.e_pblk + (lblk - extent.e_lblk);
+ }
+
+ /* block mapped file; see if block zero is mapped? */
+ if (inode->i_block[0])
+ return inode->i_block[0];
+
+no_blocks:
+ ext2fs_extent_free(handle);
+ log_flex = fs->super->s_log_groups_per_flex;
+ group = ext2fs_group_of_ino(fs, ino);
+ if (log_flex)
+ group = group & ~((1 << (log_flex)) - 1);
+ return ext2fs_group_first_block2(fs, group);
+}
+
+/*
+ * Starting at _goal_, scan around the filesystem to find a run of free blocks
+ * that's at least _len_ blocks long. Possible flags:
+ * - EXT2_NEWRANGE_EXACT_GOAL: The range of blocks must start at _goal_.
+ * - EXT2_NEWRANGE_MIN_LENGTH: do not return a allocation shorter than _len_.
+ * - EXT2_NEWRANGE_ZERO_BLOCKS: Zero blocks pblk to pblk+plen before returning.
+ *
+ * The starting block is returned in _pblk_ and the length is returned via
+ * _plen_. The blocks are not marked in the bitmap; the caller must mark
+ * however much of the returned run they actually use, hopefully via
+ * ext2fs_block_alloc_stats_range().
+ *
+ * This function can return a range that is longer than what was requested.
+ */
+errcode_t ext2fs_new_range(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, ext2fs_block_bitmap map, blk64_t *pblk,
+ blk64_t *plen)
+{
+ errcode_t retval;
+ blk64_t start, end, b;
+ int looped = 0;
+ blk64_t max_blocks = ext2fs_blocks_count(fs->super);
+ errcode_t (*nrf)(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, blk64_t *pblk, blk64_t *plen);
+
+ dbg_printf("%s: flags=0x%x goal=%llu len=%llu\n", __func__, flags,
+ goal, len);
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+ if (len == 0 || (flags & ~EXT2_NEWRANGE_ALL_FLAGS))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (!map && fs->new_range) {
+ /*
+ * In case there are clients out there whose new_range
+ * handlers call ext2fs_new_range with a NULL block map,
+ * temporarily swap out the function pointer so that we don't
+ * end up in an infinite loop.
+ */
+ nrf = fs->new_range;
+ fs->new_range = NULL;
+ retval = nrf(fs, flags, goal, len, pblk, plen);
+ fs->new_range = nrf;
+ if (retval)
+ return retval;
+ start = *pblk;
+ end = *pblk + *plen;
+ goto allocated;
+ }
+ 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;
+
+ start = goal;
+ while (!looped || start <= goal) {
+ retval = ext2fs_find_first_zero_block_bitmap2(map, start,
+ max_blocks - 1,
+ &start);
+ if (retval == ENOENT) {
+ /*
+ * If there are no free blocks beyond the starting
+ * point, try scanning the whole filesystem, unless the
+ * user told us only to allocate from _goal_, or if
+ * we're already scanning the whole filesystem.
+ */
+ if (flags & EXT2_NEWRANGE_FIXED_GOAL ||
+ start == fs->super->s_first_data_block)
+ goto fail;
+ start = fs->super->s_first_data_block;
+ continue;
+ } else if (retval)
+ goto errout;
+
+ if (flags & EXT2_NEWRANGE_FIXED_GOAL && start != goal)
+ goto fail;
+
+ b = min(start + len - 1, max_blocks - 1);
+ retval = ext2fs_find_first_set_block_bitmap2(map, start, b,
+ &end);
+ if (retval == ENOENT)
+ end = b + 1;
+ else if (retval)
+ goto errout;
+
+ if (!(flags & EXT2_NEWRANGE_MIN_LENGTH) ||
+ (end - start) >= len) {
+ /* Success! */
+ *pblk = start;
+ *plen = end - start;
+ dbg_printf("%s: new_range goal=%llu--%llu "
+ "blk=%llu--%llu %llu\n",
+ __func__, goal, goal + len - 1,
+ *pblk, *pblk + *plen - 1, *plen);
+allocated:
+ for (b = start; b < end;
+ b += fs->super->s_blocks_per_group)
+ ext2fs_clear_block_uninit(fs,
+ ext2fs_group_of_blk2(fs, b));
+ return 0;
+ }
+
+ if (flags & EXT2_NEWRANGE_FIXED_GOAL)
+ goto fail;
+ start = end;
+ if (start >= max_blocks) {
+ if (looped)
+ goto fail;
+ looped = 1;
+ start = fs->super->s_first_data_block;
+ }
+ }
+
+fail:
+ retval = EXT2_ET_BLOCK_ALLOC_FAIL;
+errout:
+ return retval;
+}
+
+void ext2fs_set_new_range_callback(ext2_filsys fs,
+ errcode_t (*func)(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, blk64_t *pblk, blk64_t *plen),
+ errcode_t (**old)(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, blk64_t *pblk, blk64_t *plen))
+{
+ if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)
+ return;
+
+ if (old)
+ *old = fs->new_range;
+
+ fs->new_range = func;
+}
+
+errcode_t ext2fs_alloc_range(ext2_filsys fs, int flags, blk64_t goal,
+ blk_t len, blk64_t *ret)
+{
+ int newr_flags = EXT2_NEWRANGE_MIN_LENGTH;
+ errcode_t retval;
+ blk64_t plen;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+ if (len == 0 || (flags & ~EXT2_ALLOCRANGE_ALL_FLAGS))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (flags & EXT2_ALLOCRANGE_FIXED_GOAL)
+ newr_flags |= EXT2_NEWRANGE_FIXED_GOAL;
+
+ retval = ext2fs_new_range(fs, newr_flags, goal, len, NULL, ret, &plen);
+ if (retval)
+ return retval;
+
+ if (plen < len)
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+
+ if (flags & EXT2_ALLOCRANGE_ZERO_BLOCKS) {
+ retval = ext2fs_zero_blocks2(fs, *ret, len, NULL, NULL);
+ if (retval)
+ return retval;
+ }
+
+ ext2fs_block_alloc_stats_range(fs, *ret, len, +1);
+ return retval;
+}
diff --git a/lib/ext2fs/alloc_sb.c b/lib/ext2fs/alloc_sb.c
new file mode 100644
index 0000000..8530b40
--- /dev/null
+++ b/lib/ext2fs/alloc_sb.c
@@ -0,0 +1,81 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 old_desc_blocks, num_blocks;
+
+ ext2fs_super_and_bgd_loc2(fs, group, &super_blk,
+ &old_desc_blk, &new_desc_blk, &used_blks);
+
+ if (ext2fs_has_feature_meta_bg(fs->super))
+ 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 ((group == 0) && (fs->blocksize == 1024) &&
+ EXT2FS_CLUSTER_RATIO(fs) > 1)
+ ext2fs_mark_block_bitmap2(bmap, 0);
+
+ if (old_desc_blk) {
+ num_blocks = old_desc_blocks;
+ if (old_desc_blk + num_blocks >= ext2fs_blocks_count(fs->super))
+ num_blocks = ext2fs_blocks_count(fs->super) -
+ old_desc_blk;
+ ext2fs_mark_block_bitmap_range2(bmap, old_desc_blk, num_blocks);
+ }
+ if (new_desc_blk)
+ ext2fs_mark_block_bitmap2(bmap, new_desc_blk);
+
+ num_blocks = ext2fs_group_blocks_count(fs, group);
+ num_blocks -= 2 + fs->inode_blocks_per_group + used_blks;
+
+ return num_blocks ;
+}
diff --git a/lib/ext2fs/alloc_stats.c b/lib/ext2fs/alloc_stats.c
new file mode 100644
index 0000000..6f98bcc
--- /dev/null
+++ b/lib/ext2fs/alloc_stats.c
@@ -0,0 +1,165 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+
+#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);
+
+ if (ino > fs->super->s_inodes_count) {
+#ifndef OMIT_COM_ERR
+ com_err("ext2fs_inode_alloc_stats2", 0,
+ "Illegal inode number: %lu", (unsigned long) ino);
+#endif
+ return;
+ }
+ 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 (ext2fs_has_group_desc_csum(fs)) {
+ 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);
+
+ if (blk < fs->super->s_first_data_block ||
+ blk >= ext2fs_blocks_count(fs->super)) {
+#ifndef OMIT_COM_ERR
+ com_err("ext2fs_block_alloc_stats", 0,
+ "Illegal block number: %lu", (unsigned long) blk);
+#endif
+ return;
+ }
+ 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 * (blk64_t) EXT2FS_CLUSTER_RATIO(fs));
+ 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;
+}
+
+void ext2fs_block_alloc_stats_range(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse)
+{
+#ifndef OMIT_COM_ERR
+ if (blk + num > ext2fs_blocks_count(fs->super)) {
+ com_err("ext2fs_block_alloc_stats_range", 0,
+ "Illegal block range: %llu (%u) ",
+ (unsigned long long) blk, num);
+ return;
+ }
+#endif
+ if (inuse == 0)
+ return;
+ if (inuse > 0) {
+ ext2fs_mark_block_bitmap_range2(fs->block_map, blk, num);
+ inuse = 1;
+ } else {
+ ext2fs_unmark_block_bitmap_range2(fs->block_map, blk, num);
+ inuse = -1;
+ }
+ while (num) {
+ int group = ext2fs_group_of_blk2(fs, blk);
+ blk64_t last_blk = ext2fs_group_last_block2(fs, group);
+ blk64_t n = num;
+
+ if (blk + num > last_blk)
+ n = last_blk - blk + 1;
+
+ ext2fs_bg_free_blocks_count_set(fs, group,
+ ext2fs_bg_free_blocks_count(fs, group) -
+ inuse*n/EXT2FS_CLUSTER_RATIO(fs));
+ 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 * (blk64_t) n);
+ blk += n;
+ num -= n;
+ }
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+ if (fs->block_alloc_stats_range)
+ (fs->block_alloc_stats_range)(fs, blk, num, inuse);
+}
+
+void ext2fs_set_block_alloc_stats_range_callback(ext2_filsys fs,
+ void (*func)(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse),
+ void (**old)(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse))
+{
+ if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)
+ return;
+ if (old)
+ *old = fs->block_alloc_stats_range;
+
+ fs->block_alloc_stats_range = func;
+}
diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c
new file mode 100644
index 0000000..e8a1fef
--- /dev/null
+++ b/lib/ext2fs/alloc_tables.c
@@ -0,0 +1,278 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 continuously and in order.
+ */
+static blk64_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk64_t start_blk,
+ ext2fs_block_bitmap bmap, int rem_grp,
+ int elem_size)
+{
+ int flexbg, flexbg_size, size;
+ blk64_t last_blk, first_free = 0;
+ dgrp_t last_grp;
+
+ flexbg_size = 1U << fs->super->s_log_groups_per_flex;
+ flexbg = group / flexbg_size;
+ size = rem_grp * elem_size;
+
+ if (size > (int) (fs->super->s_blocks_per_group / 4))
+ size = (int) fs->super->s_blocks_per_group / 4;
+
+ /*
+ * Don't do a long search if the previous block search is still valid,
+ * but skip minor obstructions such as group descriptor backups.
+ */
+ if (start_blk && start_blk < ext2fs_blocks_count(fs->super) &&
+ ext2fs_get_free_blocks2(fs, start_blk, start_blk + size, elem_size,
+ bmap, &first_free) == 0)
+ return first_free;
+
+ start_blk = ext2fs_group_first_block2(fs, flexbg_size * flexbg);
+ last_grp = group | (flexbg_size - 1);
+ if (last_grp > fs->group_desc_count-1)
+ last_grp = fs->group_desc_count-1;
+ last_blk = ext2fs_group_last_block2(fs, last_grp);
+
+ /* Find the first available block */
+ if (ext2fs_get_free_blocks2(fs, start_blk, last_blk, size,
+ bmap, &first_free) == 0)
+ return first_free;
+
+ if (ext2fs_get_free_blocks2(fs, start_blk, last_blk, elem_size,
+ bmap, &first_free) == 0)
+ return first_free;
+
+ if (ext2fs_get_free_blocks2(fs, 0, last_blk, elem_size, bmap,
+ &first_free) == 0)
+ 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;
+ dgrp_t last_grp = 0;
+ int rem_grps = 0, flexbg_size = 0, table_offset = 0;
+
+ group_blk = ext2fs_group_first_block2(fs, group);
+ last_blk = ext2fs_group_last_block2(fs, group);
+
+ if (!bmap)
+ bmap = fs->block_map;
+
+ if (ext2fs_has_feature_flex_bg(fs->super) &&
+ fs->super->s_log_groups_per_flex) {
+ flexbg_size = 1U << fs->super->s_log_groups_per_flex;
+ last_grp = group | (flexbg_size - 1);
+ if (last_grp > fs->group_desc_count-1)
+ last_grp = fs->group_desc_count-1;
+ rem_grps = last_grp - group + 1;
+ }
+
+ /*
+ * Allocate the block and inode bitmaps, if necessary
+ */
+ if (fs->stride && !flexbg_size) {
+ 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;
+
+ table_offset = flexbg_size;
+ if (group % flexbg_size)
+ prev_block = ext2fs_block_bitmap_loc(fs, group - 1) + 1;
+ else if (last_grp == fs->group_desc_count-1) {
+ /*
+ * If we are allocating for the last flex_bg
+ * keep the metadata tables contiguous
+ */
+ table_offset = last_grp & (flexbg_size - 1);
+ if (table_offset == 0)
+ table_offset = flexbg_size;
+ else
+ table_offset++;
+ }
+ /* FIXME: Take backup group descriptor blocks into account
+ * if the flexbg allocations will grow to overlap them... */
+ start_blk = flexbg_offset(fs, group, prev_block, bmap,
+ 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 % flexbg_size)
+ prev_block = ext2fs_inode_bitmap_loc(fs, group - 1) + 1;
+ else
+ prev_block = ext2fs_block_bitmap_loc(fs, group) +
+ table_offset;
+ /* FIXME: Take backup group descriptor blocks into account
+ * if the flexbg allocations will grow to overlap them... */
+ start_blk = flexbg_offset(fs, group, prev_block, bmap,
+ 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 % flexbg_size)
+ prev_block = ext2fs_inode_table_loc(fs, group - 1) +
+ fs->inode_blocks_per_group;
+ else
+ prev_block = ext2fs_inode_bitmap_loc(fs, group) +
+ table_offset;
+
+ /* FIXME: Take backup group descriptor blocks into account
+ * if the flexbg allocations will grow to overlap them... */
+ group_blk = flexbg_offset(fs, group, prev_block, bmap,
+ 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;
+
+ ext2fs_mark_block_bitmap_range2(bmap,
+ new_blk, fs->inode_blocks_per_group);
+ if (flexbg_size) {
+ blk64_t num, blk;
+ num = fs->inode_blocks_per_group;
+ blk = new_blk;
+ while (num) {
+ int gr = ext2fs_group_of_blk2(fs, blk);
+ last_blk = ext2fs_group_last_block2(fs, gr);
+ blk64_t n = num;
+
+ if (blk + num > last_blk)
+ n = last_blk - blk + 1;
+
+ ext2fs_bg_free_blocks_count_set(fs, gr,
+ ext2fs_bg_free_blocks_count(fs, gr) -
+ n/EXT2FS_CLUSTER_RATIO(fs));
+ ext2fs_bg_flags_clear(fs, gr,
+ EXT2_BG_BLOCK_UNINIT);
+ ext2fs_group_desc_csum_set(fs, gr);
+ ext2fs_free_blocks_count_add(fs->super, -n);
+ blk += n;
+ num -= n;
+ }
+ }
+ 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;
+
+ if (fs->progress_ops && fs->progress_ops->init)
+ (fs->progress_ops->init)(fs, &progress, NULL,
+ fs->group_desc_count);
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (fs->progress_ops && fs->progress_ops->update)
+ (fs->progress_ops->update)(fs, &progress, i);
+ retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
+ if (retval)
+ return retval;
+ }
+ if (fs->progress_ops && fs->progress_ops->close)
+ (fs->progress_ops->close)(fs, &progress, NULL);
+ return 0;
+}
+
diff --git a/lib/ext2fs/atexit.c b/lib/ext2fs/atexit.c
new file mode 100644
index 0000000..b3be1d5
--- /dev/null
+++ b/lib/ext2fs/atexit.c
@@ -0,0 +1,116 @@
+/*
+ * atexit.c --- Clean things up when we exit normally.
+ *
+ * Copyright Oracle, 2014
+ * Author Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include <stdlib.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+struct exit_data {
+ ext2_exit_fn func;
+ void *data;
+};
+
+static struct exit_data *items;
+static size_t nr_items;
+
+static void handle_exit(void)
+{
+ struct exit_data *ed;
+
+ for (ed = items + nr_items - 1; ed >= items; ed--) {
+ if (ed->func == NULL)
+ continue;
+ ed->func(ed->data);
+ }
+
+ ext2fs_free_mem(&items);
+ nr_items = 0;
+}
+
+/*
+ * Schedule a function to be called at (normal) program termination.
+ * If you want this to be called during a signal exit, you must capture
+ * the signal and call exit() yourself!
+ */
+errcode_t ext2fs_add_exit_fn(ext2_exit_fn func, void *data)
+{
+ struct exit_data *ed, *free_ed = NULL;
+ size_t x;
+ errcode_t ret;
+
+ if (func == NULL)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ for (x = 0, ed = items; x < nr_items; x++, ed++) {
+ if (ed->func == func && ed->data == data)
+ return EXT2_ET_FILE_EXISTS;
+ if (ed->func == NULL)
+ free_ed = ed;
+ }
+
+ if (free_ed) {
+ free_ed->func = func;
+ free_ed->data = data;
+ return 0;
+ }
+
+ if (nr_items == 0) {
+ ret = atexit(handle_exit);
+ if (ret)
+ return ret;
+ }
+
+ ret = ext2fs_resize_mem(0, (nr_items + 1) * sizeof(struct exit_data),
+ &items);
+ if (ret)
+ return ret;
+
+ items[nr_items].func = func;
+ items[nr_items].data = data;
+ nr_items++;
+
+ return 0;
+}
+
+/* Remove a function from the exit cleanup list. */
+errcode_t ext2fs_remove_exit_fn(ext2_exit_fn func, void *data)
+{
+ struct exit_data *ed;
+ size_t x;
+
+ if (func == NULL)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ for (x = 0, ed = items; x < nr_items; x++, ed++) {
+ if (ed->func == NULL)
+ return 0;
+ if (ed->func == func && ed->data == data) {
+ size_t sz = (nr_items - (x + 1)) *
+ sizeof(struct exit_data);
+ memmove(ed, ed + 1, sz);
+ memset(items + nr_items - 1, 0,
+ sizeof(struct exit_data));
+ }
+ }
+
+ return 0;
+}
diff --git a/lib/ext2fs/badblocks.c b/lib/ext2fs/badblocks.c
new file mode 100644
index 0000000..a306bc0
--- /dev/null
+++ b/lib/ext2fs/badblocks.c
@@ -0,0 +1,328 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 an item to a tracking list (e.g. badblocks or casefold).
+ */
+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 = ((unsigned)low + (unsigned)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/ext2fs/bb_compat.c b/lib/ext2fs/bb_compat.c
new file mode 100644
index 0000000..373792a
--- /dev/null
+++ b/lib/ext2fs/bb_compat.c
@@ -0,0 +1,64 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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/ext2fs/bb_inode.c b/lib/ext2fs/bb_inode.c
new file mode 100644
index 0000000..11f10eb
--- /dev/null
+++ b/lib/ext2fs/bb_inode.c
@@ -0,0 +1,270 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 indirect 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);
+ retval = ext2fs_inode_size_set(fs, &inode,
+ rec.bad_block_count * fs->blocksize);
+ if (retval)
+ goto cleanup;
+
+ 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/ext2fs/bitmaps.c b/lib/ext2fs/bitmaps.c
new file mode 100644
index 0000000..8bfa24b
--- /dev/null
+++ b/lib/ext2fs/bitmaps.c
@@ -0,0 +1,320 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+#include "bmap64.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);
+
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ start = 1;
+ end = fs->super->s_inodes_count;
+ real_end = (__u64)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,
+ fs->default_bitmap_type,
+ 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);
+
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ start = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ end = EXT2FS_B2C(fs, ext2fs_blocks_count(fs->super)-1);
+ real_end = ((__u64) EXT2_CLUSTERS_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,
+ fs->default_bitmap_type,
+ 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));
+}
+
+/*
+ * ext2fs_allocate_block_bitmap() really allocates a per-cluster
+ * bitmap for backwards compatibility. This function allocates a
+ * block bitmap which is truly per-block, even if clusters/bigalloc
+ * are enabled. mke2fs and e2fsck need this for tracking the
+ * allocation of the file system metadata blocks.
+ */
+errcode_t ext2fs_allocate_subcluster_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_block_bitmap *ret)
+{
+ __u64 start, end, real_end;
+ ext2fs_generic_bitmap bmap;
+ ext2fs_generic_bitmap_64 bmap64;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ if (!fs->cluster_ratio_bits)
+ return ext2fs_allocate_block_bitmap(fs, descr, ret);
+
+ if ((fs->flags & EXT2_FLAG_64BITS) == 0)
+ return EXT2_ET_CANT_USE_LEGACY_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;
+
+ retval = ext2fs_alloc_generic_bmap(fs, EXT2_ET_MAGIC_BLOCK_BITMAP64,
+ fs->default_bitmap_type, start,
+ end, real_end, descr, &bmap);
+ if (retval)
+ return retval;
+ bmap64 = (ext2fs_generic_bitmap_64) bmap;
+ bmap64->cluster_bits = 0;
+ *ret = bmap;
+ return 0;
+}
+
+int ext2fs_get_bitmap_granularity(ext2fs_block_bitmap bitmap)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) bitmap;
+
+ if (!EXT2FS_IS_64_BITMAP(bmap))
+ return 0;
+
+ return bmap->cluster_bits;
+}
+
+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/ext2fs/bitops.c b/lib/ext2fs/bitops.c
new file mode 100644
index 0000000..ce2acc4
--- /dev/null
+++ b/lib/ext2fs/bitops.c
@@ -0,0 +1,148 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * C language bitmap functions 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);
+}
+
+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
+}
+
+/* Bitmap functions that take a 64-bit offset */
+
+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);
+}
+
+static unsigned int popcount8(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res + (res >> 4)) & 0x0F;
+}
+
+static unsigned int popcount32(unsigned int w)
+{
+ unsigned int res = w - ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
+unsigned int ext2fs_bitcount(const void *addr, unsigned int nbytes)
+{
+ const unsigned char *cp = addr;
+ const __u32 *p;
+ unsigned int res = 0;
+
+ while (((((uintptr_t) cp) & 3) != 0) && (nbytes > 0)) {
+ res += popcount8(*cp++);
+ nbytes--;
+ }
+ p = (const __u32 *) cp;
+
+ while (nbytes > 4) {
+ res += popcount32(*p++);
+ nbytes -= 4;
+ }
+ cp = (const unsigned char *) p;
+
+ while (nbytes > 0) {
+ res += popcount8(*cp++);
+ nbytes--;
+ }
+ return res;
+}
diff --git a/lib/ext2fs/bitops.h b/lib/ext2fs/bitops.h
new file mode 100644
index 0000000..9edf594
--- /dev/null
+++ b/lib/ext2fs/bitops.h
@@ -0,0 +1,606 @@
+/*
+ * 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%
+ */
+
+#ifdef WORDS_BIGENDIAN
+#define ext2fs_cpu_to_le64(x) ((__force __le64)ext2fs_swab64((__u64)(x)))
+#define ext2fs_le64_to_cpu(x) ext2fs_swab64((__force __u64)(__le64)(x))
+#define ext2fs_cpu_to_le32(x) ((__force __le32)ext2fs_swab32((__u32)(x)))
+#define ext2fs_le32_to_cpu(x) ext2fs_swab32((__force __u32)(__le32)(x))
+#define ext2fs_cpu_to_le16(x) ((__force __le16)ext2fs_swab16((__u16)(x)))
+#define ext2fs_le16_to_cpu(x) ext2fs_swab16((__force __u16)(__le16)(x))
+
+#define ext2fs_cpu_to_be64(x) ((__force __be64)(__u64)(x))
+#define ext2fs_be64_to_cpu(x) ((__force __u64)(__be64)(x))
+#define ext2fs_cpu_to_be32(x) ((__force __be32)(__u32)(x))
+#define ext2fs_be32_to_cpu(x) ((__force __u32)(__be32)(x))
+#define ext2fs_cpu_to_be16(x) ((__force __be16)(__u16)(x))
+#define ext2fs_be16_to_cpu(x) ((__force __u16)(__be16)(x))
+#else
+#define ext2fs_cpu_to_le64(x) ((__force __le64)(__u64)(x))
+#define ext2fs_le64_to_cpu(x) ((__force __u64)(__le64)(x))
+#define ext2fs_cpu_to_le32(x) ((__force __le32)(__u32)(x))
+#define ext2fs_le32_to_cpu(x) ((__force __u32)(__le32)(x))
+#define ext2fs_cpu_to_le16(x) ((__force __le16)(__u16)(x))
+#define ext2fs_le16_to_cpu(x) ((__force __u16)(__le16)(x))
+
+#define ext2fs_cpu_to_be64(x) ((__force __be64)ext2fs_swab64((__u64)(x)))
+#define ext2fs_be64_to_cpu(x) ext2fs_swab64((__force __u64)(__be64)(x))
+#define ext2fs_cpu_to_be32(x) ((__force __be32)ext2fs_swab32((__u32)(x)))
+#define ext2fs_be32_to_cpu(x) ext2fs_swab32((__force __u32)(__be32)(x))
+#define ext2fs_cpu_to_be16(x) ((__force __be16)ext2fs_swab16((__u16)(x)))
+#define ext2fs_be16_to_cpu(x) ext2fs_swab16((__force __u16)(__be16)(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);
+
+#ifdef NO_INLINE_FUNCS
+extern void ext2fs_fast_set_bit(unsigned int nr,void * addr);
+extern void ext2fs_fast_clear_bit(unsigned int nr, 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);
+
+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_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);
+#endif
+
+/* These functions routines moved to gen_bitmap.c */
+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,
+ ext2_ino_t inode, int num);
+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 void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
+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 */
+
+#ifdef NO_INLINE_FUNCS
+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 errcode_t ext2fs_find_first_zero_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out);
+extern errcode_t ext2fs_find_first_zero_inode_bitmap2(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t start,
+ ext2_ino_t end,
+ ext2_ino_t *out);
+extern errcode_t ext2fs_find_first_set_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out);
+extern errcode_t ext2fs_find_first_set_inode_bitmap2(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t start,
+ ext2_ino_t end,
+ ext2_ino_t *out);
+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);
+#endif
+
+/* 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);
+extern errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end,
+ __u64 *out);
+extern errcode_t ext2fs_find_first_set_generic_bmap(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end,
+ __u64 *out);
+
+/*
+ * 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
+ */
+
+#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+#ifdef INCLUDE_INLINE_FUNCS
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ extern inline
+#else
+#define _INLINE_ inline
+#endif
+#else /* !INCLUDE_INLINE FUNCS */
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ inline
+#else /* not C99 */
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else /* For Watcom C */
+#define _INLINE_ extern inline
+#endif /* __GNUC__ */
+#endif /* __STDC_VERSION__ >= 199901L */
+#endif /* INCLUDE_INLINE_FUNCS */
+
+/*
+ * 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 |= (unsigned char) (1 << (nr & 0x07));
+}
+
+_INLINE_ void ext2fs_fast_clear_bit(unsigned int nr, void * addr)
+{
+ unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ *ADDR &= (unsigned char) ~(1 << (nr & 0x07));
+}
+
+
+_INLINE_ void ext2fs_fast_set_bit64(__u64 nr, void * addr)
+{
+ unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ *ADDR |= (unsigned char) (1 << (nr & 0x07));
+}
+
+_INLINE_ void ext2fs_fast_clear_bit64(__u64 nr, void * addr)
+{
+ unsigned char *ADDR = (unsigned char *) addr;
+
+ ADDR += nr >> 3;
+ *ADDR &= (unsigned char) ~(1 << (nr & 0x07));
+}
+
+_INLINE_ __u16 ext2fs_swab16(__u16 val)
+{
+ return (val >> 8) | (__u16) (val << 8);
+}
+
+_INLINE_ __u32 ext2fs_swab32(__u32 val)
+{
+ return ((val>>24) | ((val>>8)&0xFF00) |
+ ((val<<8)&0xFF0000) | (val<<24));
+}
+
+_INLINE_ __u64 ext2fs_swab64(__u64 val)
+{
+ return (ext2fs_swab32((__u32) (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_ errcode_t ext2fs_find_first_zero_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out)
+{
+ __u64 o;
+ errcode_t rv;
+
+ rv = ext2fs_find_first_zero_generic_bmap((ext2fs_generic_bitmap) bitmap,
+ start, end, &o);
+ if (!rv)
+ *out = o;
+ return rv;
+}
+
+_INLINE_ errcode_t ext2fs_find_first_zero_inode_bitmap2(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t start,
+ ext2_ino_t end,
+ ext2_ino_t *out)
+{
+ __u64 o;
+ errcode_t rv;
+
+ rv = ext2fs_find_first_zero_generic_bmap((ext2fs_generic_bitmap) bitmap,
+ start, end, &o);
+ if (!rv)
+ *out = (ext2_ino_t) o;
+ return rv;
+}
+
+_INLINE_ errcode_t ext2fs_find_first_set_block_bitmap2(ext2fs_block_bitmap bitmap,
+ blk64_t start,
+ blk64_t end,
+ blk64_t *out)
+{
+ __u64 o;
+ errcode_t rv;
+
+ rv = ext2fs_find_first_set_generic_bmap((ext2fs_generic_bitmap) bitmap,
+ start, end, &o);
+ if (!rv)
+ *out = o;
+ return rv;
+}
+
+_INLINE_ errcode_t ext2fs_find_first_set_inode_bitmap2(ext2fs_inode_bitmap bitmap,
+ ext2_ino_t start,
+ ext2_ino_t end,
+ ext2_ino_t *out)
+{
+ __u64 o;
+ errcode_t rv;
+
+ rv = ext2fs_find_first_set_generic_bmap((ext2fs_generic_bitmap) bitmap,
+ start, end, &o);
+ if (!rv)
+ *out = (ext2_ino_t) o;
+ return rv;
+}
+
+_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 (ext2_ino_t) 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 (ext2_ino_t) 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
+
+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 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 unsigned int ext2fs_bitcount(const void *addr, unsigned int nbytes);
diff --git a/lib/ext2fs/blkmap64_ba.c b/lib/ext2fs/blkmap64_ba.c
new file mode 100644
index 0000000..5d8f154
--- /dev/null
+++ b/lib/ext2fs/blkmap64_ba.c
@@ -0,0 +1,492 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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_64 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_64 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_64 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_64 src,
+ ext2fs_generic_bitmap_64 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_64 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_64 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_64 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_64 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_64 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_64 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_64 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_64 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_64 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_64 bitmap)
+{
+ ext2fs_ba_private bp = (ext2fs_ba_private) bitmap->private;
+
+ memset(bp->bitarray, 0,
+ (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
+
+#ifdef ENABLE_BMAP_STATS
+static void ba_print_stats(ext2fs_generic_bitmap_64 bitmap)
+{
+ fprintf(stderr, "%16llu Bytes used by bitarray\n", (unsigned long long)
+ ((bitmap->real_end - bitmap->start) >> 3) + 1 +
+ sizeof(struct ext2fs_ba_private_struct));
+}
+#else
+static void ba_print_stats(ext2fs_generic_bitmap_64 bitmap EXT2FS_ATTR((unused)))
+{
+}
+#endif
+
+/* Find the first zero bit between start and end, inclusive. */
+static errcode_t ba_find_first_zero(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ ext2fs_ba_private bp = (ext2fs_ba_private)bitmap->private;
+ unsigned long bitpos = start - bitmap->start;
+ unsigned long count = end - start + 1;
+ int byte_found = 0; /* whether a != 0xff byte has been found */
+ const unsigned char *pos;
+ unsigned long max_loop_count, i;
+
+ /* scan bits until we hit a byte boundary */
+ while ((bitpos & 0x7) != 0 && count > 0) {
+ if (!ext2fs_test_bit64(bitpos, bp->bitarray)) {
+ *out = bitpos + bitmap->start;
+ return 0;
+ }
+ bitpos++;
+ count--;
+ }
+
+ if (!count)
+ return ENOENT;
+
+ pos = ((unsigned char *)bp->bitarray) + (bitpos >> 3);
+ /* scan bytes until 8-byte (64-bit) aligned */
+ while (count >= 8 && (((uintptr_t)pos) & 0x07)) {
+ if (*pos != 0xff) {
+ byte_found = 1;
+ break;
+ }
+ pos++;
+ count -= 8;
+ bitpos += 8;
+ }
+
+ if (!byte_found) {
+ max_loop_count = count >> 6; /* 8-byte blocks */
+ i = max_loop_count;
+ while (i) {
+ if (*((const __u64 *)pos) != ((__u64)-1))
+ break;
+ pos += 8;
+ i--;
+ }
+ count -= 64 * (max_loop_count - i);
+ bitpos += 64 * (max_loop_count - i);
+
+ max_loop_count = count >> 3;
+ i = max_loop_count;
+ while (i) {
+ if (*pos != 0xff) {
+ byte_found = 1;
+ break;
+ }
+ pos++;
+ i--;
+ }
+ count -= 8 * (max_loop_count - i);
+ bitpos += 8 * (max_loop_count - i);
+ }
+
+ /* Here either count < 8 or byte_found == 1. */
+ while (count-- > 0) {
+ if (!ext2fs_test_bit64(bitpos, bp->bitarray)) {
+ *out = bitpos + bitmap->start;
+ return 0;
+ }
+ bitpos++;
+ }
+
+ return ENOENT;
+}
+
+/* Find the first one bit between start and end, inclusive. */
+static errcode_t ba_find_first_set(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ ext2fs_ba_private bp = (ext2fs_ba_private)bitmap->private;
+ unsigned long bitpos = start - bitmap->start;
+ unsigned long count = end - start + 1;
+ int byte_found = 0; /* whether a != 0xff byte has been found */
+ const unsigned char *pos;
+ unsigned long max_loop_count, i;
+
+ /* scan bits until we hit a byte boundary */
+ while ((bitpos & 0x7) != 0 && count > 0) {
+ if (ext2fs_test_bit64(bitpos, bp->bitarray)) {
+ *out = bitpos + bitmap->start;
+ return 0;
+ }
+ bitpos++;
+ count--;
+ }
+
+ if (!count)
+ return ENOENT;
+
+ pos = ((unsigned char *)bp->bitarray) + (bitpos >> 3);
+ /* scan bytes until 8-byte (64-bit) aligned */
+ while (count >= 8 && (((uintptr_t)pos) & 0x07)) {
+ if (*pos != 0) {
+ byte_found = 1;
+ break;
+ }
+ pos++;
+ count -= 8;
+ bitpos += 8;
+ }
+
+ if (!byte_found) {
+ max_loop_count = count >> 6; /* 8-byte blocks */
+ i = max_loop_count;
+ while (i) {
+ if (*((const __u64 *)pos) != 0)
+ break;
+ pos += 8;
+ i--;
+ }
+ count -= 64 * (max_loop_count - i);
+ bitpos += 64 * (max_loop_count - i);
+
+ max_loop_count = count >> 3;
+ i = max_loop_count;
+ while (i) {
+ if (*pos != 0) {
+ byte_found = 1;
+ break;
+ }
+ pos++;
+ i--;
+ }
+ count -= 8 * (max_loop_count - i);
+ bitpos += 8 * (max_loop_count - i);
+ }
+
+ /* Here either count < 8 or byte_found == 1. */
+ while (count-- > 0) {
+ if (ext2fs_test_bit64(bitpos, bp->bitarray)) {
+ *out = bitpos + bitmap->start;
+ return 0;
+ }
+ bitpos++;
+ }
+
+ return ENOENT;
+}
+
+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,
+ .print_stats = ba_print_stats,
+ .find_first_zero = ba_find_first_zero,
+ .find_first_set = ba_find_first_set
+};
diff --git a/lib/ext2fs/blkmap64_rb.c b/lib/ext2fs/blkmap64_rb.c
new file mode 100644
index 0000000..0df58dc
--- /dev/null
+++ b/lib/ext2fs/blkmap64_rb.c
@@ -0,0 +1,998 @@
+/*
+ * blkmap64_rb.c --- Simple rb-tree implementation for bitmaps
+ *
+ * (C)2010 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "bmap64.h"
+#include "rbtree.h"
+
+#include <limits.h>
+
+struct bmap_rb_extent {
+ struct rb_node node;
+ __u64 start;
+ __u64 count;
+};
+
+struct ext2fs_rb_private {
+ struct rb_root root;
+ struct bmap_rb_extent *wcursor;
+ struct bmap_rb_extent *rcursor;
+ struct bmap_rb_extent *rcursor_next;
+#ifdef ENABLE_BMAP_STATS_OPS
+ __u64 mark_hit;
+ __u64 test_hit;
+#endif
+};
+
+inline static struct bmap_rb_extent *node_to_extent(struct rb_node *node)
+{
+ /*
+ * This depends on the fact the struct rb_node is at the
+ * beginning of the bmap_rb_extent structure. We use this
+ * instead of the ext2fs_rb_entry macro because it causes gcc
+ * -Wall to generate a huge amount of noise.
+ */
+ return (struct bmap_rb_extent *) node;
+}
+
+static int rb_insert_extent(__u64 start, __u64 count,
+ struct ext2fs_rb_private *);
+static void rb_get_new_extent(struct bmap_rb_extent **, __u64, __u64);
+
+/* #define DEBUG_RB */
+
+#ifdef DEBUG_RB
+static void print_tree(struct rb_root *root)
+{
+ struct rb_node *node = NULL;
+ struct bmap_rb_extent *ext;
+
+ fprintf(stderr, "\t\t\t=================================\n");
+ node = ext2fs_rb_first(root);
+ for (node = ext2fs_rb_first(root); node != NULL;
+ node = ext2fs_rb_next(node)) {
+ ext = node_to_extent(node);
+ fprintf(stderr, "\t\t\t--> (%llu -> %llu)\n",
+ (unsigned long long) ext->start,
+ (unsigned long long) ext->start + ext->count);
+ }
+ fprintf(stderr, "\t\t\t=================================\n");
+}
+
+static void check_tree(struct rb_root *root, const char *msg)
+{
+ struct rb_node *node;
+ struct bmap_rb_extent *ext, *old = NULL;
+
+ for (node = ext2fs_rb_first(root); node;
+ node = ext2fs_rb_next(node)) {
+ ext = node_to_extent(node);
+ if (ext->count == 0) {
+ fprintf(stderr, "Tree Error: count is zero\n");
+ fprintf(stderr, "extent: %llu -> %llu (%llu)\n",
+ (unsigned long long) ext->start,
+ (unsigned long long) ext->start + ext->count,
+ (unsigned long long) ext->count);
+ goto err_out;
+ }
+ if (ext->start + ext->count < ext->start) {
+ fprintf(stderr,
+ "Tree Error: start or count is crazy\n");
+ fprintf(stderr, "extent: %llu -> %llu (%llu)\n",
+ (unsigned long long) ext->start,
+ (unsigned long long) ext->start + ext->count,
+ (unsigned long long) ext->count);
+ goto err_out;
+ }
+
+ if (old) {
+ if (old->start > ext->start) {
+ fprintf(stderr, "Tree Error: start is crazy\n");
+ fprintf(stderr, "extent: %llu -> %llu (%llu)\n",
+ (unsigned long long) old->start,
+ (unsigned long long) old->start + old->count,
+ (unsigned long long) old->count);
+ fprintf(stderr,
+ "extent next: %llu -> %llu (%llu)\n",
+ (unsigned long long) ext->start,
+ (unsigned long long) ext->start + ext->count,
+ (unsigned long long) ext->count);
+ goto err_out;
+ }
+ if ((old->start + old->count) >= ext->start) {
+ fprintf(stderr,
+ "Tree Error: extent is crazy\n");
+ fprintf(stderr, "extent: %llu -> %llu (%llu)\n",
+ (unsigned long long) old->start,
+ (unsigned long long) old->start + old->count,
+ (unsigned long long) old->count);
+ fprintf(stderr,
+ "extent next: %llu -> %llu (%llu)\n",
+ (unsigned long long) ext->start,
+ (unsigned long long) ext->start + ext->count,
+ (unsigned long long) ext->count);
+ goto err_out;
+ }
+ }
+ old = ext;
+ }
+ return;
+
+err_out:
+ fprintf(stderr, "%s\n", msg);
+ print_tree(root);
+ exit(1);
+}
+#else
+#define check_tree(root, msg) do {} while (0)
+#define print_tree(root) do {} while (0)
+#endif
+
+static void rb_get_new_extent(struct bmap_rb_extent **ext, __u64 start,
+ __u64 count)
+{
+ struct bmap_rb_extent *new_ext;
+ int retval;
+
+ retval = ext2fs_get_mem(sizeof (struct bmap_rb_extent),
+ &new_ext);
+ if (retval)
+ abort();
+
+ new_ext->start = start;
+ new_ext->count = count;
+ *ext = new_ext;
+}
+
+inline
+static void rb_free_extent(struct ext2fs_rb_private *bp,
+ struct bmap_rb_extent *ext)
+{
+ if (bp->wcursor == ext)
+ bp->wcursor = NULL;
+ if (bp->rcursor == ext)
+ bp->rcursor = NULL;
+ if (bp->rcursor_next == ext)
+ bp->rcursor_next = NULL;
+ ext2fs_free_mem(&ext);
+}
+
+static errcode_t rb_alloc_private_data (ext2fs_generic_bitmap_64 bitmap)
+{
+ struct ext2fs_rb_private *bp;
+ errcode_t retval;
+
+ retval = ext2fs_get_mem(sizeof (struct ext2fs_rb_private), &bp);
+ if (retval)
+ return retval;
+
+ bp->root = RB_ROOT;
+ bp->rcursor = NULL;
+ bp->rcursor_next = NULL;
+ bp->wcursor = NULL;
+
+#ifdef ENABLE_BMAP_STATS_OPS
+ bp->test_hit = 0;
+ bp->mark_hit = 0;
+#endif
+
+ bitmap->private = (void *) bp;
+ return 0;
+}
+
+static errcode_t rb_new_bmap(ext2_filsys fs EXT2FS_ATTR((unused)),
+ ext2fs_generic_bitmap_64 bitmap)
+{
+ errcode_t retval;
+
+ retval = rb_alloc_private_data (bitmap);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+static void rb_free_tree(struct rb_root *root)
+{
+ struct bmap_rb_extent *ext;
+ struct rb_node *node, *next;
+
+ for (node = ext2fs_rb_first(root); node; node = next) {
+ next = ext2fs_rb_next(node);
+ ext = node_to_extent(node);
+ ext2fs_rb_erase(node, root);
+ ext2fs_free_mem(&ext);
+ }
+}
+
+static void rb_free_bmap(ext2fs_generic_bitmap_64 bitmap)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+
+ rb_free_tree(&bp->root);
+ ext2fs_free_mem(&bp);
+ bp = 0;
+}
+
+static errcode_t rb_copy_bmap(ext2fs_generic_bitmap_64 src,
+ ext2fs_generic_bitmap_64 dest)
+{
+ struct ext2fs_rb_private *src_bp, *dest_bp;
+ struct bmap_rb_extent *src_ext, *dest_ext;
+ struct rb_node *dest_node, *src_node, *dest_last, **n;
+ errcode_t retval = 0;
+
+ retval = rb_alloc_private_data (dest);
+ if (retval)
+ return retval;
+
+ src_bp = (struct ext2fs_rb_private *) src->private;
+ dest_bp = (struct ext2fs_rb_private *) dest->private;
+ src_bp->rcursor = NULL;
+ dest_bp->rcursor = NULL;
+
+ src_node = ext2fs_rb_first(&src_bp->root);
+ while (src_node) {
+ src_ext = node_to_extent(src_node);
+ retval = ext2fs_get_mem(sizeof (struct bmap_rb_extent),
+ &dest_ext);
+ if (retval)
+ break;
+
+ memcpy(dest_ext, src_ext, sizeof(struct bmap_rb_extent));
+
+ dest_node = &dest_ext->node;
+ n = &dest_bp->root.rb_node;
+
+ dest_last = NULL;
+ if (*n) {
+ dest_last = ext2fs_rb_last(&dest_bp->root);
+ n = &(dest_last)->rb_right;
+ }
+
+ ext2fs_rb_link_node(dest_node, dest_last, n);
+ ext2fs_rb_insert_color(dest_node, &dest_bp->root);
+
+ src_node = ext2fs_rb_next(src_node);
+ }
+
+ return retval;
+}
+
+static void rb_truncate(__u64 new_max, struct rb_root *root)
+{
+ struct bmap_rb_extent *ext;
+ struct rb_node *node;
+
+ node = ext2fs_rb_last(root);
+ while (node) {
+ ext = node_to_extent(node);
+
+ if ((ext->start + ext->count - 1) <= new_max)
+ break;
+ else if (ext->start > new_max) {
+ ext2fs_rb_erase(node, root);
+ ext2fs_free_mem(&ext);
+ node = ext2fs_rb_last(root);
+ continue;
+ } else
+ ext->count = new_max - ext->start + 1;
+ }
+}
+
+static errcode_t rb_resize_bmap(ext2fs_generic_bitmap_64 bmap,
+ __u64 new_end, __u64 new_real_end)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bmap->private;
+ bp->rcursor = NULL;
+ bp->wcursor = NULL;
+
+ rb_truncate(((new_end < bmap->end) ? new_end : bmap->end) - bmap->start,
+ &bp->root);
+
+ bmap->end = new_end;
+ bmap->real_end = new_real_end;
+
+ if (bmap->end < bmap->real_end)
+ rb_insert_extent(bmap->end + 1 - bmap->start,
+ bmap->real_end - bmap->end, bp);
+ return 0;
+
+}
+
+inline static int
+rb_test_bit(struct ext2fs_rb_private *bp, __u64 bit)
+{
+ struct bmap_rb_extent *rcursor, *next_ext = NULL;
+ struct rb_node *parent = NULL, *next;
+ struct rb_node **n = &bp->root.rb_node;
+ struct bmap_rb_extent *ext;
+
+ rcursor = bp->rcursor;
+ if (!rcursor)
+ goto search_tree;
+
+ if (bit >= rcursor->start && bit < rcursor->start + rcursor->count) {
+#ifdef ENABLE_BMAP_STATS_OPS
+ bp->test_hit++;
+#endif
+ return 1;
+ }
+
+ next_ext = bp->rcursor_next;
+ if (!next_ext) {
+ next = ext2fs_rb_next(&rcursor->node);
+ if (next)
+ next_ext = node_to_extent(next);
+ bp->rcursor_next = next_ext;
+ }
+ if (next_ext) {
+ if ((bit >= rcursor->start + rcursor->count) &&
+ (bit < next_ext->start)) {
+#ifdef BMAP_STATS_OPS
+ bp->test_hit++;
+#endif
+ return 0;
+ }
+ }
+ bp->rcursor = NULL;
+ bp->rcursor_next = NULL;
+
+ rcursor = bp->wcursor;
+ if (!rcursor)
+ goto search_tree;
+
+ if (bit >= rcursor->start && bit < rcursor->start + rcursor->count)
+ return 1;
+
+search_tree:
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (bit < ext->start)
+ n = &(*n)->rb_left;
+ else if (bit >= (ext->start + ext->count))
+ n = &(*n)->rb_right;
+ else {
+ bp->rcursor = ext;
+ bp->rcursor_next = NULL;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int rb_insert_extent(__u64 start, __u64 count,
+ struct ext2fs_rb_private *bp)
+{
+ struct rb_root *root = &bp->root;
+ struct rb_node *parent = NULL, **n = &root->rb_node;
+ struct rb_node *new_node, *node, *next;
+ struct bmap_rb_extent *new_ext;
+ struct bmap_rb_extent *ext;
+ int retval = 0;
+
+ if (count == 0)
+ return 0;
+
+ bp->rcursor_next = NULL;
+ ext = bp->wcursor;
+ if (ext) {
+ if (start >= ext->start &&
+ start <= (ext->start + ext->count)) {
+#ifdef ENABLE_BMAP_STATS_OPS
+ bp->mark_hit++;
+#endif
+ goto got_extent;
+ }
+ }
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start > (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else {
+got_extent:
+ if ((start + count) <= (ext->start + ext->count))
+ return 1;
+
+ if ((ext->start + ext->count) == start)
+ retval = 0;
+ else
+ retval = 1;
+
+ count += (start - ext->start);
+ start = ext->start;
+ new_ext = ext;
+ new_node = &ext->node;
+
+ goto skip_insert;
+ }
+ }
+
+ rb_get_new_extent(&new_ext, start, count);
+
+ new_node = &new_ext->node;
+ ext2fs_rb_link_node(new_node, parent, n);
+ ext2fs_rb_insert_color(new_node, root);
+ bp->wcursor = new_ext;
+
+ node = ext2fs_rb_prev(new_node);
+ if (node) {
+ ext = node_to_extent(node);
+ if ((ext->start + ext->count) == start) {
+ start = ext->start;
+ count += ext->count;
+ ext2fs_rb_erase(node, root);
+ rb_free_extent(bp, ext);
+ }
+ }
+
+skip_insert:
+ /* See if we can merge extent to the right */
+ for (node = ext2fs_rb_next(new_node); node != NULL; node = next) {
+ next = ext2fs_rb_next(node);
+ ext = node_to_extent(node);
+
+ if ((ext->start + ext->count) <= start)
+ continue;
+
+ /* No more merging */
+ if ((start + count) < ext->start)
+ break;
+
+ /* ext is embedded in new_ext interval */
+ if ((start + count) >= (ext->start + ext->count)) {
+ ext2fs_rb_erase(node, root);
+ rb_free_extent(bp, ext);
+ continue;
+ } else {
+ /* merge ext with new_ext */
+ count += ((ext->start + ext->count) -
+ (start + count));
+ ext2fs_rb_erase(node, root);
+ rb_free_extent(bp, ext);
+ break;
+ }
+ }
+
+ new_ext->start = start;
+ new_ext->count = count;
+
+ return retval;
+}
+
+static int rb_remove_extent(__u64 start, __u64 count,
+ struct ext2fs_rb_private *bp)
+{
+ struct rb_root *root = &bp->root;
+ struct rb_node *parent = NULL, **n = &root->rb_node;
+ struct rb_node *node;
+ struct bmap_rb_extent *ext;
+ __u64 new_start, new_count;
+ int retval = 0;
+
+ if (ext2fs_rb_empty_root(root))
+ return 0;
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ continue;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ continue;
+ }
+
+ if ((start > ext->start) &&
+ (start + count) < (ext->start + ext->count)) {
+ /* We have to split extent into two */
+ new_start = start + count;
+ new_count = (ext->start + ext->count) - new_start;
+
+ ext->count = start - ext->start;
+
+ rb_insert_extent(new_start, new_count, bp);
+ return 1;
+ }
+
+ if ((start + count) >= (ext->start + ext->count)) {
+ ext->count = start - ext->start;
+ retval = 1;
+ }
+
+ if (0 == ext->count) {
+ parent = ext2fs_rb_next(&ext->node);
+ ext2fs_rb_erase(&ext->node, root);
+ rb_free_extent(bp, ext);
+ break;
+ }
+
+ if (start == ext->start) {
+ ext->start += count;
+ ext->count -= count;
+ return 1;
+ }
+ }
+
+ /* See if we should delete or truncate extent on the right */
+ for (; parent != NULL; parent = node) {
+ node = ext2fs_rb_next(parent);
+ ext = node_to_extent(parent);
+ if ((ext->start + ext->count) <= start)
+ continue;
+
+ /* No more extents to be removed/truncated */
+ if ((start + count) < ext->start)
+ break;
+
+ /* The entire extent is within the region to be removed */
+ if ((start + count) >= (ext->start + ext->count)) {
+ ext2fs_rb_erase(parent, root);
+ rb_free_extent(bp, ext);
+ retval = 1;
+ continue;
+ } else {
+ /* modify the last extent in region to be removed */
+ ext->count -= ((start + count) - ext->start);
+ ext->start = start + count;
+ retval = 1;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static int rb_mark_bmap(ext2fs_generic_bitmap_64 bitmap, __u64 arg)
+{
+ struct ext2fs_rb_private *bp;
+ int retval;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ arg -= bitmap->start;
+
+ retval = rb_insert_extent(arg, 1, bp);
+ check_tree(&bp->root, __func__);
+ return retval;
+}
+
+static int rb_unmark_bmap(ext2fs_generic_bitmap_64 bitmap, __u64 arg)
+{
+ struct ext2fs_rb_private *bp;
+ int retval;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ arg -= bitmap->start;
+
+ retval = rb_remove_extent(arg, 1, bp);
+ check_tree(&bp->root, __func__);
+
+ return retval;
+}
+
+inline
+static int rb_test_bmap(ext2fs_generic_bitmap_64 bitmap, __u64 arg)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ arg -= bitmap->start;
+
+ return rb_test_bit(bp, arg);
+}
+
+static void rb_mark_bmap_extent(ext2fs_generic_bitmap_64 bitmap, __u64 arg,
+ unsigned int num)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ arg -= bitmap->start;
+
+ rb_insert_extent(arg, num, bp);
+ check_tree(&bp->root, __func__);
+}
+
+static void rb_unmark_bmap_extent(ext2fs_generic_bitmap_64 bitmap, __u64 arg,
+ unsigned int num)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ arg -= bitmap->start;
+
+ rb_remove_extent(arg, num, bp);
+ check_tree(&bp->root, __func__);
+}
+
+static int rb_test_clear_bmap_extent(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, unsigned int len)
+{
+ struct rb_node *parent = NULL, **n;
+ struct rb_node *node, *next;
+ struct ext2fs_rb_private *bp;
+ struct bmap_rb_extent *ext;
+ int retval = 1;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ n = &bp->root.rb_node;
+ start -= bitmap->start;
+
+ if (len == 0 || ext2fs_rb_empty_root(&bp->root))
+ return 1;
+
+ /*
+ * If we find nothing, we should examine whole extent, but
+ * when we find match, the extent is not clean, thus be return
+ * false.
+ */
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else {
+ /*
+ * We found extent int the tree -> extent is not
+ * clean
+ */
+ return 0;
+ }
+ }
+
+ node = parent;
+ while (node) {
+ next = ext2fs_rb_next(node);
+ ext = node_to_extent(node);
+ node = next;
+
+ if ((ext->start + ext->count) <= start)
+ continue;
+
+ /* No more merging */
+ if ((start + len) <= ext->start)
+ break;
+
+ retval = 0;
+ break;
+ }
+ return retval;
+}
+
+static errcode_t rb_set_bmap_range(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, size_t num, void *in)
+{
+ struct ext2fs_rb_private *bp;
+ unsigned char *cp = in;
+ size_t i;
+ int first_set = -1;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+
+ for (i = 0; i < num; i++) {
+ if ((i & 7) == 0) {
+ unsigned char c = cp[i/8];
+ if (c == 0xFF) {
+ if (first_set == -1)
+ first_set = i;
+ i += 7;
+ continue;
+ }
+ if ((c == 0x00) && (first_set == -1)) {
+ i += 7;
+ continue;
+ }
+ }
+ if (ext2fs_test_bit(i, in)) {
+ if (first_set == -1)
+ first_set = i;
+ continue;
+ }
+ if (first_set == -1)
+ continue;
+
+ rb_insert_extent(start + first_set - bitmap->start,
+ i - first_set, bp);
+ check_tree(&bp->root, __func__);
+ first_set = -1;
+ }
+ if (first_set != -1) {
+ rb_insert_extent(start + first_set - bitmap->start,
+ num - first_set, bp);
+ check_tree(&bp->root, __func__);
+ }
+
+ return 0;
+}
+
+static errcode_t rb_get_bmap_range(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, size_t num, void *out)
+{
+
+ struct rb_node *parent = NULL, *next, **n;
+ struct ext2fs_rb_private *bp;
+ struct bmap_rb_extent *ext;
+ __u64 count, pos;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ n = &bp->root.rb_node;
+ start -= bitmap->start;
+
+ if (ext2fs_rb_empty_root(&bp->root))
+ return 0;
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else
+ break;
+ }
+
+ memset(out, 0, (num + 7) >> 3);
+
+ for (; parent != NULL; parent = next) {
+ next = ext2fs_rb_next(parent);
+ ext = node_to_extent(parent);
+
+ pos = ext->start;
+ count = ext->count;
+ if (pos >= start + num)
+ break;
+ if (pos < start) {
+ if (pos + count < start)
+ continue;
+ count -= start - pos;
+ pos = start;
+ }
+ if (pos + count > start + num)
+ count = start + num - pos;
+
+ while (count > 0) {
+ if ((count >= 8) &&
+ ((pos - start) % 8) == 0) {
+ int nbytes = count >> 3;
+ int offset = (pos - start) >> 3;
+
+ memset(((char *) out) + offset, 0xFF, nbytes);
+ pos += nbytes << 3;
+ count -= nbytes << 3;
+ continue;
+ }
+ ext2fs_fast_set_bit64((pos - start), out);
+ pos++;
+ count--;
+ }
+ }
+ return 0;
+}
+
+static void rb_clear_bmap(ext2fs_generic_bitmap_64 bitmap)
+{
+ struct ext2fs_rb_private *bp;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+
+ rb_free_tree(&bp->root);
+ bp->rcursor = NULL;
+ bp->rcursor_next = NULL;
+ bp->wcursor = NULL;
+ check_tree(&bp->root, __func__);
+}
+
+static errcode_t rb_find_first_zero(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ struct rb_node *parent = NULL, **n;
+ struct ext2fs_rb_private *bp;
+ struct bmap_rb_extent *ext;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ n = &bp->root.rb_node;
+ start -= bitmap->start;
+ end -= bitmap->start;
+
+ if (start > end)
+ return EINVAL;
+
+ if (ext2fs_rb_empty_root(&bp->root))
+ return ENOENT;
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else if (ext->start + ext->count <= end) {
+ *out = ext->start + ext->count + bitmap->start;
+ return 0;
+ } else
+ return ENOENT;
+ }
+
+ *out = start + bitmap->start;
+ return 0;
+}
+
+static errcode_t rb_find_first_set(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ struct rb_node *parent = NULL, **n;
+ struct rb_node *node;
+ struct ext2fs_rb_private *bp;
+ struct bmap_rb_extent *ext;
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+ n = &bp->root.rb_node;
+ start -= bitmap->start;
+ end -= bitmap->start;
+
+ if (start > end)
+ return EINVAL;
+
+ if (ext2fs_rb_empty_root(&bp->root))
+ return ENOENT;
+
+ while (*n) {
+ parent = *n;
+ ext = node_to_extent(parent);
+ if (start < ext->start) {
+ n = &(*n)->rb_left;
+ } else if (start >= (ext->start + ext->count)) {
+ n = &(*n)->rb_right;
+ } else {
+ /* The start bit is set */
+ *out = start + bitmap->start;
+ return 0;
+ }
+ }
+
+ node = parent;
+ ext = node_to_extent(node);
+ if (ext->start < start) {
+ node = ext2fs_rb_next(node);
+ if (node == NULL)
+ return ENOENT;
+ ext = node_to_extent(node);
+ }
+ if (ext->start <= end) {
+ *out = ext->start + bitmap->start;
+ return 0;
+ }
+ return ENOENT;
+}
+
+#ifdef ENABLE_BMAP_STATS
+static void rb_print_stats(ext2fs_generic_bitmap_64 bitmap)
+{
+ struct ext2fs_rb_private *bp;
+ struct rb_node *node = NULL;
+ struct bmap_rb_extent *ext;
+ __u64 count = 0;
+ __u64 max_size = 0;
+ __u64 min_size = ULONG_MAX;
+ __u64 size = 0, avg_size = 0;
+ double eff;
+#ifdef ENABLE_BMAP_STATS_OPS
+ __u64 mark_all, test_all;
+ double m_hit = 0.0, t_hit = 0.0;
+#endif
+
+ bp = (struct ext2fs_rb_private *) bitmap->private;
+
+ for (node = ext2fs_rb_first(&bp->root); node != NULL;
+ node = ext2fs_rb_next(node)) {
+ ext = node_to_extent(node);
+ count++;
+ if (ext->count > max_size)
+ max_size = ext->count;
+ if (ext->count < min_size)
+ min_size = ext->count;
+ size += ext->count;
+ }
+
+ if (count)
+ avg_size = size / count;
+ if (min_size == ULONG_MAX)
+ min_size = 0;
+ eff = (double)((count * sizeof(struct bmap_rb_extent)) << 3) /
+ (bitmap->real_end - bitmap->start);
+#ifdef ENABLE_BMAP_STATS_OPS
+ mark_all = bitmap->stats.mark_count + bitmap->stats.mark_ext_count;
+ test_all = bitmap->stats.test_count + bitmap->stats.test_ext_count;
+ if (mark_all)
+ m_hit = ((double)bp->mark_hit / mark_all) * 100;
+ if (test_all)
+ t_hit = ((double)bp->test_hit / test_all) * 100;
+
+ fprintf(stderr, "%16llu cache hits on test (%.2f%%)\n"
+ "%16llu cache hits on mark (%.2f%%)\n",
+ bp->test_hit, t_hit, bp->mark_hit, m_hit);
+#endif
+ fprintf(stderr, "%16llu extents (%llu bytes)\n",
+ (unsigned long long) count, (unsigned long long)
+ ((count * sizeof(struct bmap_rb_extent)) +
+ sizeof(struct ext2fs_rb_private)));
+ fprintf(stderr, "%16llu bits minimum size\n",
+ (unsigned long long) min_size);
+ fprintf(stderr, "%16llu bits maximum size\n"
+ "%16llu bits average size\n",
+ (unsigned long long) max_size, (unsigned long long) avg_size);
+ fprintf(stderr, "%16llu bits set in bitmap (out of %llu)\n",
+ (unsigned long long) size,
+ (unsigned long long) bitmap->real_end - bitmap->start);
+ fprintf(stderr,
+ "%16.4lf memory / bitmap bit memory ratio (bitarray = 1)\n",
+ eff);
+}
+#else
+static void rb_print_stats(ext2fs_generic_bitmap_64 bitmap EXT2FS_ATTR((unused)))
+{
+}
+#endif
+
+struct ext2_bitmap_ops ext2fs_blkmap64_rbtree = {
+ .type = EXT2FS_BMAP64_RBTREE,
+ .new_bmap = rb_new_bmap,
+ .free_bmap = rb_free_bmap,
+ .copy_bmap = rb_copy_bmap,
+ .resize_bmap = rb_resize_bmap,
+ .mark_bmap = rb_mark_bmap,
+ .unmark_bmap = rb_unmark_bmap,
+ .test_bmap = rb_test_bmap,
+ .test_clear_bmap_extent = rb_test_clear_bmap_extent,
+ .mark_bmap_extent = rb_mark_bmap_extent,
+ .unmark_bmap_extent = rb_unmark_bmap_extent,
+ .set_bmap_range = rb_set_bmap_range,
+ .get_bmap_range = rb_get_bmap_range,
+ .clear_bmap = rb_clear_bmap,
+ .print_stats = rb_print_stats,
+ .find_first_zero = rb_find_first_zero,
+ .find_first_set = rb_find_first_set,
+};
diff --git a/lib/ext2fs/blknum.c b/lib/ext2fs/blknum.c
new file mode 100644
index 0000000..04839d8
--- /dev/null
+++ b/lib/ext2fs/blknum.c
@@ -0,0 +1,606 @@
+/*
+ * blknum.c --- Functions to handle blk64_t and high/low 64-bit block
+ * number.
+ *
+ * Copyright IBM Corporation, 2007
+ * Author Jose R. Santos <jrs@us.ibm.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#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 +
+ EXT2_GROUPS_TO_BLOCKS(fs->super, 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 number of blocks in a group
+ */
+int ext2fs_group_blocks_count(ext2_filsys fs, dgrp_t group)
+{
+ int num_blocks;
+
+ 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;
+
+ return num_blocks;
+}
+
+/*
+ * Return the inode data block count
+ */
+blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs,
+ struct ext2_inode *inode)
+{
+ return (inode->i_blocks |
+ (ext2fs_has_feature_huge_file(fs->super) ?
+ (__u64) inode->osd2.linux2.l_i_blocks_hi << 32 : 0)) -
+ (inode->i_file_acl ? EXT2_CLUSTER_SIZE(fs->super) >> 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 |
+ (ext2fs_has_feature_huge_file(fs->super) ?
+ (__u64)inode->osd2.linux2.l_i_blocks_hi << 32 : 0));
+}
+
+/*
+ * Return the inode i_blocks in stat (512 byte) units
+ */
+blk64_t ext2fs_get_stat_i_blocks(ext2_filsys fs,
+ struct ext2_inode *inode)
+{
+ blk64_t ret = inode->i_blocks;
+
+ if (ext2fs_has_feature_huge_file(fs->super)) {
+ ret += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
+ if (inode->i_flags & EXT4_HUGE_FILE_FL)
+ ret *= (fs->blocksize / 512);
+ }
+ return ret;
+}
+
+/*
+ * Return the fs block count
+ */
+blk64_t ext2fs_blocks_count(struct ext2_super_block *super)
+{
+ return super->s_blocks_count |
+ (ext2fs_has_feature_64bit(super) ?
+ (__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 (ext2fs_has_feature_64bit(super))
+ 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 |
+ (ext2fs_has_feature_64bit(super) ?
+ (__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 (ext2fs_has_feature_64bit(super))
+ 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 |
+ (ext2fs_has_feature_64bit(super) ?
+ (__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 (ext2fs_has_feature_64bit(super))
+ 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)
+{
+ struct ext2_group_desc *ret_gdp;
+ errcode_t retval;
+ static char *buf = 0;
+ static unsigned bufsize = 0;
+ blk64_t blk;
+ int desc_size = EXT2_DESC_SIZE(fs->super) & ~7;
+ int desc_per_blk = EXT2_DESC_PER_BLOCK(fs->super);
+
+ if (group > fs->group_desc_count)
+ return NULL;
+ if (gdp)
+ return (struct ext2_group_desc *)((char *)gdp +
+ group * desc_size);
+ /*
+ * If fs->group_desc wasn't read in when the file system was
+ * opened, then read it on demand here.
+ */
+ if (bufsize < fs->blocksize)
+ ext2fs_free_mem(&buf);
+ if (!buf) {
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return NULL;
+ bufsize = fs->blocksize;
+ }
+ blk = ext2fs_descriptor_block_loc2(fs, fs->super->s_first_data_block,
+ group / desc_per_blk);
+ retval = io_channel_read_blk(fs->io, blk, 1, buf);
+ if (retval)
+ return NULL;
+ ret_gdp = (struct ext2_group_desc *)
+ (buf + ((group % desc_per_blk) * desc_size));
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_group_desc2(fs, ret_gdp);
+#endif
+ return ret_gdp;
+}
+
+/* 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 checksum of a group
+ */
+__u32 ext2fs_block_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext4_group_desc *gdp;
+ __u32 csum;
+
+ gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+ csum = gdp->bg_block_bitmap_csum_lo;
+ if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ csum |= ((__u32)gdp->bg_block_bitmap_csum_hi << 16);
+ return csum;
+}
+
+/*
+ * 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ gdp->bg_block_bitmap_hi = (__u64) blk >> 32;
+}
+
+/*
+ * Return the inode bitmap checksum of a group
+ */
+__u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext4_group_desc *gdp;
+ __u32 csum;
+
+ gdp = ext4fs_group_desc(fs, fs->group_desc, group);
+ csum = gdp->bg_inode_bitmap_csum_lo;
+ if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ csum |= ((__u32)gdp->bg_inode_bitmap_csum_hi << 16);
+ return csum;
+}
+
+/*
+ * 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ 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 |
+ (ext2fs_has_feature_64bit(fs->super) ?
+ (__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 (ext2fs_has_feature_64bit(fs->super))
+ 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
+ */
+blk64_t ext2fs_file_acl_block(ext2_filsys fs, const struct ext2_inode *inode)
+{
+ blk64_t blk = inode->i_file_acl;
+
+ if (fs && ext2fs_has_feature_64bit(fs->super))
+ blk |= ((__u64) inode->osd2.linux2.l_i_file_acl_high) << 32;
+ return blk;
+}
+
+/*
+ * Set the acl block of a file
+ */
+void ext2fs_file_acl_block_set(ext2_filsys fs, struct ext2_inode *inode,
+ blk64_t blk)
+{
+ inode->i_file_acl = blk;
+ if (fs && ext2fs_has_feature_64bit(fs->super))
+ inode->osd2.linux2.l_i_file_acl_high = (__u64) blk >> 32;
+}
+
+/*
+ * Set the size of the inode
+ */
+errcode_t ext2fs_inode_size_set(ext2_filsys fs, struct ext2_inode *inode,
+ ext2_off64_t size)
+{
+ if (size < 0)
+ return EINVAL;
+
+ /* If writing a large inode, set the large_file or large_dir flag */
+ if (ext2fs_needs_large_file_feature(size)) {
+ int dirty_sb = 0;
+
+ if (LINUX_S_ISREG(inode->i_mode)) {
+ if (!ext2fs_has_feature_large_file(fs->super)) {
+ ext2fs_set_feature_large_file(fs->super);
+ dirty_sb = 1;
+ }
+ } else if (LINUX_S_ISDIR(inode->i_mode)) {
+ if (!ext2fs_has_feature_largedir(fs->super)) {
+ ext2fs_set_feature_largedir(fs->super);
+ dirty_sb = 1;
+ }
+ } else {
+ /* Only regular files get to be larger than 4GB */
+ return EXT2_ET_FILE_TOO_BIG;
+ }
+ if (dirty_sb) {
+ if (fs->super->s_rev_level == EXT2_GOOD_OLD_REV)
+ ext2fs_update_dynamic_rev(fs);
+ ext2fs_mark_super_dirty(fs);
+ }
+ }
+
+ inode->i_size = size & 0xffffffff;
+ inode->i_size_high = (size >> 32);
+
+ return 0;
+}
+
diff --git a/lib/ext2fs/block.c b/lib/ext2fs/block.c
new file mode 100644
index 0000000..06eed6e
--- /dev/null
+++ b/lib/ext2fs/block.c
@@ -0,0 +1,659 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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 += ((unsigned long long) 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;
+
+ /*
+ * An inode with inline data has no blocks over which to
+ * iterate, so return an error code indicating this fact.
+ */
+ if (inode.i_flags & EXT4_INLINE_DATA_FL)
+ return EXT2_ET_INLINE_DATA_CANT_ITERATE;
+
+ /*
+ * 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, next;
+ 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) {
+ if (op == EXT2_EXTENT_CURRENT)
+ ctx.errcode = 0;
+ else
+ 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_done);
+ 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;
+ }
+ if (ret & BLOCK_ABORT)
+ break;
+ }
+ continue;
+ }
+ uninit = 0;
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
+ uninit = EXT2_EXTENT_SET_BMAP_UNINIT;
+
+ /*
+ * Get the next extent before we start messing
+ * with the current extent
+ */
+ retval = ext2fs_extent_get(handle, op, &next);
+
+#if 0
+ printf("lblk %llu pblk %llu len %d blockcnt %llu\n",
+ extent.e_lblk, extent.e_pblk,
+ extent.e_len, blockcnt);
+#endif
+ if (extent.e_lblk + extent.e_len <= (blk64_t) blockcnt)
+ continue;
+ if (extent.e_lblk > (blk64_t) blockcnt)
+ blockcnt = extent.e_lblk;
+ j = blockcnt - extent.e_lblk;
+ blk += j;
+ 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_done);
+ if (r & BLOCK_CHANGED) {
+ ctx.errcode =
+ ext2fs_extent_set_bmap(handle,
+ (blk64_t) blockcnt,
+ new_blk, uninit);
+ if (ctx.errcode)
+ goto extent_done;
+ }
+ if (ret & BLOCK_ABORT)
+ goto extent_done;
+ }
+ if (retval == 0) {
+ extent = next;
+ op = EXT2_EXTENT_CURRENT;
+ }
+ }
+
+ extent_done:
+ ext2fs_extent_free(handle);
+ ret |= BLOCK_ERROR; /* ctx.errcode is always valid here */
+ 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/ext2fs/bmap.c b/lib/ext2fs/bmap.c
new file mode 100644
index 0000000..65c45c5
--- /dev/null
+++ b/lib/ext2fs/bmap.c
@@ -0,0 +1,499 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.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 ? ext2fs_le32_to_cpu(((blk_t *)block_buf)[nr - 1]) : ind;
+ 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 = 0;
+ 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 = 0;
+ 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;
+}
+
+static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ ext2_extent_handle_t handle,
+ char *block_buf, int bmap_flags, blk64_t block,
+ int *ret_flags, int *blocks_alloc,
+ blk64_t *phys_blk);
+
+static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ ext2_extent_handle_t handle,
+ blk64_t lblk, blk64_t *phys_blk)
+{
+ blk64_t base_block, pblock = 0;
+ int i;
+
+ if (!ext2fs_has_feature_bigalloc(fs->super))
+ return 0;
+
+ base_block = lblk & ~EXT2FS_CLUSTER_MASK(fs);
+ /*
+ * Except for the logical block (lblk) that was passed in, search all
+ * blocks in this logical cluster for a mapping to a physical cluster.
+ * If any such map exists, calculate the physical block that maps to
+ * the logical block and return that.
+ *
+ * The old code wouldn't even look if (block % cluster_ratio) == 0;
+ * this is incorrect if we're allocating blocks in reverse order.
+ */
+ for (i = 0; i < EXT2FS_CLUSTER_RATIO(fs); i++) {
+ if (base_block + i == lblk)
+ continue;
+ extent_bmap(fs, ino, inode, handle, 0, 0,
+ base_block + i, 0, 0, &pblock);
+ if (pblock)
+ break;
+ }
+ if (pblock == 0)
+ return 0;
+ *phys_blk = pblock - i + (lblk - base_block);
+ return 0;
+}
+
+/* Try to map a logical block to an already-allocated physical cluster. */
+errcode_t ext2fs_map_cluster_block(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t lblk,
+ blk64_t *pblk)
+{
+ ext2_extent_handle_t handle;
+ errcode_t retval;
+
+ /* Need bigalloc and extents to be enabled */
+ *pblk = 0;
+ if (!ext2fs_has_feature_bigalloc(fs->super) ||
+ !(inode->i_flags & EXT4_EXTENTS_FL))
+ return 0;
+
+ retval = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (retval)
+ goto out;
+
+ retval = implied_cluster_alloc(fs, ino, inode, handle, lblk, pblk);
+ if (retval)
+ goto out2;
+
+out2:
+ ext2fs_extent_free(handle);
+out:
+ return retval;
+}
+
+static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ ext2_extent_handle_t handle,
+ char *block_buf, int bmap_flags, blk64_t block,
+ int *ret_flags, int *blocks_alloc,
+ blk64_t *phys_blk)
+{
+ struct blk_alloc_ctx alloc_ctx;
+ struct ext2fs_extent extent;
+ unsigned int offset;
+ errcode_t retval = 0;
+ blk64_t blk64 = 0;
+ int alloc = 0;
+ int set_flags;
+
+ set_flags = bmap_flags & BMAP_UNINIT ? EXT2_EXTENT_SET_BMAP_UNINIT : 0;
+
+ if (bmap_flags & BMAP_SET) {
+ retval = ext2fs_extent_set_bmap(handle, block,
+ *phys_blk, set_flags);
+ return retval;
+ }
+ 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) {
+ extent.e_lblk = block;
+ goto got_block;
+ }
+ return retval;
+ }
+ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
+ if (retval)
+ return retval;
+ 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)) {
+ implied_cluster_alloc(fs, ino, inode, handle, block, &blk64);
+ if (blk64)
+ goto set_extent;
+ retval = extent_bmap(fs, ino, inode, handle, block_buf,
+ 0, block-1, 0, blocks_alloc, &blk64);
+ if (retval)
+ blk64 = ext2fs_find_inode_goal(fs, ino, inode, block);
+ alloc_ctx.ino = ino;
+ alloc_ctx.inode = inode;
+ alloc_ctx.lblk = extent.e_lblk;
+ alloc_ctx.flags = BLOCK_ALLOC_DATA;
+ retval = ext2fs_alloc_block3(fs, blk64, block_buf, &blk64,
+ &alloc_ctx);
+ if (retval)
+ return retval;
+ blk64 &= ~EXT2FS_CLUSTER_MASK(fs);
+ blk64 += EXT2FS_CLUSTER_MASK(fs) & block;
+ alloc++;
+ set_extent:
+ retval = ext2fs_extent_set_bmap(handle, block,
+ blk64, set_flags);
+ if (retval) {
+ ext2fs_block_alloc_stats2(fs, blk64, -1);
+ return retval;
+ }
+ /* Update inode after setting extent */
+ retval = ext2fs_read_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+ *blocks_alloc += alloc;
+ *phys_blk = blk64;
+ }
+ return 0;
+}
+
+int ext2fs_file_block_offset_too_big(ext2_filsys fs,
+ struct ext2_inode *inode,
+ blk64_t offset)
+{
+ blk64_t addr_per_block, max_map_block;
+
+ /* Kernel seems to cut us off at 4294967294 blocks */
+ if (offset >= (1ULL << 32) - 1)
+ return 1;
+
+ if (inode->i_flags & EXT4_EXTENTS_FL)
+ return 0;
+
+ addr_per_block = fs->blocksize >> 2;
+ max_map_block = addr_per_block;
+ max_map_block += addr_per_block * addr_per_block;
+ max_map_block += addr_per_block * addr_per_block * addr_per_block;
+ max_map_block += 12;
+
+ return offset >= max_map_block;
+}
+
+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;
+ blk64_t b64;
+ char *buf = 0;
+ errcode_t retval = 0;
+ int blocks_alloc = 0, inode_dirty = 0;
+ struct blk_alloc_ctx alloc_ctx = {
+ .ino = ino,
+ .inode = inode,
+ .lblk = 0,
+ .flags = BLOCK_ALLOC_DATA,
+ };
+
+ 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 (ext2fs_file_block_offset_too_big(fs, inode, block))
+ return EXT2_ET_FILE_TOO_BIG;
+
+ /*
+ * If an inode has inline data, that means that it doesn't have
+ * any blocks and we shouldn't map any blocks for it.
+ */
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return EXT2_ET_INLINE_DATA_NO_BLOCK;
+
+ if (!block_buf) {
+ retval = ext2fs_get_array(2, fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ block_buf = buf;
+ }
+
+ if (inode->i_flags & EXT4_EXTENTS_FL) {
+ retval = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (retval)
+ goto done;
+ retval = extent_bmap(fs, ino, inode, handle, block_buf,
+ bmap_flags, block, ret_flags,
+ &blocks_alloc, phys_blk);
+ goto done;
+ }
+
+ 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) :
+ ext2fs_find_inode_goal(fs, ino, inode, block);
+
+ if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
+ b64 = b;
+ retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64,
+ &alloc_ctx);
+ b = b64;
+ 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);
+ b64 = b;
+ retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64,
+ &alloc_ctx);
+ b = b64;
+ 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);
+ b64 = b;
+ retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64,
+ &alloc_ctx);
+ b = b64;
+ 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);
+ b64 = b;
+ retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64,
+ &alloc_ctx);
+ b = b64;
+ 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 (*phys_blk && retval == 0 && (bmap_flags & BMAP_ZERO))
+ retval = ext2fs_zero_blocks2(fs, *phys_blk, 1, NULL, NULL);
+ 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/ext2fs/bmap64.h b/lib/ext2fs/bmap64.h
new file mode 100644
index 0000000..de33454
--- /dev/null
+++ b/lib/ext2fs/bmap64.h
@@ -0,0 +1,106 @@
+/*
+ * 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 ext2_bmap_statistics {
+ int type;
+ struct timeval created;
+
+#ifdef ENABLE_BMAP_STATS_OPS
+ unsigned long copy_count;
+ unsigned long resize_count;
+ unsigned long mark_count;
+ unsigned long unmark_count;
+ unsigned long test_count;
+ unsigned long mark_ext_count;
+ unsigned long unmark_ext_count;
+ unsigned long test_ext_count;
+ unsigned long set_range_count;
+ unsigned long get_range_count;
+ unsigned long clear_count;
+
+ blk64_t last_marked;
+ blk64_t last_tested;
+ blk64_t mark_back;
+ blk64_t test_back;
+
+ unsigned long mark_seq;
+ unsigned long test_seq;
+#endif /* ENABLE_BMAP_STATS_OPS */
+};
+
+
+struct ext2fs_struct_generic_bitmap_64 {
+ errcode_t magic;
+ ext2_filsys fs;
+ struct ext2_bitmap_ops *bitmap_ops;
+ int flags;
+ __u64 start, end;
+ __u64 real_end;
+ int cluster_bits;
+ char *description;
+ void *private;
+ errcode_t base_error_code;
+#ifdef ENABLE_BMAP_STATS
+ struct ext2_bmap_statistics stats;
+#endif
+};
+
+typedef struct ext2fs_struct_generic_bitmap_64 *ext2fs_generic_bitmap_64;
+
+#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_64 bmap);
+ void (*free_bmap)(ext2fs_generic_bitmap_64 bitmap);
+ errcode_t (*copy_bmap)(ext2fs_generic_bitmap_64 src,
+ ext2fs_generic_bitmap_64 dest);
+ errcode_t (*resize_bmap)(ext2fs_generic_bitmap_64 bitmap,
+ __u64 new_end,
+ __u64 new_real_end);
+ /* bit set/test operators */
+ int (*mark_bmap)(ext2fs_generic_bitmap_64 bitmap, __u64 arg);
+ int (*unmark_bmap)(ext2fs_generic_bitmap_64 bitmap, __u64 arg);
+ int (*test_bmap)(ext2fs_generic_bitmap_64 bitmap, __u64 arg);
+ void (*mark_bmap_extent)(ext2fs_generic_bitmap_64 bitmap, __u64 arg,
+ unsigned int num);
+ void (*unmark_bmap_extent)(ext2fs_generic_bitmap_64 bitmap, __u64 arg,
+ unsigned int num);
+ int (*test_clear_bmap_extent)(ext2fs_generic_bitmap_64 bitmap,
+ __u64 arg, unsigned int num);
+ errcode_t (*set_bmap_range)(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, size_t num, void *in);
+ errcode_t (*get_bmap_range)(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, size_t num, void *out);
+ void (*clear_bmap)(ext2fs_generic_bitmap_64 bitmap);
+ void (*print_stats)(ext2fs_generic_bitmap_64);
+
+ /* Find the first zero bit between start and end, inclusive.
+ * May be NULL, in which case a generic function is used. */
+ errcode_t (*find_first_zero)(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, __u64 end, __u64 *out);
+ /* Find the first set bit between start and end, inclusive.
+ * May be NULL, in which case a generic function is used. */
+ errcode_t (*find_first_set)(ext2fs_generic_bitmap_64 bitmap,
+ __u64 start, __u64 end, __u64 *out);
+};
+
+extern struct ext2_bitmap_ops ext2fs_blkmap64_bitarray;
+extern struct ext2_bitmap_ops ext2fs_blkmap64_rbtree;
diff --git a/lib/ext2fs/bmove.c b/lib/ext2fs/bmove.c
new file mode 100644
index 0000000..e2ea405
--- /dev/null
+++ b/lib/ext2fs/bmove.c
@@ -0,0 +1,167 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#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_blocks2(fs, &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/ext2fs/brel.h b/lib/ext2fs/brel.h
new file mode 100644
index 0000000..9fdddd4
--- /dev/null
+++ b/lib/ext2fs/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 {
+ blk64_t new;
+ __s16 offset;
+ __u16 flags;
+ union {
+ blk64_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;
+ blk64_t current;
+ void *priv_data;
+
+ /*
+ * Add a block relocation entry.
+ */
+ errcode_t (*put)(ext2_brel brel, blk64_t old,
+ struct ext2_block_relocate_entry *ent);
+
+ /*
+ * Get a block relocation entry.
+ */
+ errcode_t (*get)(ext2_brel brel, blk64_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, blk64_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, blk64_t old, blk_t new);
+
+ /*
+ * Remove a block relocation entry.
+ */
+ errcode_t (*delete)(ext2_brel brel, blk64_t old);
+
+
+ /*
+ * Free the block relocation table.
+ */
+ errcode_t (*free)(ext2_brel brel);
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk64_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/ext2fs/brel_ma.c b/lib/ext2fs/brel_ma.c
new file mode 100644
index 0000000..a12afae
--- /dev/null
+++ b/lib/ext2fs/brel_ma.c
@@ -0,0 +1,199 @@
+/*
+ * 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 "config.h"
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "brel.h"
+
+static errcode_t bma_put(ext2_brel brel, blk64_t old,
+ struct ext2_block_relocate_entry *ent);
+static errcode_t bma_get(ext2_brel brel, blk64_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, blk64_t *old,
+ struct ext2_block_relocate_entry *ent);
+static errcode_t bma_move(ext2_brel brel, blk64_t old, blk64_t new);
+static errcode_t bma_delete(ext2_brel brel, blk64_t old);
+static errcode_t bma_free(ext2_brel brel);
+
+struct brel_ma {
+ __u32 magic;
+ blk64_t max_block;
+ struct ext2_block_relocate_entry *entries;
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk64_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, blk64_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, blk64_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, blk64_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, blk64_t old, blk64_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, blk64_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/ext2fs/check_desc.c b/lib/ext2fs/check_desc.c
new file mode 100644
index 0000000..3e3fa94
--- /dev/null
+++ b/lib/ext2fs/check_desc.c
@@ -0,0 +1,104 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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;
+ unsigned int j;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (EXT2_DESC_SIZE(fs->super) & (EXT2_DESC_SIZE(fs->super) - 1))
+ return EXT2_ET_BAD_DESC_SIZE;
+
+ retval = ext2fs_allocate_subcluster_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 (!ext2fs_has_feature_flex_bg(fs->super)) {
+ first_block = ext2fs_group_first_block2(fs, i);
+ last_block = ext2fs_group_last_block2(fs, i);
+ }
+
+ /*
+ * 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/ext2fs/closefs.c b/lib/ext2fs/closefs.c
new file mode 100644
index 0000000..69cbdd8
--- /dev/null
+++ b/lib/ext2fs/closefs.c
@@ -0,0 +1,521 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(unsigned int a, unsigned int b)
+{
+ while (1) {
+ if (a < b)
+ return 0;
+ if (a == b)
+ return 1;
+ if (a % b)
+ return 0;
+ a = a / b;
+ }
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, dgrp_t group)
+{
+ if (group == 0)
+ return 1;
+ if (ext2fs_has_feature_sparse_super2(fs->super)) {
+ if (group == fs->super->s_backup_bgs[0] ||
+ group == fs->super->s_backup_bgs[1])
+ return 1;
+ return 0;
+ }
+ if ((group <= 1) || !ext2fs_has_feature_sparse_super(fs->super))
+ return 1;
+ if (!(group & 1))
+ return 0;
+ if (test_root(group, 3) || (test_root(group, 5)) ||
+ test_root(group, 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 (group_block == 0 && fs->blocksize == 1024)
+ group_block = 1; /* Deal with 1024 blocksize && bigalloc */
+
+ if (ext2fs_has_feature_meta_bg(fs->super))
+ 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 (!ext2fs_has_feature_meta_bg(fs->super) ||
+ (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);
+
+ numblocks = ext2fs_group_blocks_count(fs, 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,
+ blk64_t group_block,
+ struct ext2_super_block *super_shadow)
+{
+ errcode_t retval;
+ dgrp_t sgrp = group;
+
+ if (sgrp > ((1 << 16) - 1))
+ sgrp = (1 << 16) - 1;
+
+ super_shadow->s_block_group_nr = ext2fs_cpu_to_le16(sgrp);
+
+ retval = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;
+
+ return io_channel_write_blk64(fs->io, group_block, -SUPERBLOCK_SIZE,
+ super_shadow);
+}
+
+errcode_t ext2fs_flush(ext2_filsys fs)
+{
+ return ext2fs_flush2(fs, 0);
+}
+
+errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
+{
+ dgrp_t i;
+ errcode_t retval;
+ unsigned long fs_state;
+ __u32 feature_incompat;
+ struct ext2_super_block *super_shadow = 0;
+ struct opaque_ext2_group_desc *group_shadow = 0;
+#ifdef WORDS_BIGENDIAN
+ struct ext2_group_desc *gdp;
+ dgrp_t j;
+#endif
+ char *group_ptr;
+ blk64_t old_desc_blocks;
+ struct ext2fs_numeric_progress_struct progress;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if ((fs->flags & EXT2_FLAG_SUPER_ONLY) == 0 &&
+ !ext2fs_has_feature_journal_dev(fs->super) &&
+ fs->group_desc == NULL)
+ return EXT2_ET_NO_GDESC;
+
+ 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;
+
+ /*
+ * 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.
+ *
+ * Bitmap checksums live in the group descriptor, so the
+ * bitmaps need to be written before the descriptors.
+ */
+ if (fs->write_bitmaps) {
+ retval = fs->write_bitmaps(fs);
+ if (retval)
+ goto errout;
+ }
+
+ /*
+ * 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;
+ ext2fs_clear_feature_journal_needs_recovery(fs->super);
+
+ /* Byte swap the superblock and the group descriptors if necessary */
+#ifdef WORDS_BIGENDIAN
+ retval = EXT2_ET_NO_MEMORY;
+ retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
+ if (retval)
+ goto errout;
+ memcpy(super_shadow, fs->super, sizeof(struct ext2_super_block));
+ ext2fs_swap_super(super_shadow);
+
+ if (((fs->flags & EXT2_FLAG_SUPER_ONLY) == 0) &&
+ !ext2fs_has_feature_journal_dev(fs->super)) {
+ 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);
+
+ for (j = 0; j < fs->group_desc_count; j++) {
+ gdp = ext2fs_group_desc(fs, group_shadow, j);
+ ext2fs_swap_group_desc2(fs, gdp);
+ }
+ }
+#else
+ super_shadow = fs->super;
+ group_shadow = fs->group_desc;
+#endif
+
+ /*
+ * If this is an external journal device, don't write out the
+ * block group descriptors or any of the backup superblocks
+ */
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ goto write_primary_superblock_only;
+
+ /*
+ * Write out the master group descriptors, and the backup
+ * superblocks and group descriptors.
+ */
+ group_ptr = (char *) group_shadow;
+ if (ext2fs_has_feature_meta_bg(fs->super)) {
+ old_desc_blocks = fs->super->s_first_meta_bg;
+ if (old_desc_blocks > fs->desc_blocks)
+ old_desc_blocks = fs->desc_blocks;
+ } else
+ old_desc_blocks = fs->desc_blocks;
+
+ if (fs->progress_ops && fs->progress_ops->init)
+ (fs->progress_ops->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;
+
+ if (fs->progress_ops && fs->progress_ops->update)
+ (fs->progress_ops->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;
+ }
+ }
+
+ if (fs->progress_ops && fs->progress_ops->close)
+ (fs->progress_ops->close)(fs, &progress, NULL);
+
+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 = ext2fs_superblock_csum_set(fs, super_shadow);
+ if (retval)
+ return retval;
+
+ if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) {
+ retval = io_channel_flush(fs->io);
+ if (retval)
+ goto errout;
+ }
+ retval = write_primary_superblock(fs, super_shadow);
+ if (retval)
+ goto errout;
+
+ fs->flags &= ~EXT2_FLAG_DIRTY;
+
+ if (!(flags & EXT2_FLAG_FLUSH_NO_SYNC)) {
+ retval = io_channel_flush(fs->io);
+ if (retval)
+ goto errout;
+ }
+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_free(ext2_filsys *fs_ptr)
+{
+ errcode_t ret;
+ ext2_filsys fs = *fs_ptr;
+
+ ret = ext2fs_close2(fs, 0);
+ if (ret)
+ ext2fs_free(fs);
+ *fs_ptr = NULL;
+ return ret;
+}
+
+errcode_t ext2fs_close(ext2_filsys fs)
+{
+ return ext2fs_close2(fs, 0);
+}
+
+errcode_t ext2fs_close2(ext2_filsys fs, int flags)
+{
+ 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_flush2(fs, flags);
+ if (retval)
+ return retval;
+ }
+
+ retval = ext2fs_mmp_stop(fs);
+ if (retval)
+ return retval;
+
+ ext2fs_free(fs);
+ return 0;
+}
+
diff --git a/lib/ext2fs/compiler.h b/lib/ext2fs/compiler.h
new file mode 100644
index 0000000..3bb3521
--- /dev/null
+++ b/lib/ext2fs/compiler.h
@@ -0,0 +1,26 @@
+#ifndef _EXT2FS_COMPILER_H
+#define _EXT2FS_COMPILER_H
+
+#include <stddef.h>
+
+#ifdef __GNUC__
+
+#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
+
+#define container_of(ptr, type, member) ({ \
+ __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#else
+#define container_of(ptr, type, member) \
+ ((type *)((char *)(ptr) - offsetof(type, member)))
+#endif
+
+
+#endif /* _EXT2FS_COMPILER_H */
diff --git a/lib/ext2fs/crc16.c b/lib/ext2fs/crc16.c
new file mode 100644
index 0000000..2fdeb24
--- /dev/null
+++ b/lib/ext2fs/crc16.c
@@ -0,0 +1,74 @@
+/*
+ * crc16.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <ext2fs/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/ext2fs/crc16.h b/lib/ext2fs/crc16.h
new file mode 100644
index 0000000..322e68d
--- /dev/null
+++ b/lib/ext2fs/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 <bgardner@wabtec.com>
+ *
+ * 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/ext2fs/crc32c.c b/lib/ext2fs/crc32c.c
new file mode 100644
index 0000000..df5cf9b
--- /dev/null
+++ b/lib/ext2fs/crc32c.c
@@ -0,0 +1,938 @@
+/*
+ * crc32c.c
+ *
+ * August 26, 2011 Darrick J. Wong <djwong at us.ibm.com>
+ * Reuse Bob Pearson's slice-by-8 implementation for e2fsprogs.
+ *
+ * July 20, 2011 Bob Pearson <rpearson at systemfabricworks.com>
+ * added slice by 8 algorithm to the existing conventional and
+ * slice by 4 algorithms.
+ *
+ * Oct 15, 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * Nicer crc32 functions/docs submitted by linux@horizon.com. Thanks!
+ * Code was from the public domain, copyright abandoned. Code was
+ * subsequently included in the kernel, thus was re-licensed under the
+ * GNU GPL v2.
+ *
+ * Oct 12, 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * Same crc32 function was used in 5 other places in the kernel.
+ * I made one version, and deleted the others.
+ * There are various incantations of crc32(). Some use a seed of 0 or ~0.
+ * Some xor at the end with ~0. The generic crc32() function takes
+ * seed as an argument, and doesn't xor at the end. Then individual
+ * users can do whatever they need.
+ * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0.
+ * fs/jffs2 uses seed 0, doesn't xor with ~0.
+ * fs/partitions/efi.c uses seed ~0, xor's with ~0.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+#include "config.h"
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#define min(x, y) ((x) > (y) ? (y) : (x))
+#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
+#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof__(x))(a) - 1)
+#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
+#define PTR_ALIGN(p, a) ((__typeof__(p))ALIGN((unsigned long)(p), (a)))
+#include "crc32c_defs.h"
+
+#include "ext2fs.h"
+#ifdef WORDS_BIGENDIAN
+#define __constant_cpu_to_le32(x) ___constant_swab32((x))
+#define __constant_cpu_to_be32(x) (x)
+#define __be32_to_cpu(x) (x)
+#define __cpu_to_be32(x) (x)
+#define __cpu_to_le32(x) (ext2fs_cpu_to_le32((x)))
+#define __le32_to_cpu(x) (ext2fs_le32_to_cpu((x)))
+#else
+#define __constant_cpu_to_le32(x) (x)
+#define __constant_cpu_to_be32(x) ___constant_swab32((x))
+#define __be32_to_cpu(x) (ext2fs_be32_to_cpu((x)))
+#define __cpu_to_be32(x) (ext2fs_cpu_to_be32((x)))
+#define __cpu_to_le32(x) (x)
+#define __le32_to_cpu(x) (x)
+#endif
+
+#if CRC_LE_BITS > 8
+# define tole(x) __constant_cpu_to_le32(x)
+#else
+# define tole(x) (x)
+#endif
+
+#if CRC_BE_BITS > 8
+# define tobe(x) __constant_cpu_to_be32(x)
+#else
+# define tobe(x) (x)
+#endif
+
+#include "crc32c_table.h"
+
+#if CRC_LE_BITS > 8 || CRC_BE_BITS > 8
+
+#if CRC_LE_BITS < 64 && CRC_BE_BITS < 64
+#define CRC_INLINE inline
+#else
+#define CRC_INLINE
+#endif
+
+/* implements slicing-by-4 or slicing-by-8 algorithm */
+static CRC_INLINE uint32_t
+crc32_body(uint32_t crc, unsigned char const *buf, size_t len,
+ const uint32_t (*tab)[256])
+{
+# ifndef WORDS_BIGENDIAN
+# define DO_CRC(x) (crc = t0[(crc ^ (x)) & 255] ^ (crc >> 8))
+# define DO_CRC4 (t3[(q) & 255] ^ t2[(q >> 8) & 255] ^ \
+ t1[(q >> 16) & 255] ^ t0[(q >> 24) & 255])
+# define DO_CRC8 (t7[(q) & 255] ^ t6[(q >> 8) & 255] ^ \
+ t5[(q >> 16) & 255] ^ t4[(q >> 24) & 255])
+# else
+# define DO_CRC(x) (crc = t0[((crc >> 24) ^ (x)) & 255] ^ (crc << 8))
+# define DO_CRC4 (t0[(q) & 255] ^ t1[(q >> 8) & 255] ^ \
+ t2[(q >> 16) & 255] ^ t3[(q >> 24) & 255])
+# define DO_CRC8 (t4[(q) & 255] ^ t5[(q >> 8) & 255] ^ \
+ t6[(q >> 16) & 255] ^ t7[(q >> 24) & 255])
+# endif
+ const uint32_t *b;
+ size_t rem_len;
+ const uint32_t *t0 = tab[0], *t1 = tab[1], *t2 = tab[2], *t3 = tab[3];
+ const uint32_t *t4 = tab[4], *t5 = tab[5], *t6 = tab[6], *t7 = tab[7];
+ uint32_t q;
+
+ /* Align it */
+ if (unlikely((uintptr_t)buf & 3 && len)) {
+ do {
+ DO_CRC(*buf++);
+ } while ((--len) && ((uintptr_t)buf)&3);
+ }
+
+# if CRC_LE_BITS == 32
+ rem_len = len & 3;
+ len = len >> 2;
+# else
+ rem_len = len & 7;
+ len = len >> 3;
+# endif
+
+ b = (const uint32_t *)buf;
+ for (--b; len; --len) {
+ q = crc ^ *++b; /* use pre increment for speed */
+# if CRC_LE_BITS == 32
+ crc = DO_CRC4;
+# else
+ crc = DO_CRC8;
+ q = *++b;
+ crc ^= DO_CRC4;
+# endif
+ }
+ len = rem_len;
+ /* And the last few bytes */
+ if (len) {
+ const uint8_t *p = (const uint8_t *)(b + 1) - 1;
+ do {
+ DO_CRC(*++p); /* use pre increment for speed */
+ } while (--len);
+ }
+ return crc;
+#undef DO_CRC
+#undef DO_CRC4
+#undef DO_CRC8
+}
+#endif
+
+/**
+ * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
+ * @p: pointer to buffer over which CRC is run
+ * @len: length of buffer @p
+ */
+static inline uint32_t crc32_le_generic(uint32_t crc, unsigned char const *p,
+ size_t len, const uint32_t (*tab)[256],
+ uint32_t polynomial EXT2FS_ATTR((unused)))
+{
+#if CRC_LE_BITS == 1
+ int i;
+ while (len--) {
+ crc ^= *p++;
+ for (i = 0; i < 8; i++)
+ crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0);
+ }
+# elif CRC_LE_BITS == 2
+ while (len--) {
+ crc ^= *p++;
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ crc = (crc >> 2) ^ tab[0][crc & 3];
+ }
+# elif CRC_LE_BITS == 4
+ while (len--) {
+ crc ^= *p++;
+ crc = (crc >> 4) ^ tab[0][crc & 15];
+ crc = (crc >> 4) ^ tab[0][crc & 15];
+ }
+# elif CRC_LE_BITS == 8
+ /* aka Sarwate algorithm */
+ while (len--) {
+ crc ^= *p++;
+ crc = (crc >> 8) ^ tab[0][crc & 255];
+ }
+# else
+ crc = __cpu_to_le32(crc);
+ crc = crc32_body(crc, p, len, tab);
+ crc = __le32_to_cpu(crc);
+#endif
+ return crc;
+}
+
+uint32_t ext2fs_crc32c_le(uint32_t crc, unsigned char const *p, size_t len)
+{
+ return crc32_le_generic(crc, p, len, crc32ctable_le, CRC32C_POLY_LE);
+}
+
+/**
+ * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32
+ * @crc: seed value for computation. ~0 for Ethernet, sometimes 0 for
+ * other uses, or the previous crc32 value if computing incrementally.
+ * @p: pointer to buffer over which CRC is run
+ * @len: length of buffer @p
+ */
+static inline uint32_t crc32_be_generic(uint32_t crc, unsigned char const *p,
+ size_t len, const uint32_t (*tab)[256],
+ uint32_t polynomial EXT2FS_ATTR((unused)))
+{
+#if CRC_BE_BITS == 1
+ int i;
+ while (len--) {
+ crc ^= *p++ << 24;
+ for (i = 0; i < 8; i++)
+ crc =
+ (crc << 1) ^ ((crc & 0x80000000) ? polynomial :
+ 0);
+ }
+# elif CRC_BE_BITS == 2
+ while (len--) {
+ crc ^= *p++ << 24;
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ crc = (crc << 2) ^ tab[0][crc >> 30];
+ }
+# elif CRC_BE_BITS == 4
+ while (len--) {
+ crc ^= *p++ << 24;
+ crc = (crc << 4) ^ tab[0][crc >> 28];
+ crc = (crc << 4) ^ tab[0][crc >> 28];
+ }
+# elif CRC_BE_BITS == 8
+ while (len--) {
+ crc ^= *p++ << 24;
+ crc = (crc << 8) ^ tab[0][crc >> 24];
+ }
+# else
+ crc = __cpu_to_be32(crc);
+ crc = crc32_body(crc, p, len, tab);
+ crc = __be32_to_cpu(crc);
+# endif
+ return crc;
+}
+
+uint32_t ext2fs_crc32_be(uint32_t crc, unsigned char const *p, size_t len)
+{
+ return crc32_be_generic(crc, p, len, crc32table_be, CRCPOLY_BE);
+}
+
+#ifdef UNITTEST
+static uint8_t test_buf[] = {
+ 0xd9, 0xd7, 0x6a, 0x13, 0x3a, 0xb1, 0x05, 0x48,
+ 0xda, 0xad, 0x14, 0xbd, 0x03, 0x3a, 0x58, 0x5e,
+ 0x6e, 0xd1, 0x56, 0xc9, 0x2e, 0xc4, 0xcb, 0x6b,
+ 0xe8, 0x77, 0x52, 0x37, 0x4e, 0x0f, 0x55, 0xd2,
+ 0x12, 0x65, 0x90, 0xc2, 0x41, 0x49, 0x81, 0x01,
+ 0xf5, 0x01, 0xeb, 0x2d, 0x78, 0x74, 0x23, 0x5d,
+ 0x84, 0x5c, 0x81, 0x92, 0x21, 0xe9, 0x8d, 0x1d,
+ 0x89, 0xf2, 0x4a, 0xac, 0xdd, 0xf9, 0xaf, 0xee,
+ 0x44, 0xe7, 0x6e, 0xed, 0xfb, 0xd8, 0x89, 0x0e,
+ 0x96, 0x62, 0xcd, 0xa4, 0x4b, 0xa9, 0xe5, 0x45,
+ 0xb1, 0x29, 0x9b, 0x0f, 0xfc, 0xbd, 0x83, 0xab,
+ 0xa8, 0x54, 0x96, 0x44, 0x2c, 0x7f, 0xbb, 0xe7,
+ 0x52, 0x29, 0x08, 0xee, 0x14, 0xc5, 0xc2, 0xec,
+ 0x5a, 0xeb, 0x40, 0x40, 0xea, 0xd1, 0x3d, 0x15,
+ 0x73, 0xaa, 0x8c, 0x73, 0xfc, 0xf2, 0x2b, 0x49,
+ 0x0b, 0x13, 0x96, 0xd9, 0x8e, 0x4b, 0xbc, 0xe0,
+ 0xf4, 0xd2, 0xe0, 0x2e, 0x7a, 0xf0, 0x5d, 0x1f,
+ 0xd2, 0x92, 0x97, 0xe0, 0xaa, 0x59, 0xab, 0xc9,
+ 0x5c, 0xa6, 0x51, 0x1a, 0xe3, 0xd6, 0x06, 0xb9,
+ 0xae, 0xb8, 0x76, 0x36, 0x79, 0x37, 0x52, 0xf6,
+ 0x34, 0xaf, 0x27, 0x19, 0xe1, 0xc0, 0x2b, 0xdd,
+ 0x01, 0x15, 0xcd, 0xce, 0x44, 0xf6, 0x4c, 0x18,
+ 0x92, 0x69, 0xbe, 0x8a, 0x76, 0x23, 0x52, 0x13,
+ 0x3f, 0xf9, 0xe0, 0xf5, 0x06, 0x28, 0x7c, 0xc7,
+ 0xf3, 0x42, 0x0f, 0xdd, 0x40, 0x33, 0xf7, 0x99,
+ 0xe2, 0xad, 0x26, 0xd9, 0x53, 0x10, 0x72, 0x0c,
+ 0x4e, 0x43, 0x4c, 0x61, 0xfe, 0xd9, 0xc1, 0x16,
+ 0xa1, 0x93, 0xca, 0x3c, 0x75, 0x7f, 0x07, 0x7a,
+ 0x65, 0xb3, 0x53, 0x2a, 0x52, 0x00, 0xa0, 0x62,
+ 0xe0, 0xa3, 0x1f, 0xad, 0xd7, 0xbb, 0xc0, 0x83,
+ 0x5d, 0x54, 0x87, 0x5f, 0xc8, 0x2f, 0xc8, 0xbf,
+ 0x69, 0x04, 0x91, 0xc8, 0xa6, 0x1d, 0x4d, 0x46,
+ 0x91, 0xfc, 0x26, 0xf4, 0x16, 0xd1, 0xa4, 0xbf,
+ 0x5c, 0xa2, 0x6c, 0xdd, 0xb4, 0x40, 0xf2, 0x2e,
+ 0xa2, 0xad, 0xf7, 0xf4, 0xa5, 0x8a, 0x3e, 0x23,
+ 0x64, 0x08, 0xc8, 0xa1, 0xa0, 0xf0, 0x5d, 0x70,
+ 0xd2, 0x77, 0xfd, 0xc8, 0x50, 0x83, 0x0f, 0xd6,
+ 0x2b, 0xe4, 0x1f, 0x52, 0x34, 0x33, 0x68, 0xfd,
+ 0x92, 0xbe, 0x9f, 0x97, 0x6b, 0x8d, 0x81, 0x91,
+ 0x0f, 0xef, 0x65, 0xc8, 0x0d, 0x15, 0x01, 0x77,
+ 0x58, 0xb2, 0xf4, 0x1b, 0x06, 0x7e, 0xf5, 0xca,
+ 0x15, 0x2e, 0x38, 0xd8, 0x81, 0x1c, 0x1c, 0xa0,
+ 0xb6, 0x13, 0x6a, 0x2b, 0x71, 0x34, 0x52, 0xd7,
+ 0x1d, 0xbd, 0x37, 0x59, 0xbc, 0x86, 0x25, 0x2b,
+ 0xa8, 0x93, 0xce, 0x1a, 0x03, 0x16, 0xfe, 0x01,
+ 0x57, 0x99, 0x24, 0x25, 0x2c, 0xb3, 0xab, 0x1e,
+ 0x2d, 0x65, 0x20, 0x89, 0x17, 0x02, 0x0e, 0x0a,
+ 0xf5, 0x1e, 0xc7, 0xff, 0x1f, 0x61, 0xa9, 0x54,
+ 0x18, 0xd4, 0xba, 0x50, 0x57, 0x02, 0xa1, 0xab,
+ 0x22, 0x2e, 0x07, 0xea, 0xa9, 0xa3, 0x83, 0x4f,
+ 0x27, 0xf5, 0xc5, 0xee, 0x3c, 0x3b, 0x10, 0xad,
+ 0x32, 0x2b, 0x1c, 0x03, 0xcb, 0xaf, 0x98, 0x83,
+ 0x54, 0xc3, 0x68, 0x63, 0xd4, 0xe0, 0x0e, 0x3c,
+ 0x1a, 0x4e, 0xc0, 0x81, 0xd0, 0xe8, 0x6a, 0x62,
+ 0x6b, 0x3e, 0x6f, 0xc4, 0xc6, 0x33, 0x4e, 0x26,
+ 0x21, 0xf5, 0x04, 0xdf, 0xfa, 0xce, 0x45, 0xaf,
+ 0xdc, 0x5e, 0x1b, 0xad, 0x93, 0xca, 0xf5, 0xcf,
+ 0xd7, 0xee, 0x0c, 0x5c, 0x5e, 0xb4, 0xf0, 0x92,
+ 0xd2, 0xf2, 0xf0, 0xa9, 0x1e, 0xab, 0x80, 0x68,
+ 0x46, 0xef, 0xcc, 0x26, 0x0c, 0x5c, 0xdd, 0x4e,
+ 0x83, 0xb8, 0xb9, 0x53, 0x6e, 0xf8, 0x93, 0x38,
+ 0x67, 0xa4, 0x41, 0x87, 0x72, 0xe7, 0x7e, 0x86,
+ 0xc9, 0x49, 0x00, 0x33, 0xb1, 0x38, 0x6c, 0x71,
+ 0xd7, 0x1d, 0x8e, 0x61, 0x01, 0xb6, 0x57, 0xa9,
+ 0xf1, 0xac, 0x15, 0xc2, 0x83, 0x77, 0xca, 0x64,
+ 0xca, 0x7b, 0x6c, 0xa1, 0x10, 0x1b, 0x13, 0xd0,
+ 0xd3, 0x9e, 0x9e, 0x10, 0x70, 0xc8, 0x1a, 0xbb,
+ 0x3f, 0x19, 0x86, 0xab, 0x01, 0x0e, 0xea, 0x34,
+ 0x22, 0xea, 0xe2, 0x15, 0xb7, 0xed, 0x21, 0x21,
+ 0x75, 0xa5, 0xe7, 0x08, 0xa1, 0x38, 0xe0, 0x91,
+ 0x05, 0x60, 0xea, 0xa7, 0x50, 0x27, 0x18, 0x07,
+ 0x9d, 0xe0, 0x18, 0x2b, 0xd4, 0x07, 0x59, 0x00,
+ 0xe6, 0x45, 0x18, 0x2a, 0x30, 0x6e, 0xf3, 0xb4,
+ 0xd0, 0xef, 0xa6, 0x5b, 0x71, 0xa2, 0x5a, 0x3b,
+ 0x89, 0x4c, 0xaf, 0x3f, 0xcb, 0x9f, 0x03, 0xfb,
+ 0x43, 0x7c, 0x6b, 0xd3, 0x6a, 0xea, 0xce, 0x4a,
+ 0x5f, 0x64, 0xb5, 0x62, 0xda, 0x5d, 0x27, 0xb7,
+ 0xb8, 0x11, 0xca, 0x33, 0x30, 0xec, 0x70, 0xf0,
+ 0x1b, 0x03, 0x50, 0xff, 0x5e, 0xa6, 0x08, 0xde,
+ 0x37, 0x70, 0xc0, 0x81, 0x55, 0x60, 0x17, 0xa1,
+ 0x85, 0xae, 0x26, 0x44, 0xe4, 0x67, 0x3c, 0x91,
+ 0xfd, 0xc4, 0x3d, 0x97, 0x72, 0x23, 0xf3, 0x3c,
+ 0x8f, 0xe0, 0xe2, 0xf2, 0x09, 0x96, 0x10, 0x67,
+ 0xb5, 0xfe, 0xff, 0x3d, 0x4a, 0xc8, 0x62, 0x11,
+ 0xa5, 0x98, 0xc1, 0x2d, 0x40, 0x82, 0x88, 0x8b,
+ 0xe5, 0xb0, 0x75, 0xbf, 0x2f, 0xa8, 0x6a, 0x55,
+ 0x49, 0x2e, 0x9c, 0x29, 0xd2, 0x7c, 0xbf, 0xf3,
+ 0xaa, 0x3a, 0x16, 0x4a, 0xa4, 0x15, 0xf3, 0x48,
+ 0xde, 0x38, 0x13, 0x44, 0x26, 0x02, 0xe6, 0xe9,
+ 0xa8, 0x24, 0x89, 0xb5, 0x43, 0x95, 0xe4, 0x4c,
+ 0xc3, 0xa0, 0xdf, 0xcc, 0x42, 0xf8, 0x8d, 0xb0,
+ 0x3b, 0xea, 0x10, 0xb7, 0xe1, 0x40, 0x54, 0xb9,
+ 0xa3, 0x2d, 0xfb, 0xb4, 0x91, 0xc0, 0x3e, 0x94,
+ 0xf1, 0xa1, 0x3c, 0xbe, 0xef, 0xb8, 0x70, 0x55,
+ 0x0a, 0x26, 0x93, 0xbf, 0xe6, 0x21, 0x92, 0x32,
+ 0x3c, 0x39, 0x27, 0x6a, 0x23, 0x48, 0x02, 0x35,
+ 0x3c, 0xd4, 0xcc, 0x04, 0xc0, 0x4e, 0xa7, 0x02,
+ 0x63, 0x37, 0xc2, 0xb8, 0x56, 0x1d, 0x57, 0x57,
+ 0x42, 0x04, 0x8d, 0xee, 0xcf, 0x8b, 0xc9, 0xc3,
+ 0xba, 0x3b, 0x15, 0xd7, 0xaf, 0xbf, 0x9e, 0xcd,
+ 0x44, 0xcf, 0xf0, 0x00, 0xb7, 0x3a, 0xfc, 0xa8,
+ 0x12, 0xab, 0x3a, 0x62, 0x01, 0x21, 0x46, 0xe9,
+ 0x1e, 0x48, 0x37, 0xfc, 0x13, 0x4d, 0xf6, 0x2a,
+ 0x72, 0x40, 0x75, 0x38, 0x71, 0xf2, 0x17, 0x20,
+ 0x2c, 0xdd, 0xc0, 0x49, 0xbc, 0x63, 0x33, 0xea,
+ 0x06, 0x75, 0x41, 0xe7, 0x5c, 0x1f, 0xfb, 0xf9,
+ 0x68, 0x83, 0xc2, 0x5a, 0x4a, 0x1e, 0x61, 0x08,
+ 0x57, 0xf3, 0x00, 0xba, 0x77, 0x92, 0x63, 0xa5,
+ 0xb7, 0xfe, 0x97, 0x22, 0xda, 0x5e, 0xd3, 0xaf,
+ 0xbc, 0x89, 0x0d, 0x4c, 0x37, 0xa9, 0x27, 0x4a,
+ 0x7f, 0xdb, 0x81, 0x39, 0x11, 0x86, 0x12, 0xf9,
+ 0x10, 0x50, 0xe4, 0xdb, 0x72, 0xf9, 0xae, 0x10,
+ 0x7c, 0xed, 0x50, 0x5c, 0x61, 0xeb, 0x42, 0x1e,
+ 0xa4, 0xf4, 0xf0, 0xfa, 0x45, 0x4d, 0x95, 0x2b,
+ 0xd4, 0x67, 0x4a, 0xe3, 0x8a, 0x15, 0x55, 0x92,
+ 0x77, 0x64, 0x8c, 0x51, 0x38, 0xf9, 0x26, 0x3e,
+ 0x68, 0xe2, 0xac, 0xbb, 0x64, 0x77, 0xe2, 0x82,
+ 0xa4, 0x42, 0x41, 0x38, 0xa0, 0xf0, 0xc9, 0xd8,
+ 0x6c, 0xe0, 0xef, 0x4c, 0xda, 0xb4, 0x92, 0xef,
+ 0x1b, 0xe3, 0x9b, 0xc1, 0x44, 0x3c, 0xb9, 0xb7,
+ 0x39, 0xac, 0x5c, 0x32, 0x39, 0xb4, 0x21, 0x85,
+ 0x93, 0xbc, 0xf2, 0x51, 0x43, 0xb7, 0xae, 0x1e,
+ 0x61, 0x9c, 0x38, 0x9c, 0xaa, 0xff, 0xde, 0xfc,
+ 0xbf, 0x85, 0xef, 0x17, 0x34, 0x36, 0x71, 0x5f,
+ 0x04, 0x16, 0xa6, 0x9e, 0xfd, 0x3a, 0x03, 0xd8,
+ 0xbf, 0x71, 0x70, 0x20, 0x8f, 0x7c, 0xfb, 0xff,
+ 0x61, 0xe0, 0xe2, 0x60, 0xa7, 0xb1, 0xc0, 0xe0,
+ 0xd9, 0x3f, 0xdc, 0x8d, 0x4a, 0xa4, 0x52, 0x61,
+ 0xaf, 0x9d, 0xdf, 0x8a, 0x0d, 0x41, 0xc0, 0x25,
+ 0x68, 0x12, 0x7b, 0xd5, 0xc7, 0xdb, 0x68, 0x70,
+ 0x2d, 0x7d, 0x95, 0x12, 0x03, 0x23, 0x0c, 0xe8,
+ 0x14, 0x41, 0x11, 0x28, 0xec, 0x9d, 0xd3, 0x28,
+ 0x77, 0x7a, 0x3c, 0x93, 0x8e, 0x5c, 0x7e, 0xb3,
+ 0x42, 0x9a, 0x18, 0x25, 0x93, 0xc8, 0xea, 0x43,
+ 0x1b, 0xbe, 0xd5, 0x27, 0xf1, 0xd4, 0xe0, 0x1e,
+ 0xce, 0xc7, 0xc7, 0x2c, 0x25, 0x35, 0x58, 0xb8,
+ 0x6c, 0xf3, 0xa2, 0xad, 0xe7, 0x58, 0x49, 0x47,
+ 0xf7, 0xca, 0xde, 0x8b, 0x81, 0xb7, 0x75, 0xf4,
+ 0x95, 0xa7, 0x5c, 0xc3, 0x2c, 0x0e, 0x1c, 0x52,
+ 0x9a, 0xc3, 0x2a, 0x00, 0x21, 0xa7, 0x51, 0x6b,
+ 0xf0, 0x05, 0x87, 0x8c, 0x42, 0x1b, 0xc3, 0x2e,
+ 0xa3, 0x76, 0x22, 0xd5, 0x7f, 0x56, 0x10, 0xef,
+ 0x98, 0x85, 0x65, 0x86, 0x71, 0x87, 0xd2, 0x8c,
+ 0xc0, 0x47, 0x20, 0xe8, 0xb5, 0x1c, 0xe3, 0xdd,
+ 0x3c, 0x5c, 0x03, 0xbb, 0x0e, 0x97, 0x3b, 0xe1,
+ 0x56, 0x9a, 0xd5, 0x0a, 0x63, 0xd5, 0x33, 0xaf,
+ 0x36, 0xca, 0xcf, 0x8f, 0x00, 0x28, 0xa3, 0x45,
+ 0xb8, 0xcd, 0xde, 0x73, 0xd4, 0xfa, 0x2d, 0x6f,
+ 0xdb, 0x93, 0xaa, 0xdd, 0x7f, 0xd2, 0x22, 0x9c,
+ 0x96, 0x48, 0x1e, 0xa8, 0x63, 0xbe, 0xbc, 0x0d,
+ 0x14, 0x3c, 0x2e, 0x11, 0x1f, 0xd2, 0xf4, 0x57,
+ 0xb3, 0x47, 0xf8, 0xa6, 0x1b, 0xc3, 0xa7, 0x95,
+ 0x2d, 0xd4, 0xca, 0xb8, 0x0d, 0xfb, 0x06, 0x85,
+ 0xda, 0x63, 0xf0, 0x3e, 0x9d, 0x5e, 0xee, 0xce,
+ 0xed, 0x74, 0x1d, 0x2c, 0x97, 0x3f, 0x71, 0x95,
+ 0x12, 0x03, 0xc5, 0x92, 0x46, 0x84, 0x1b, 0x07,
+ 0xe6, 0xb4, 0x1d, 0x3a, 0xf1, 0x89, 0x90, 0x50,
+ 0x10, 0x29, 0x34, 0xc0, 0x90, 0xbe, 0x4a, 0xa9,
+ 0x0d, 0xb0, 0x7b, 0xfb, 0x35, 0xee, 0x4e, 0x34,
+ 0xec, 0x5a, 0x58, 0xbc, 0xb8, 0xda, 0x38, 0x88,
+ 0x8c, 0x74, 0x1e, 0xc9, 0xab, 0x78, 0x2e, 0x2a,
+ 0x17, 0x8a, 0x43, 0x3d, 0xa1, 0x2a, 0x41, 0xb5,
+ 0xd6, 0xe8, 0x5b, 0xc5, 0x4a, 0x1c, 0x3c, 0x9f,
+ 0x8d, 0x3a, 0x69, 0x88, 0xf8, 0x80, 0xd2, 0x11,
+ 0xfc, 0x7e, 0x80, 0x8e, 0x7f, 0x85, 0x64, 0x9c,
+ 0x46, 0x58, 0xc8, 0x48, 0x98, 0x4b, 0xf5, 0x73,
+ 0x3f, 0x49, 0xce, 0x53, 0x2c, 0xd5, 0xfc, 0x33,
+ 0xf1, 0x6f, 0xd8, 0xe9, 0x2e, 0x70, 0x2e, 0xdc,
+ 0xe5, 0x43, 0x80, 0x38, 0xf2, 0x87, 0xed, 0x85,
+ 0xe4, 0x3e, 0x45, 0x14, 0x20, 0xcf, 0xa0, 0x61,
+ 0x4f, 0xe8, 0xd7, 0x5b, 0xb3, 0x0d, 0x0e, 0x4e,
+ 0x4d, 0xce, 0xbe, 0xba, 0xaa, 0x90, 0x09, 0xcb,
+ 0x4b, 0x5d, 0x08, 0xff, 0x52, 0xd5, 0x23, 0xbc,
+ 0xad, 0x8d, 0xd3, 0x06, 0x4a, 0xa0, 0x51, 0x56,
+ 0xa7, 0xd8, 0x33, 0xab, 0xbc, 0xd0, 0xdf, 0x92,
+ 0x87, 0x20, 0x2d, 0x7b, 0x5e, 0xfa, 0x30, 0xa7,
+ 0x06, 0x06, 0xe5, 0x4f, 0x2c, 0xb5, 0x61, 0xd7,
+ 0x54, 0xd3, 0xdf, 0xd0, 0x0a, 0xb0, 0x06, 0xce,
+ 0xf6, 0x86, 0xb7, 0x8e, 0xaa, 0x7b, 0x78, 0xd5,
+ 0xb9, 0xeb, 0x07, 0xac, 0x5f, 0xc5, 0xd2, 0x8c,
+ 0x40, 0xe0, 0x7f, 0x98, 0xd4, 0xe5, 0x4b, 0xca,
+ 0xfb, 0x47, 0xef, 0xef, 0xb9, 0x4d, 0x6d, 0x8f,
+ 0x82, 0x68, 0x74, 0x84, 0xe0, 0x0a, 0x93, 0x0f,
+ 0xb2, 0x01, 0xa9, 0x9f, 0x68, 0x6a, 0xe8, 0xf7,
+ 0xfb, 0x0b, 0xde, 0x17, 0xe0, 0x30, 0x38, 0x51,
+ 0xbc, 0x07, 0xb8, 0x2c, 0x91, 0x0f, 0xc1, 0x0e,
+ 0xa6, 0xf9, 0xf0, 0xd5, 0x48, 0x76, 0x8a, 0xde,
+ 0x74, 0xe3, 0x30, 0x65, 0x56, 0xb3, 0x5c, 0xe2,
+ 0x89, 0x8d, 0xda, 0x80, 0xad, 0x0f, 0x22, 0xfb,
+ 0x24, 0x1d, 0x16, 0xdd, 0x34, 0x4b, 0x90, 0x58,
+ 0x4e, 0x0c, 0x13, 0x28, 0xcf, 0x1d, 0xa4, 0xaa,
+ 0xb7, 0xf3, 0xb1, 0x66, 0xad, 0x3b, 0xcf, 0x79,
+ 0x12, 0x04, 0xd7, 0x79, 0xd9, 0x5f, 0xdf, 0x89,
+ 0xb2, 0x5b, 0xa7, 0x9a, 0x26, 0x1e, 0x67, 0x46,
+ 0x7c, 0x66, 0x95, 0x67, 0xe6, 0x45, 0x8b, 0x1f,
+ 0x65, 0x79, 0x9f, 0x6d, 0x11, 0x81, 0x17, 0x0d,
+ 0x11, 0xb0, 0x5c, 0xb4, 0xc7, 0x27, 0x87, 0xab,
+ 0x5d, 0x0a, 0x18, 0xae, 0x4e, 0x06, 0xa3, 0x3d,
+ 0xc7, 0xb0, 0x22, 0xba, 0x03, 0xa4, 0x0f, 0xe5,
+ 0x1c, 0x72, 0x2a, 0x04, 0xce, 0x83, 0xe9, 0xf3,
+ 0xd7, 0xc9, 0x67, 0x6c, 0x1e, 0x6b, 0x3c, 0x9b,
+ 0x0b, 0x5e, 0x6a, 0xa6, 0x79, 0x0a, 0xf1, 0xbe,
+ 0xd7, 0xb4, 0x6f, 0x45, 0x1e, 0xfb, 0x78, 0x97,
+ 0xaf, 0x34, 0x76, 0x95, 0x52, 0xf7, 0x3d, 0x5d,
+ 0x07, 0x28, 0x57, 0x9c, 0x4a, 0x0f, 0xcf, 0x0b,
+ 0x1b, 0xc4, 0xc2, 0x72, 0xd7, 0x72, 0x38, 0x9b,
+ 0xea, 0xeb, 0xee, 0xae, 0x34, 0xc8, 0x01, 0xd7,
+ 0xa5, 0xe3, 0xce, 0x41, 0xad, 0x02, 0x60, 0x23,
+ 0x18, 0x36, 0xba, 0x17, 0xfa, 0xcf, 0xe4, 0xda,
+ 0xdc, 0xfc, 0x82, 0xdc, 0x7c, 0x11, 0xf4, 0xb8,
+ 0x52, 0x5d, 0xf7, 0x2f, 0xc8, 0xfe, 0x4a, 0xe6,
+ 0xb9, 0xaf, 0x4b, 0x17, 0x18, 0x91, 0xc2, 0xfe,
+ 0xd7, 0x3a, 0x77, 0x0c, 0xa0, 0x43, 0x9c, 0x6f,
+ 0x13, 0x06, 0xbe, 0x6e, 0xe0, 0x1a, 0x3c, 0xf3,
+ 0xf5, 0xcc, 0x78, 0xfb, 0x5d, 0xd5, 0xda, 0xb7,
+ 0x58, 0xea, 0x86, 0x42, 0x6b, 0x32, 0xff, 0xb2,
+ 0xe2, 0xee, 0x03, 0x1f, 0xf4, 0xef, 0xdb, 0x53,
+ 0x79, 0xd5, 0x4e, 0xaf, 0x60, 0x8e, 0x02, 0xc2,
+ 0xcc, 0x39, 0x97, 0x7b, 0xfd, 0xa1, 0xf8, 0x7a,
+ 0x26, 0xe8, 0x55, 0xd6, 0xa4, 0x8b, 0xa0, 0x1b,
+ 0x2d, 0x63, 0xaa, 0x73, 0x71, 0x6e, 0xbf, 0x8b,
+ 0x3b, 0xe3, 0x1b, 0x0d, 0xbb, 0x2e, 0x44, 0x09,
+ 0x64, 0xac, 0xc7, 0x9e, 0xb5, 0xc6, 0x77, 0xb0,
+ 0x79, 0xb3, 0xaa, 0xfc, 0x67, 0x57, 0x9a, 0x50,
+ 0x81, 0x37, 0x14, 0x7c, 0xd7, 0xa0, 0xd4, 0x6a,
+ 0x79, 0x84, 0x51, 0x0e, 0x95, 0x0a, 0x30, 0xa3,
+ 0x60, 0x55, 0x48, 0x05, 0x16, 0xae, 0x43, 0x90,
+ 0xdc, 0x8e, 0x09, 0xbe, 0x79, 0xf6, 0x90, 0x74,
+ 0xf8, 0x20, 0x96, 0x4d, 0xa7, 0xf5, 0x1a, 0x2b,
+ 0xc7, 0x15, 0x9d, 0x18, 0xf7, 0x94, 0x87, 0xf7,
+ 0xf4, 0xfb, 0x0d, 0x61, 0xb6, 0xd7, 0xbe, 0x10,
+ 0x8e, 0x47, 0x3c, 0x10, 0x44, 0x90, 0x52, 0x21,
+ 0x83, 0xc0, 0xf5, 0x99, 0xaa, 0xbc, 0xf6, 0x55,
+ 0xae, 0xf5, 0xb2, 0xa4, 0xcd, 0x4d, 0xb9, 0x38,
+ 0x6c, 0xbc, 0x80, 0xc3, 0xad, 0xf4, 0x46, 0x31,
+ 0x01, 0x58, 0x2d, 0x88, 0x57, 0xc3, 0x23, 0xd1,
+ 0x64, 0xc9, 0xa3, 0x21, 0x6b, 0x8b, 0x8a, 0x23,
+ 0x2c, 0x4f, 0xa9, 0xcd, 0x67, 0xfa, 0x77, 0xad,
+ 0xa3, 0x16, 0xa2, 0xe5, 0x19, 0x14, 0x70, 0x41,
+ 0x5b, 0xda, 0x14, 0xde, 0xe3, 0xe5, 0xc1, 0x15,
+ 0xb4, 0x77, 0xa4, 0x9b, 0xb8, 0xb1, 0x28, 0x51,
+ 0x30, 0xb4, 0xf1, 0xf3, 0xf8, 0x6d, 0xd0, 0xc3,
+ 0x8c, 0x4c, 0x76, 0xb0, 0x9a, 0xdf, 0xc8, 0xbe,
+ 0xf8, 0x4a, 0x61, 0x6e, 0x3e, 0xd6, 0x3c, 0xe8,
+ 0xde, 0x56, 0xa0, 0x9c, 0x25, 0xbe, 0xce, 0x93,
+ 0x1f, 0x88, 0xfb, 0x9a, 0x1a, 0xe2, 0xff, 0x88,
+ 0xad, 0x10, 0xcb, 0x6c, 0xd6, 0xe7, 0x39, 0x0b,
+ 0xe5, 0x1a, 0x06, 0x05, 0x64, 0x5b, 0x0a, 0xdf,
+ 0x22, 0x58, 0xd7, 0xfb, 0x88, 0x12, 0xdd, 0xb7,
+ 0x52, 0x3a, 0xc9, 0xbf, 0x49, 0xdf, 0x8c, 0x87,
+ 0x9f, 0x84, 0xb5, 0x0a, 0xf6, 0x00, 0x52, 0xae,
+ 0x67, 0x12, 0x1a, 0x8c, 0x71, 0x15, 0xf5, 0xa1,
+ 0x13, 0x39, 0xf0, 0x91, 0x7e, 0x88, 0x7c, 0xb3,
+ 0x95, 0x50, 0x02, 0xa6, 0x63, 0xb5, 0x64, 0xfb,
+ 0x90, 0x87, 0x61, 0xe2, 0x27, 0xaf, 0x11, 0x0c,
+ 0x73, 0x83, 0xef, 0xa9, 0x28, 0xfe, 0xc8, 0x85,
+ 0x1a, 0x3a, 0xde, 0xf2, 0xe5, 0x25, 0x64, 0x6d,
+ 0xaa, 0x41, 0x4c, 0x80, 0x2e, 0x84, 0xff, 0xc1,
+ 0xc0, 0x54, 0x0c, 0x29, 0x1b, 0xa3, 0x07, 0x7c,
+ 0x33, 0x4c, 0x10, 0xf6, 0x6f, 0x79, 0xdf, 0xd3,
+ 0xf0, 0x24, 0x57, 0xf1, 0x60, 0xe1, 0xf0, 0xbd,
+ 0xc4, 0x1f, 0xf4, 0x67, 0xd2, 0xd3, 0xcc, 0x6a,
+ 0x07, 0x72, 0x44, 0x16, 0x85, 0x46, 0xd0, 0x73,
+ 0x87, 0xa9, 0xc7, 0x2f, 0xd1, 0xf5, 0xec, 0xe3,
+ 0x28, 0xa3, 0x93, 0x4f, 0xd7, 0x76, 0xc1, 0x3c,
+ 0x0d, 0x13, 0x33, 0xcf, 0x5b, 0xbd, 0x6a, 0x52,
+ 0x4e, 0xee, 0xc8, 0x5e, 0xa1, 0x58, 0x4a, 0x08,
+ 0x81, 0xd9, 0x23, 0xcc, 0xfb, 0x1c, 0xb2, 0xd8,
+ 0xa3, 0xe4, 0x53, 0xfe, 0xf4, 0x4b, 0x48, 0xc1,
+ 0x20, 0xa4, 0x97, 0xf8, 0x38, 0xa3, 0x69, 0xc1,
+ 0x11, 0xf0, 0xa1, 0x3b, 0xa9, 0x9a, 0x12, 0x61,
+ 0xe8, 0x8d, 0x99, 0x44, 0x3f, 0x94, 0x72, 0x82,
+ 0x19, 0x96, 0x62, 0xb0, 0xa6, 0x64, 0x05, 0x19,
+ 0x8f, 0xd6, 0x5d, 0x05, 0xbf, 0x79, 0x9e, 0x9d,
+ 0xe4, 0x93, 0x4c, 0xad, 0x61, 0x8c, 0x18, 0xda,
+ 0xb6, 0x2e, 0xb3, 0xca, 0x14, 0x4d, 0x53, 0xa4,
+ 0x97, 0x27, 0x10, 0x56, 0xa2, 0x67, 0x5a, 0x5a,
+ 0x5e, 0x13, 0xc0, 0xdb, 0xa7, 0x9f, 0x45, 0x5b,
+ 0xeb, 0x1a, 0x14, 0x0c, 0x8c, 0x38, 0x5e, 0x77,
+ 0x9a, 0xec, 0x75, 0x68, 0x93, 0x65, 0x02, 0x9c,
+ 0xfb, 0x62, 0x60, 0x49, 0xdd, 0xb2, 0x2a, 0x67,
+ 0x86, 0xe3, 0x8a, 0x7d, 0x8c, 0x46, 0x78, 0x81,
+ 0x60, 0x69, 0xf2, 0x3f, 0x74, 0x11, 0x35, 0xff,
+ 0x77, 0xa3, 0x66, 0x20, 0xfc, 0x98, 0x4a, 0x35,
+ 0x7a, 0x52, 0xe4, 0x90, 0x13, 0x80, 0xb9, 0xa6,
+ 0x73, 0x7a, 0x7d, 0x66, 0x6e, 0x6b, 0xb6, 0x43,
+ 0x10, 0xd5, 0x91, 0x2b, 0x66, 0xdd, 0x89, 0x87,
+ 0xe3, 0x8c, 0x58, 0x53, 0x2f, 0x40, 0x74, 0x45,
+ 0x1b, 0x77, 0x7a, 0xa4, 0x44, 0x19, 0x78, 0xba,
+ 0x87, 0x10, 0x41, 0x31, 0x32, 0x5f, 0x87, 0x68,
+ 0xde, 0x43, 0x4a, 0xef, 0x33, 0xb3, 0x11, 0x83,
+ 0xa9, 0xc2, 0x6f, 0x8d, 0x34, 0xe2, 0x95, 0x84,
+ 0x3a, 0x4f, 0x6f, 0x8c, 0x31, 0x1d, 0xb6, 0xf5,
+ 0x95, 0x0d, 0x01, 0x11, 0x20, 0xdf, 0x72, 0xf3,
+ 0x3f, 0x9a, 0x33, 0xaa, 0xb1, 0x06, 0x6a, 0x63,
+ 0x47, 0x91, 0x01, 0xdf, 0xb3, 0x54, 0x36, 0xfd,
+ 0x06, 0x2d, 0xb8, 0x08, 0xe3, 0xd3, 0x65, 0xac,
+ 0x66, 0x03, 0xee, 0xa4, 0x63, 0xbd, 0xd4, 0xce,
+ 0xbd, 0x79, 0xa7, 0x48, 0x38, 0xc5, 0x7d, 0xb5,
+ 0x71, 0x9a, 0x3c, 0x11, 0x7c, 0x6c, 0xe2, 0x54,
+ 0x02, 0x5d, 0x42, 0xab, 0x25, 0x93, 0x66, 0x01,
+ 0x37, 0x78, 0x35, 0x4a, 0x8c, 0x19, 0x4d, 0x00,
+ 0x75, 0x4f, 0xcc, 0xc0, 0x26, 0x82, 0xc1, 0x35,
+ 0x8c, 0xc7, 0xc2, 0x59, 0x01, 0x3e, 0x98, 0x22,
+ 0x88, 0x9c, 0x90, 0x75, 0x05, 0x33, 0x07, 0xb9,
+ 0x39, 0x81, 0x38, 0x58, 0x10, 0x29, 0xcf, 0xc8,
+ 0x98, 0xb2, 0x03, 0xd7, 0x5b, 0xb3, 0x18, 0xba,
+ 0x34, 0x0c, 0x9f, 0xab, 0xd7, 0xed, 0x29, 0x82,
+ 0x41, 0xe0, 0x20, 0x97, 0x57, 0x92, 0xb2, 0xb8,
+ 0x10, 0x2d, 0x0b, 0xa2, 0xc5, 0x8f, 0x90, 0x6f,
+ 0xed, 0x12, 0x56, 0x25, 0xbe, 0xfd, 0x75, 0xf7,
+ 0xb6, 0xf8, 0x40, 0x67, 0x39, 0x11, 0xfa, 0x15,
+ 0xae, 0x6a, 0x54, 0x5f, 0x32, 0x2b, 0xf8, 0x48,
+ 0x55, 0xbe, 0x86, 0x2f, 0x69, 0x48, 0x5b, 0x5d,
+ 0x4d, 0xb7, 0x35, 0xaa, 0xb6, 0x91, 0x88, 0x19,
+ 0x96, 0x1c, 0x68, 0xf6, 0x85, 0x9e, 0xb3, 0xb2,
+ 0xa3, 0x32, 0xd4, 0x52, 0x70, 0xb7, 0x62, 0xe3,
+ 0x14, 0xb6, 0x78, 0x5f, 0x1b, 0x1d, 0x04, 0x9c,
+ 0x26, 0x0c, 0x33, 0x94, 0xb1, 0x97, 0x08, 0xdb,
+ 0x0b, 0x39, 0x29, 0xd4, 0xbc, 0x6d, 0xdf, 0x02,
+ 0xc6, 0x99, 0xab, 0x99, 0x32, 0xe5, 0xce, 0x51,
+ 0x4f, 0xae, 0xb8, 0x8b, 0xe0, 0xaf, 0x07, 0xc4,
+ 0xf9, 0x41, 0x7c, 0x59, 0xa0, 0xac, 0x74, 0x4d,
+ 0x7e, 0x43, 0x77, 0x9c, 0x06, 0x49, 0x79, 0x8a,
+ 0x14, 0x73, 0x93, 0xa8, 0x5b, 0x1b, 0x34, 0x29,
+ 0x78, 0x04, 0x2f, 0xd7, 0x1f, 0x13, 0x90, 0xe0,
+ 0xdd, 0x3b, 0x42, 0x6b, 0x79, 0x6e, 0x52, 0xc7,
+ 0x0f, 0x38, 0xda, 0x01, 0x2c, 0x8d, 0xe6, 0x94,
+ 0x5d, 0x59, 0x27, 0x1d, 0x10, 0x4e, 0x11, 0x36,
+ 0xfb, 0x53, 0x16, 0x05, 0x25, 0xf2, 0x64, 0xd8,
+ 0xf9, 0xcd, 0x5c, 0xfe, 0xb4, 0x18, 0x44, 0x80,
+ 0x10, 0xbc, 0x3d, 0xf3, 0x1d, 0x5a, 0xf0, 0xc1,
+ 0xc3, 0x55, 0xff, 0x41, 0x3e, 0xe3, 0xef, 0x44,
+ 0xb2, 0xc0, 0x01, 0x18, 0xa2, 0x49, 0x88, 0x78,
+ 0x0d, 0x4c, 0xc8, 0x73, 0xcf, 0x30, 0x85, 0x3a,
+ 0x88, 0x90, 0x01, 0xcf, 0x69, 0x53, 0xa3, 0x18,
+ 0x3f, 0xd6, 0xe7, 0x94, 0x14, 0xa7, 0xae, 0xcd,
+ 0x6f, 0x11, 0x72, 0xfe, 0x2b, 0xb0, 0x81, 0x53,
+ 0xea, 0x67, 0xd6, 0xe4, 0xca, 0x42, 0xa0, 0xf9,
+ 0xb1, 0xd4, 0xb5, 0x3b, 0xc9, 0xf0, 0x36, 0xc1,
+ 0x1c, 0xf4, 0xb1, 0xf6, 0x84, 0xd0, 0x86, 0x6c,
+ 0x76, 0x9a, 0x03, 0xc2, 0xb6, 0x2e, 0x9a, 0x46,
+ 0xf5, 0x5f, 0x2c, 0x38, 0xac, 0xad, 0x6f, 0x2e,
+ 0x7a, 0x18, 0x2d, 0x22, 0x95, 0x5e, 0x5e, 0xc9,
+ 0x7a, 0x0a, 0x56, 0xe1, 0xc7, 0x15, 0xfd, 0xbf,
+ 0xff, 0xf7, 0x7e, 0x85, 0x20, 0xa9, 0x8a, 0x9c,
+ 0xa9, 0x7d, 0xe8, 0xed, 0xfc, 0x7f, 0xbb, 0xf0,
+ 0x05, 0x3f, 0xce, 0x4f, 0x4c, 0xee, 0xa4, 0xa0,
+ 0xcc, 0x9c, 0x62, 0x1e, 0xd6, 0xd0, 0x30, 0x37,
+ 0xb8, 0x98, 0x56, 0x1d, 0xaa, 0xd6, 0x5e, 0x73,
+ 0x12, 0xe4, 0x88, 0x82, 0x48, 0x64, 0x06, 0xd7,
+ 0x2a, 0x31, 0x50, 0x7b, 0x10, 0x17, 0xb8, 0x4c,
+ 0x5a, 0x8d, 0xf1, 0xfc, 0xf1, 0x33, 0x3b, 0x98,
+ 0x42, 0x18, 0x5b, 0x35, 0x78, 0xca, 0x8e, 0x41,
+ 0x52, 0xae, 0x6d, 0xe1, 0xa2, 0x9d, 0x5b, 0xbd,
+ 0xf3, 0x5f, 0x49, 0xc1, 0x27, 0x06, 0xc1, 0xaf,
+ 0xc0, 0xa3, 0x9d, 0xf3, 0x1c, 0x8e, 0x90, 0x8a,
+ 0xb0, 0x69, 0xb0, 0xc5, 0x11, 0x0c, 0x91, 0x14,
+ 0x1f, 0x5e, 0x10, 0xe1, 0x1d, 0x14, 0x30, 0x54,
+ 0x1e, 0x17, 0x3d, 0x31, 0x7b, 0xbf, 0x2f, 0x9d,
+ 0x6d, 0x63, 0x32, 0xf0, 0x9d, 0x9f, 0x95, 0x3d,
+ 0x0b, 0xd2, 0x4d, 0x10, 0xe2, 0x3f, 0x67, 0x69,
+ 0x43, 0x9a, 0x4a, 0x2c, 0x54, 0x71, 0xa8, 0xa0,
+ 0x9e, 0x9f, 0x10, 0xaf, 0x1b, 0xce, 0x99, 0xe3,
+ 0x25, 0x32, 0x10, 0x54, 0x80, 0xfe, 0xda, 0x57,
+ 0xd0, 0xb2, 0x92, 0x7f, 0xbb, 0x5f, 0xe7, 0x4d,
+ 0x1b, 0x3d, 0x46, 0x4d, 0xe4, 0x4c, 0xd6, 0xaf,
+ 0x1a, 0x32, 0x12, 0x40, 0xb8, 0x84, 0x8e, 0xe4,
+ 0x80, 0xce, 0x7e, 0xc1, 0x13, 0x8b, 0xb0, 0xb7,
+ 0x6f, 0x24, 0xba, 0x85, 0x50, 0x83, 0xc3, 0xcf,
+ 0x19, 0xb3, 0xf0, 0xc7, 0xee, 0x68, 0xbe, 0x9e,
+ 0x6d, 0xb9, 0xfb, 0xd5, 0x29, 0xce, 0x82, 0xcd,
+ 0x69, 0x16, 0x68, 0x6b, 0x6a, 0xf4, 0x02, 0x32,
+ 0xce, 0x60, 0x37, 0x0c, 0xb9, 0x38, 0x92, 0x9c,
+ 0x42, 0xa9, 0x0b, 0x53, 0x96, 0xfe, 0x39, 0xc1,
+ 0x24, 0x65, 0x9b, 0xcd, 0xe7, 0x8d, 0x36, 0x07,
+ 0x9f, 0x1d, 0x35, 0x8e, 0xdc, 0x4c, 0xb5, 0x68,
+ 0xc5, 0xfd, 0x44, 0x19, 0xf2, 0x6c, 0x59, 0x1c,
+ 0xb1, 0x0b, 0x35, 0x48, 0x86, 0x1a, 0x05, 0x22,
+ 0x03, 0x0c, 0x0c, 0xa2, 0x92, 0x90, 0x35, 0xfb,
+ 0x37, 0x94, 0xc7, 0x15, 0x84, 0xae, 0xe8, 0x05,
+ 0xa0, 0xf7, 0x30, 0x11, 0x5c, 0xe4, 0x5d, 0x3e,
+ 0x12, 0x54, 0x80, 0x54, 0x6b, 0x09, 0x8c, 0xce,
+ 0x80, 0x5e, 0xa7, 0xc8, 0x6a, 0x0c, 0x56, 0xe1,
+ 0x18, 0x7d, 0xc9, 0x39, 0xc1, 0xef, 0xe3, 0x25,
+ 0xa0, 0x8b, 0x2f, 0x60, 0x3a, 0x43, 0x39, 0xa6,
+ 0x28, 0x28, 0x7b, 0x4c, 0x77, 0xd4, 0x49, 0x61,
+ 0x46, 0xe9, 0x1b, 0x45, 0xd6, 0xb1, 0x56, 0xe1,
+ 0x7d, 0x34, 0xcd, 0x06, 0xb6, 0x67, 0x8d, 0x7d,
+ 0x7a, 0xe2, 0xbe, 0x68, 0x35, 0xa6, 0x78, 0xe5,
+ 0x47, 0x48, 0xb7, 0xc7, 0xde, 0xcd, 0xc9, 0x05,
+ 0xb4, 0xe7, 0x50, 0x48, 0xe1, 0x4b, 0xfe, 0x76,
+ 0x77, 0xc6, 0xf7, 0x5f, 0xcb, 0xc2, 0xa8, 0xd7,
+ 0xd6, 0x8a, 0xe5, 0x49, 0xd9, 0xca, 0x45, 0xf4,
+ 0xda, 0xcd, 0x33, 0xd1, 0x59, 0x2d, 0x9e, 0xc1,
+ 0x5c, 0xe6, 0x01, 0x18, 0xb8, 0xf0, 0x5e, 0xb1,
+ 0x69, 0x95, 0x2f, 0x02, 0x2a, 0xe7, 0x4a, 0xd7,
+ 0xd1, 0xc3, 0xd5, 0x6f, 0x15, 0xc8, 0xdc, 0x29,
+ 0xde, 0xb9, 0x3f, 0x8b, 0xa6, 0xbc, 0xdd, 0x25,
+ 0x84, 0x35, 0x3c, 0x90, 0x2d, 0xc2, 0x1e, 0x98,
+ 0x8a, 0x50, 0x09, 0x77, 0x42, 0xe9, 0x35, 0x8a,
+ 0x7c, 0x97, 0xbf, 0xe8, 0xbf, 0x56, 0xd0, 0x8b,
+ 0x65, 0xd3, 0xaf, 0x1e, 0x05, 0x94, 0xfa, 0xac,
+ 0xa8, 0x2b, 0x28, 0xcb, 0x37, 0x3e, 0xe8, 0xbb,
+ 0x66, 0x3a, 0xed, 0xb2, 0x48, 0x10, 0x0f, 0x3a,
+ 0x5a, 0xc5, 0xdb, 0x26, 0x0e, 0xaa, 0x5e, 0x69,
+ 0x15, 0xd6, 0x81, 0xae, 0xbd, 0xe6, 0x03, 0xf1,
+ 0xf6, 0x37, 0xc8, 0xde, 0x70, 0x1f, 0x64, 0xb9,
+ 0x5e, 0xbf, 0x2e, 0x4f, 0xb1, 0xea, 0xa0, 0x17,
+ 0xe6, 0x7c, 0xf9, 0x2f, 0x1e, 0xd8, 0x58, 0xde,
+ 0xa7, 0xf0, 0x46, 0x52, 0x95, 0xdf, 0xa4, 0x96,
+ 0xd0, 0xc4, 0x97, 0x2b, 0x95, 0xcd, 0x5e, 0x40,
+ 0x23, 0x5c, 0x10, 0xee, 0xba, 0x72, 0x9b, 0xcf,
+ 0x0b, 0xe8, 0x18, 0x3a, 0x70, 0xd2, 0x5e, 0x07,
+ 0x68, 0x93, 0xef, 0x4a, 0x5b, 0x8d, 0x72, 0x41,
+ 0x4e, 0xea, 0x33, 0x6a, 0x0a, 0x5e, 0xfb, 0x02,
+ 0x3f, 0xd4, 0xed, 0x5b, 0xe0, 0x42, 0x84, 0xd4,
+ 0xaa, 0x85, 0xdc, 0x5b, 0x67, 0xee, 0x71, 0x67,
+ 0xba, 0x8e, 0xd2, 0xbe, 0x61, 0xdf, 0x5a, 0x26,
+ 0xb9, 0xf0, 0x77, 0x81, 0x53, 0x24, 0x16, 0xcb,
+ 0x8c, 0xb8, 0x06, 0x6e, 0x68, 0xda, 0xc8, 0x2d,
+ 0x17, 0x54, 0xdb, 0x46, 0xcb, 0xfd, 0x1f, 0x3d,
+ 0x94, 0x81, 0x09, 0x4b, 0xfa, 0xb1, 0x46, 0xd9,
+ 0x11, 0xa3, 0xb7, 0x31, 0x9c, 0xd2, 0x38, 0xd6,
+ 0xba, 0x3d, 0xa3, 0x74, 0xd8, 0xf1, 0x24, 0xe8,
+ 0x9c, 0xcb, 0x1d, 0xf9, 0x4a, 0xf7, 0xc8, 0x4b,
+ 0xfe, 0x97, 0x7c, 0xa1, 0x02, 0xeb, 0x40, 0xc3,
+ 0x89, 0x71, 0x01, 0xcd, 0x33, 0x2a, 0xc2, 0x82,
+ 0xce, 0x62, 0x8d, 0x53, 0x7c, 0xdf, 0xce, 0xd7,
+ 0xf5, 0xa8, 0x4f, 0xf2, 0xf2, 0x2e, 0xc1, 0xeb,
+ 0x97, 0x99, 0x37, 0x3c, 0x53, 0xa6, 0xb4, 0x46,
+ 0x05, 0x64, 0x92, 0x87, 0x08, 0x3c, 0x23, 0x4b,
+ 0x9d, 0x67, 0x18, 0xf9, 0xe2, 0x0b, 0x1c, 0x39,
+ 0xd3, 0x87, 0x70, 0xc0, 0xb9, 0x1e, 0x52, 0x0a,
+ 0x0f, 0x48, 0xe2, 0xe7, 0x51, 0x72, 0x94, 0xf7,
+ 0xa3, 0xdc, 0xe5, 0x66, 0x33, 0x39, 0x54, 0x06,
+ 0x55, 0x93, 0x30, 0xf9, 0x5e, 0x76, 0x8f, 0xe0,
+ 0x59, 0x4d, 0x0d, 0xa7, 0xf5, 0xbe, 0xdb, 0x20,
+ 0xad, 0x0d, 0x76, 0x88, 0x5f, 0x9c, 0x7c, 0x75,
+ 0x2f, 0x2a, 0x0b, 0x79, 0x6e, 0xd3, 0xe2, 0x66,
+ 0xf5, 0x4a, 0x2d, 0x87, 0x87, 0x49, 0x84, 0x17,
+ 0xa2, 0x62, 0x4c, 0xbb, 0xe4, 0x6e, 0x98, 0x10,
+ 0xc9, 0xfb, 0x8a, 0x04, 0x68, 0x8d, 0x22, 0x66,
+ 0xad, 0xea, 0x2a, 0xc9, 0x97, 0x2d, 0x3c, 0xbc,
+ 0xd0, 0x77, 0x5f, 0xe6, 0xb8, 0x7f, 0xe6, 0xf6,
+ 0x39, 0xbf, 0x56, 0x0e, 0x26, 0x6d, 0xc5, 0x3e,
+ 0x53, 0x19, 0xd6, 0xb4, 0x57, 0x36, 0xa3, 0xc6,
+ 0xd3, 0x3d, 0x66, 0x79, 0x30, 0x5c, 0x14, 0x0c,
+ 0x0f, 0x3e, 0x96, 0xae, 0x90, 0x97, 0xab, 0x0d,
+ 0x9f, 0xc3, 0xe7, 0x66, 0x3e, 0xe0, 0x31, 0x43,
+ 0x4b, 0x01, 0xb3, 0x0e, 0x9e, 0x8c, 0x82, 0x4a,
+ 0x8c, 0xc7, 0x79, 0x85, 0xdf, 0x75, 0x0d, 0xb4,
+ 0x2b, 0x03, 0x14, 0xef, 0x72, 0x58, 0xfd, 0x64,
+ 0xc8, 0xe3, 0x0d, 0x9a, 0x14, 0x6f, 0x76, 0xf9,
+ 0x46, 0xd1, 0xd2, 0x81, 0xb3, 0x16, 0x6e, 0xc7,
+ 0x76, 0x82, 0xce, 0xf4, 0xee, 0x33, 0x00, 0xe6,
+ 0x77, 0xc4, 0xad, 0x4f, 0x06, 0xa7, 0x48, 0x80,
+ 0x9e, 0x21, 0x66, 0xca, 0x75, 0x69, 0x57, 0xcb,
+ 0xf0, 0x67, 0x6a, 0xaa, 0x8f, 0x88, 0x14, 0xbd,
+ 0x65, 0x62, 0xe2, 0xad, 0xcc, 0x22, 0x88, 0x7b,
+ 0x94, 0xbd, 0x0e, 0xcd, 0xb6, 0x69, 0xa2, 0xcb,
+ 0x7d, 0x57, 0x5c, 0xb4, 0x92, 0x80, 0x13, 0x99,
+ 0x84, 0xf3, 0x79, 0x0a, 0x2d, 0x70, 0xa4, 0xe0,
+ 0xde, 0xc6, 0x32, 0xb0, 0x8a, 0x62, 0xb5, 0xcf,
+ 0xfa, 0x5e, 0x5a, 0x92, 0x32, 0x7d, 0x34, 0x07,
+ 0xb5, 0x52, 0x3a, 0xb5, 0x7d, 0x0f, 0xa1, 0xba,
+ 0x56, 0xd0, 0x07, 0x76, 0x11, 0xf2, 0xc3, 0x33,
+ 0x9d, 0xbd, 0x12, 0x35, 0x5e, 0xf7, 0x05, 0x88,
+ 0x76, 0x94, 0xa6, 0xbf, 0xed, 0xb8, 0xa4, 0xa2,
+ 0x0c, 0xbe, 0x0f, 0x6a, 0xaf, 0xf3, 0x1b, 0x33,
+ 0x4a, 0xb7, 0x68, 0x3f, 0xbe, 0x95, 0x13, 0x97,
+ 0x0f, 0x15, 0x17, 0x1b, 0x23, 0xaa, 0x08, 0x78,
+ 0xa6, 0x5b, 0x08, 0xa2, 0x9d, 0x03, 0xa8, 0xa7,
+ 0x39, 0xdc, 0xbc, 0x9a, 0x85, 0xf5, 0xe5, 0x55,
+ 0x59, 0x3c, 0xef, 0xf9, 0x3f, 0x22, 0x8e, 0xf8,
+ 0xd8, 0x3e, 0x02, 0x0b, 0xd8, 0x78, 0x4b, 0x15,
+ 0x7f, 0xaa, 0x2c, 0xff, 0xbe, 0x77, 0x33, 0xc7,
+ 0x6a, 0x12, 0xaa, 0xa4, 0xbe, 0xc0, 0x3b, 0xcb,
+ 0x13, 0x9d, 0x9c, 0x5a, 0x9f, 0x8a, 0x57, 0x36,
+ 0x4f, 0x02, 0x5a, 0xf8, 0x1d, 0x97, 0x77, 0x43,
+ 0xc8, 0xa5, 0xb7, 0x9b, 0x10, 0x98, 0xfd, 0x58,
+ 0xbf, 0x42, 0xf6, 0xbf, 0xff, 0x6c, 0x40, 0x18,
+ 0x18, 0xdf, 0xac, 0x57, 0x71, 0xea, 0xcc, 0x8e,
+ 0xfd, 0xfe, 0x10, 0xfb, 0xb9, 0xfe, 0xbc, 0x9a,
+ 0x9c, 0x27, 0xe4, 0x10, 0x15, 0x94, 0x41, 0xa1,
+ 0xcc, 0xf6, 0x25, 0x49, 0x4f, 0x96, 0xc1, 0x8c,
+ 0x9e, 0x3e, 0x18, 0x29, 0x49, 0x92, 0xe7, 0xfe,
+ 0x22, 0xff, 0xed, 0x02, 0x16, 0x90, 0xef, 0xac,
+ 0xec, 0x95, 0x1d, 0x5b, 0x94, 0x9c, 0xf6, 0x7c,
+ 0x1b, 0x5a, 0x9d, 0xb0, 0x9b, 0x05, 0x36, 0xbf,
+ 0xef, 0xec, 0x63, 0x35, 0x40, 0x24, 0x45, 0x40,
+ 0x30, 0x1a, 0x9b, 0x90, 0xc3, 0xc2, 0xf7, 0x37,
+ 0xfb, 0x08, 0x8e, 0x48, 0x19, 0x48, 0xed, 0xa8,
+ 0xa8, 0x04, 0x6f, 0xd0, 0x33, 0xe9, 0xb8, 0x8d,
+ 0xe7, 0x1e, 0x5c, 0x47, 0x74, 0xc0, 0x66, 0x30,
+ 0x4e, 0xa7, 0x86, 0x73, 0xf1, 0xe5, 0x78, 0xa6,
+ 0xe0, 0xc1, 0xda, 0x13, 0x72, 0x07, 0x85, 0x34,
+ 0x63, 0x95, 0x49, 0x30, 0x4b, 0x9d, 0x03, 0xf1,
+ 0x7a, 0x6b, 0x91, 0xa2, 0x85, 0x41, 0xf9, 0x4a,
+ 0xd6, 0xff, 0xff, 0x86, 0xf7, 0xf0, 0xce, 0xb9,
+ 0x07, 0xf1, 0x88, 0x04, 0x33, 0xaa, 0xeb, 0x54,
+ 0xb2, 0x1c, 0x8e, 0x2e, 0x7b, 0x04, 0xa8, 0xcc,
+ 0x2c, 0x7a, 0xb3, 0xad, 0x1a, 0x89, 0x38, 0x89,
+ 0xd7, 0x11, 0x3a, 0x8c, 0xcf, 0xe3, 0xc5, 0xba,
+ 0xb0, 0xcc, 0xc4, 0xe3, 0x33, 0xf3, 0x18, 0xba,
+ 0xec, 0x56, 0xd9, 0x1c, 0x40, 0x70, 0x0d, 0x4e,
+ 0x97, 0x01, 0x23, 0xf3, 0x5a, 0xdc, 0xbf, 0x68,
+ 0x93, 0xc2, 0x1d, 0x8a, 0x96, 0xb7, 0xac, 0x18,
+ 0x6f, 0xf7, 0x84, 0x71, 0x0d, 0x3d, 0xf8, 0xba,
+ 0xdf, 0xb6, 0x89, 0x1d, 0x78, 0x19, 0xf2, 0x59,
+ 0xe9, 0x15, 0x55, 0x29, 0x73, 0x50, 0x59, 0x14,
+ 0x02, 0x21, 0x16, 0x8f, 0x0f, 0xdf, 0xa5, 0xf0,
+};
+
+static struct crc_test {
+ uint32_t crc; /* random starting crc */
+ uint32_t start; /* random offset in buf */
+ uint32_t length; /* random length of test */
+ uint32_t crc32c_le; /* expected crc32c_le result */
+ uint32_t crc32_be; /* expected crc32_be result */
+} test[] = {
+ {0xffffffff, 0x00000000, 0x00001000, 0x13934bef, 0xd8ddcdc3},
+ {0xfe7328ea, 0x00000763, 0x00000717, 0xed2c0d70, 0xc863aef8},
+ {0x4c40684e, 0x00000721, 0x0000011e, 0xd7f46ccc, 0x173a11c4},
+ {0x6b487f90, 0x00000264, 0x000007bc, 0x759e9939, 0xd6307c56},
+ {0x9f5810db, 0x00000afa, 0x00000255, 0x2685197f, 0x2e5c9201},
+ {0xb15c4755, 0x00000d5b, 0x000002a4, 0xd8fadcb5, 0xf682c4be},
+ {0x06518253, 0x00000ffb, 0x00000004, 0xabee2433, 0x3d8abdf9},
+ {0xd9e71c55, 0x00000a2a, 0x00000259, 0x96682af2, 0x47b4d26c},
+ {0x0c1ae843, 0x00000ce4, 0x0000031b, 0x7b637c43, 0x62b47e8b},
+ {0xec3cd517, 0x000002ff, 0x00000566, 0x5d719a77, 0xff5bc5b7},
+ {0x77828e95, 0x0000067f, 0x0000038f, 0x43ee5b6c, 0x1a0cfacd},
+ {0xec87b4e3, 0x00000d1c, 0x000002e3, 0x2ddd2eee, 0x275118a7},
+ {0x412158bb, 0x00000eee, 0x00000111, 0x67b38ba2, 0xa74ecff5},
+ {0x2e52de3e, 0x00000c4a, 0x000003b5, 0xbcc5d61d, 0xbd800707},
+ {0x6ddaae8b, 0x00000d99, 0x00000266, 0x8b535544, 0xecbde1a1},
+ {0x049b6cb1, 0x000009c5, 0x000000b0, 0xfc22cabc, 0xfb78eb9f},
+ {0x77d4b954, 0x0000028a, 0x000007fa, 0x71d00923, 0x8c116f85},
+ {0x5e192355, 0x00000ac1, 0x000001fa, 0xb966b81a, 0x5aa17bbe},
+ {0x7d80b71d, 0x00000213, 0x000001e0, 0x2bba371a, 0xb5906aa6},
+ {0x01f6f1e4, 0x000001d6, 0x00000395, 0xb7e8a647, 0x3ad112b1},
+ {0x1dfabb13, 0x00000e14, 0x000001eb, 0x53917fba, 0xbaee0339},
+ {0xb00a4449, 0x00000bf6, 0x00000409, 0xedecb577, 0x6f3a3979},
+ {0x7ecd3981, 0x0000083f, 0x0000016b, 0xefef62b9, 0xe3e52eed},
+ {0xf8f330d2, 0x000004be, 0x00000757, 0x9357c9f3, 0x0835bc1b},
+ {0x03c38af2, 0x00000d23, 0x000002dc, 0x360fa8c0, 0x2ca885e6},
+ {0x687bb79b, 0x00000f3d, 0x000000c2, 0x448d3be2, 0x79be2f78},
+ {0x6710f550, 0x000009e9, 0x00000603, 0xdbfd1998, 0x1d25f627},
+ {0x873171d1, 0x00000787, 0x000004d5, 0xab7f1b62, 0xa76a5656},
+ {0x373b1314, 0x00000f0f, 0x000000f0, 0x184098ab, 0xba273974},
+ {0x90fad9cd, 0x00000ead, 0x00000152, 0x23ce52ff, 0xb7bc958c},
+ {0x19676fe7, 0x0000007d, 0x0000070d, 0xf8a76f1e, 0xf882b644},
+ {0x89facd45, 0x000005f3, 0x00000473, 0x4331a006, 0xe9dc1396},
+ {0x6f173747, 0x00000fc3, 0x0000003c, 0xb012f08e, 0xc6b888ee},
+ {0x4b44a106, 0x0000075a, 0x0000008b, 0xf6f7ac38, 0x60cd2b74},
+ {0xb620ad06, 0x00000774, 0x0000017e, 0xd34558e6, 0x3a0a615b},
+ {0x976f21e9, 0x000008d7, 0x0000034a, 0xe533aa3a, 0xa99e60be},
+ {0x687628c0, 0x000006c5, 0x0000061b, 0x3a840b15, 0x9bfcaef2},
+ {0xe24ac108, 0x00000cd0, 0x0000032f, 0x51010ae8, 0x20958672},
+ {0x361c44a3, 0x00000304, 0x00000719, 0xfd7bd481, 0xd70ff2b2},
+ {0xd93ff95e, 0x00000db7, 0x0000008e, 0xcfbbc304, 0xad716acd},
+ {0xed752d12, 0x00000883, 0x00000091, 0x65a6c868, 0x95c71c7b},
+ {0xb4ff4b54, 0x000003d3, 0x000001c1, 0xf82597e7, 0x44b7f99b},
+ {0x111b520f, 0x00000708, 0x000000eb, 0xc3e109f3, 0x71bc01ee},
+ {0x62c806f2, 0x00000ba3, 0x0000045c, 0x874d3a72, 0xc539b753},
+ {0x40d97470, 0x000005e1, 0x0000058d, 0x87a9684f, 0xea6073a5},
+ {0x4312179c, 0x00000056, 0x0000070e, 0x809a00f5, 0x209aea3b},
+ {0x13d5f84c, 0x00000a2d, 0x00000104, 0xf3d27578, 0xe087a8b6},
+ {0x1f302cb2, 0x00000151, 0x00000014, 0x1e162693, 0x95e4b90e},
+ {0xe491db24, 0x00000600, 0x000006f6, 0x7ff09615, 0x77611523},
+ {0xf9a98069, 0x000002ba, 0x000002ad, 0x01af7387, 0xea925faa},
+ {0xe9c477ad, 0x0000015f, 0x00000778, 0x6facf9a0, 0x1130f736},
+ {0x353f32b2, 0x0000087c, 0x00000783, 0x6cc964ea, 0x32459994},
+ {0x78e1b24f, 0x00000650, 0x000006a8, 0xb3bb7c27, 0x5a632f78},
+ {0x61aa400e, 0x00000049, 0x00000254, 0xb8cd1681, 0xdf2652d5},
+ {0xb84b10b0, 0x00000f73, 0x0000008c, 0x406a6450, 0x3619d31b},
+ {0x9fa99c9c, 0x00000a7c, 0x000004d7, 0xfb3d21b4, 0xea31c743},
+ {0x3fc9ebe3, 0x00000cd9, 0x000000d6, 0x43803f9c, 0x1f76a809},
+ {0x529879cd, 0x000002f2, 0x00000595, 0x78b4c6a6, 0x63b9b93f},
+ {0x3a933019, 0x00000516, 0x00000266, 0xdcb45436, 0x8f99c98c},
+ {0x887b4977, 0x00000227, 0x0000038d, 0xc5f7c3d9, 0xaf5e3091},
+ {0x770745de, 0x000008c6, 0x00000739, 0xf69145e8, 0x53d0dce1},
+ {0x28be3b47, 0x00000c46, 0x0000032b, 0x764c028f, 0x106d0905},
+ {0x5013a050, 0x00000cf6, 0x00000309, 0xea8fe164, 0x62180b57},
+ {0x2ec4c9ba, 0x000006e8, 0x0000078d, 0xa35557a9, 0xf44430a4},
+ {0xa9f950c9, 0x00000d33, 0x000002cc, 0x41ea8618, 0x587b4eb3},
+ {0x5b520229, 0x000007b2, 0x00000484, 0x44569f1f, 0x92406c32},
+ {0xd8dcbbfc, 0x0000002f, 0x0000048c, 0xdb88ab8b, 0x13bfe70e},
+ {0x25529792, 0x00000d1d, 0x000002e2, 0x20cda404, 0x19d3b4e4},
+ {0x9f3f6d71, 0x00000238, 0x0000079a, 0x0720443e, 0x3c107021},
+ {0x64121215, 0x000007ff, 0x0000038f, 0x6aacff2c, 0xb82fdc3e},
+ {0xfb6cdde0, 0x00000ef8, 0x00000107, 0xbd43a0f1, 0xab0d3c1d},
+ {0x221c9d6f, 0x000007b6, 0x0000014f, 0xb67f834b, 0x1371ad05},
+ {0x030e1de4, 0x00000836, 0x000004b4, 0x0d67d26a, 0xe2e72df1},
+ {0xb56fa6cf, 0x00000c07, 0x000003f8, 0x60601ac1, 0x039de73e},
+ {0xb55c89f5, 0x0000098e, 0x000001d4, 0x2400efbe, 0xfe39a2bb},
+ {0x5e90b6d5, 0x0000070b, 0x000003ea, 0x3bb5d6ea, 0xf0f794a0},
+ {0x2a7045ae, 0x00000961, 0x00000633, 0xfca89e4b, 0xe66ce41c},
+ {0x8b374ea9, 0x000006ba, 0x00000780, 0xbce036ed, 0x4cb28ef7},
+ {0x8bd90bc9, 0x00000562, 0x00000369, 0xcb26a24b, 0x40236d1d},
+ {0x5b1b1762, 0x000000fd, 0x0000051a, 0x33cdda07, 0xc32e420a},
+ {0xa4153555, 0x0000058f, 0x000005c7, 0xbe50eeca, 0x83a67f35},
+ {0x0be1f931, 0x00000651, 0x00000672, 0x95a25753, 0x88f1aac1},
+ {0xb7e78618, 0x00000a7f, 0x000002bb, 0xe06bcc1c, 0x74274f66},
+ {0x4a9bc41b, 0x00000e51, 0x000001ae, 0x709e8d2c, 0x54eff534},
+ {0xfc359d13, 0x00000440, 0x000002f8, 0x0a58451f, 0x55e9363f},
+ {0x5aa48619, 0x000006d1, 0x00000284, 0x928ead83, 0x31041c06},
+ {0xa609afa8, 0x0000053e, 0x00000272, 0xb048c141, 0x4704efba},
+ {0x3f108afb, 0x00000949, 0x00000150, 0x9a6bb5bc, 0x4e4430c8},
+ {0x79bec2d3, 0x000008ed, 0x00000712, 0x32692d57, 0x11d52a7b},
+ {0x9429e067, 0x00000bc3, 0x0000043c, 0x5295ceff, 0x04640f4d},
+ {0xae58b96a, 0x0000082d, 0x000007d2, 0xc2a681ba, 0xf7ca4a2c},
+ {0x95df24be, 0x00000985, 0x000004c1, 0x3a287765, 0x2c4af003},
+ {0x5e94976f, 0x00000596, 0x000004ed, 0xff00c489, 0x5ae11687},
+ {0xf5e5f1de, 0x00000d31, 0x000002ce, 0x35f28e91, 0x30d47957},
+ {0xa2c219cf, 0x00000a3c, 0x00000374, 0x707d21eb, 0x2a14a255},
+ {0xf21b6ceb, 0x00000919, 0x00000135, 0x0847fb8b, 0xcb8d3b93},
+ {0xaa988728, 0x00000787, 0x00000771, 0x885aeaa4, 0x6531b509},
+ {0xaa5dfaac, 0x000003e5, 0x0000051b, 0x52c48ab7, 0xe43cc5e9},
+ {0x0a053968, 0x00000d2a, 0x000002d5, 0x7a90256d, 0x8004765c},
+ {0x1421dc20, 0x00000eef, 0x00000110, 0x97d6da24, 0x1378f6ff},
+ {0xb47c2166, 0x00000a6a, 0x00000209, 0xcfd6cc52, 0x676e14a5},
+ {0x77dd1955, 0x000000de, 0x00000266, 0xba74bcaa, 0xc71b429c},
+ {0x68a03cc2, 0x0000082f, 0x000007b0, 0x752bd5d8, 0x19ed14aa},
+ {0x0226b0a3, 0x00000a5f, 0x000005a0, 0x82de4970, 0xf654d3ed},
+ {0x637bf3b1, 0x00000d93, 0x0000026c, 0x5c7115cb, 0x3cccb57e},
+ {0x3b120edf, 0x00000c13, 0x000003ec, 0x80d7d20f, 0x92132798},
+ {0xe2456780, 0x000002eb, 0x00000641, 0xc0a5d289, 0x6160c87a},
+ {0x9b2e7125, 0x00000c0c, 0x000003f3, 0xcc15f57e, 0x6f00f637},
+ {0x153033ef, 0x00000787, 0x000006b6, 0x3cde443b, 0xb46caa6e},
+ {0x18458b3f, 0x0000066c, 0x00000561, 0x9a2bd8c6, 0xb6c29121},
+ {0x4ff9d4b9, 0x00000c8f, 0x0000033a, 0xd0ee6d6d, 0xc81cf380},
+ {0xdf84b5d9, 0x00000802, 0x0000029a, 0xdab0d74a, 0xb2464559},
+ {0x81ee15df, 0x000003ce, 0x00000725, 0x9942e2de, 0x4ccf571b},
+ {0x5c768e04, 0x00000afd, 0x00000160, 0x36110831, 0xae0b305a},
+ {0xe5e18094, 0x00000b4b, 0x000000a0, 0xffa3e4a7, 0x6c8a4f09},
+ {0xed7263b6, 0x00000d0d, 0x000002f2, 0xb0006a35, 0x7e04af8c},
+ {0x5bfde7d7, 0x000006fb, 0x00000554, 0xa4193b76, 0xb3a91d12},
+ {0x67f4a743, 0x00000b85, 0x0000047a, 0xf05c8d8f, 0xfb472fdf},
+ {0xf13bdf22, 0x00000ff7, 0x00000008, 0x816351eb, 0xf347f235},
+ {0x08ecc608, 0x00000d5d, 0x00000098, 0x90492772, 0x0b7f1521},
+ {0x296f52ba, 0x000004f9, 0x00000788, 0x5e5a4896, 0x1cc67088},
+ {0xbe4624c2, 0x00000427, 0x000004ef, 0xcd267b94, 0x550caefd},
+ {0x906f7c7c, 0x00000a05, 0x0000003f, 0x03fcfc33, 0x9ed82a02},
+ {0x8f7b323e, 0x00000458, 0x000004c7, 0xcd4969c8, 0x633c38a8},
+ {0x88d6593d, 0x00000597, 0x000005b5, 0xf199cd3b, 0x0491452f},
+ {0x978a7768, 0x00000268, 0x000001d3, 0xb28c95bd, 0x1a42fe61},
+ {0x857a621e, 0x000007a7, 0x000003a8, 0xf4bf84ab, 0xcd0694c6},
+ {0xb0e121ef, 0x000005be, 0x00000644, 0x28747c14, 0xf0510c72},
+ {0, 0, 0, 0, 0},
+};
+
+static int test_crc32c(void)
+{
+ struct crc_test *t = test;
+ int failures = 0;
+
+ while (t->length) {
+ uint32_t be, le;
+ le = ext2fs_crc32c_le(t->crc, test_buf + t->start, t->length);
+ be = ext2fs_crc32_be(t->crc, test_buf + t->start, t->length);
+ if (le != t->crc32c_le) {
+ printf("Test %d LE fails, %x != %x\n",
+ (int) (t - test), le, t->crc32c_le);
+ failures++;
+ }
+ if (be != t->crc32_be) {
+ printf("Test %d BE fails, %x != %x\n",
+ (int) (t - test), be, t->crc32_be);
+ failures++;
+ }
+ t++;
+ }
+
+ return failures;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ret = test_crc32c();
+ if (!ret)
+ printf("No failures.\n");
+
+ return ret;
+}
+#endif /* UNITTEST */
diff --git a/lib/ext2fs/crc32c_defs.h b/lib/ext2fs/crc32c_defs.h
new file mode 100644
index 0000000..3f9a09e
--- /dev/null
+++ b/lib/ext2fs/crc32c_defs.h
@@ -0,0 +1,59 @@
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * 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+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+#define CRCPOLY_BE 0x04c11db7
+
+/*
+ * This is the CRC32c polynomial, as outlined by Castagnoli.
+ * x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+x^11+x^10+x^9+
+ * x^8+x^6+x^0
+ */
+#define CRC32C_POLY_LE 0x82F63B78
+#define CRC32C_POLY_BE 0x1EDC6F41
+
+/* How many bits at a time to use. Valid values are 1, 2, 4, 8, 32 and 64. */
+/* For less performance-sensitive, use 4 */
+#ifndef CRC_LE_BITS
+# define CRC_LE_BITS 64
+#endif
+#ifndef CRC_BE_BITS
+# define CRC_BE_BITS 64
+#endif
+
+/*
+ * Little-endian CRC computation. Used with serial bit streams sent
+ * lsbit-first. Be sure to use cpu_to_le32() to append the computed CRC.
+ */
+#if CRC_LE_BITS > 64 || CRC_LE_BITS < 1 || CRC_LE_BITS == 16 || \
+ CRC_LE_BITS & CRC_LE_BITS-1
+# error "CRC_LE_BITS must be one of {1, 2, 4, 8, 32, 64}"
+#endif
+
+/*
+ * Big-endian CRC computation. Used with serial bit streams sent
+ * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC.
+ */
+#if CRC_BE_BITS > 64 || CRC_BE_BITS < 1 || CRC_BE_BITS == 16 || \
+ CRC_BE_BITS & CRC_BE_BITS-1
+# error "CRC_BE_BITS must be one of {1, 2, 4, 8, 32, 64}"
+#endif
+
+
+#define ___constant_swab32(x) \
+ ((uint32_t)( \
+ (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
+
+
+#if (__GNUC__ >= 3)
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
diff --git a/lib/ext2fs/csum.c b/lib/ext2fs/csum.c
new file mode 100644
index 0000000..da32d94
--- /dev/null
+++ b/lib/ext2fs/csum.c
@@ -0,0 +1,1011 @@
+/*
+ * csum.c --- checksumming of ext3 structures
+ *
+ * Copyright (C) 2006 Cluster File Systems, Inc.
+ * Copyright (C) 2006, 2007 by Andreas Dilger <adilger@clusterfs.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "crc16.h"
+#include <assert.h>
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifdef DEBUG
+#define STATIC
+#else
+#define STATIC static
+#endif
+
+void ext2fs_init_csum_seed(ext2_filsys fs)
+{
+ if (ext2fs_has_feature_csum_seed(fs->super))
+ fs->csum_seed = fs->super->s_checksum_seed;
+ else if (ext2fs_has_feature_metadata_csum(fs->super) ||
+ ext2fs_has_feature_ea_inode(fs->super))
+ fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
+ sizeof(fs->super->s_uuid));
+}
+
+static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ int offset = offsetof(struct mmp_struct, mmp_checksum);
+
+ return ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)mmp, offset);
+}
+
+int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ __u32 calculated;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+
+ calculated = ext2fs_mmp_csum(fs, mmp);
+
+ return ext2fs_le32_to_cpu(mmp->mmp_checksum) == calculated;
+}
+
+errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp)
+{
+ __u32 crc;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ crc = ext2fs_mmp_csum(fs, mmp);
+ mmp->mmp_checksum = ext2fs_cpu_to_le32(crc);
+
+ return 0;
+}
+
+int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+
+ return sb->s_checksum_type == EXT2_CRC32C_CHKSUM;
+}
+
+static __u32 ext2fs_superblock_csum(ext2_filsys fs EXT2FS_ATTR((unused)),
+ struct ext2_super_block *sb)
+{
+ int offset = offsetof(struct ext2_super_block, s_checksum);
+
+ return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset);
+}
+
+/* NOTE: The input to this function MUST be in LE order */
+int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb)
+{
+ __u32 flag, calculated;
+
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES)
+ flag = EXT4_FEATURE_RO_COMPAT_METADATA_CSUM;
+ else
+ flag = ext2fs_cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, flag))
+ return 1;
+
+ calculated = ext2fs_superblock_csum(fs, sb);
+
+ return ext2fs_le32_to_cpu(sb->s_checksum) == calculated;
+}
+
+/* NOTE: The input to this function MUST be in LE order */
+errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+ struct ext2_super_block *sb)
+{
+ __u32 flag, crc;
+
+ if (fs->flags & EXT2_FLAG_SWAP_BYTES)
+ flag = EXT4_FEATURE_RO_COMPAT_METADATA_CSUM;
+ else
+ flag = ext2fs_cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
+
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, flag))
+ return 0;
+
+ crc = ext2fs_superblock_csum(fs, sb);
+ sb->s_checksum = ext2fs_cpu_to_le32(crc);
+
+ return 0;
+}
+
+static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs,
+ ext2_ino_t inum EXT2FS_ATTR((unused)),
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr,
+ __u32 *crc)
+{
+ char *buf = (char *)hdr;
+ __u32 old_crc = hdr->h_checksum;
+
+ hdr->h_checksum = 0;
+ block = ext2fs_cpu_to_le64(block);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block,
+ sizeof(block));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize);
+ hdr->h_checksum = old_crc;
+
+ return 0;
+}
+
+int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ __u32 calculated;
+ errcode_t retval;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated;
+}
+
+errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr)
+{
+ errcode_t retval;
+ __u32 crc;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc);
+ if (retval)
+ return retval;
+ hdr->h_checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
+static __u16 do_nothing16(__u16 x)
+{
+ return x;
+}
+
+static __u16 disk_to_host16(__u16 x)
+{
+ return ext2fs_le16_to_cpu(x);
+}
+
+static errcode_t __get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset,
+ int need_swab)
+{
+ struct ext2_dir_entry *dp;
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_countlimit *c;
+ int count_offset, max_sane_entries;
+ unsigned int rec_len;
+ __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+ rec_len = translate(dirent->rec_len);
+
+ if (rec_len == fs->blocksize && translate(dirent->name_len) == 0)
+ count_offset = 8;
+ else if (rec_len == 12) {
+ dp = (struct ext2_dir_entry *)(((char *)dirent) + rec_len);
+ rec_len = translate(dp->rec_len);
+ if (rec_len != fs->blocksize - 12)
+ return EXT2_ET_DB_NOT_FOUND;
+ root = (struct ext2_dx_root_info *)(((char *)dp + 12));
+ if (root->reserved_zero ||
+ root->info_length != sizeof(struct ext2_dx_root_info))
+ return EXT2_ET_DB_NOT_FOUND;
+ count_offset = 32;
+ } else
+ return EXT2_ET_DB_NOT_FOUND;
+
+ c = (struct ext2_dx_countlimit *)(((char *)dirent) + count_offset);
+ max_sane_entries = (fs->blocksize - count_offset) /
+ sizeof(struct ext2_dx_entry);
+ if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries ||
+ ext2fs_le16_to_cpu(c->count) > max_sane_entries)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ if (offset)
+ *offset = count_offset;
+ if (cc)
+ *cc = c;
+
+ return 0;
+}
+
+errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset)
+{
+ return __get_dx_countlimit(fs, dirent, cc, offset, 0);
+}
+
+void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry_tail *t)
+{
+ memset(t, 0, sizeof(struct ext2_dir_entry_tail));
+ ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail),
+ (struct ext2_dir_entry *)t);
+ t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM;
+}
+
+static errcode_t __get_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry_tail **tt,
+ int need_swab)
+{
+ struct ext2_dir_entry *d;
+ void *top;
+ struct ext2_dir_entry_tail *t;
+ unsigned int rec_len;
+ errcode_t retval = 0;
+ __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16);
+
+ if (fs->blocksize < 1024)
+ return EXT2_FILSYS_CORRUPTED; /* Should never happen */
+
+ d = dirent;
+ top = EXT2_DIRENT_TAIL(dirent, fs->blocksize);
+
+ while ((void *) d < top) {
+ rec_len = translate(d->rec_len);
+ if ((rec_len < 8) || (rec_len & 0x03))
+ return EXT2_ET_DIR_CORRUPTED;
+ d = (struct ext2_dir_entry *)(((char *)d) + rec_len);
+ }
+
+ if ((char *)d > ((char *)dirent + fs->blocksize))
+ return EXT2_ET_DIR_CORRUPTED;
+ if (d != top)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ t = (struct ext2_dir_entry_tail *)d;
+ if (t->det_reserved_zero1 ||
+ translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) ||
+ translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM)
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+
+ if (tt)
+ *tt = t;
+ return retval;
+}
+
+int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent)
+{
+ return __get_dirent_tail(fs, dirent, NULL, 0) !=
+ EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+}
+
+static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent, __u32 *crc,
+ int size)
+{
+ errcode_t retval;
+ char *buf = (char *)dirent;
+ __u32 gen;
+ struct ext2_inode inode;
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+
+ return 0;
+}
+
+int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 calculated;
+ struct ext2_dir_entry_tail *t;
+
+ retval = __get_dirent_tail(fs, dirent, &t, 1);
+ if (retval)
+ return 1;
+
+ /*
+ * The checksum field is overlaid with the dirent->name field
+ * so the swapfs.c functions won't change the endianness.
+ */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated,
+ (char *)t - (char *)dirent);
+ if (retval)
+ return 0;
+ return ext2fs_le32_to_cpu(t->det_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext2_dir_entry_tail *t;
+
+ retval = __get_dirent_tail(fs, dirent, &t, 1);
+ if (retval)
+ return retval;
+
+ /* swapfs.c functions don't change the checksum endianness */
+ retval = ext2fs_dirent_csum(fs, inum, dirent, &crc,
+ (char *)t - (char *)dirent);
+ if (retval)
+ return retval;
+ t->det_checksum = ext2fs_cpu_to_le32(crc);
+ return 0;
+}
+
+errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent,
+ __u32 *crc, struct ext2_dx_tail **ret_t)
+{
+ errcode_t retval;
+ char *buf = (char *)dirent;
+ int size;
+ __u32 gen, dummy_csum = 0;
+ struct ext2_inode inode;
+ struct ext2_dx_tail *t;
+ struct ext2_dx_countlimit *c;
+ int count_offset, limit, count;
+
+ retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1);
+ if (retval)
+ return retval;
+ limit = ext2fs_le16_to_cpu(c->limit);
+ count = ext2fs_le16_to_cpu(c->count);
+ if (count_offset + (limit * sizeof(struct ext2_dx_entry)) >
+ fs->blocksize - sizeof(struct ext2_dx_tail))
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+ /* htree structs are accessed in LE order */
+ t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit);
+
+ size = count_offset + (count * sizeof(struct ext2_dx_entry));
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size);
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)t, 4);
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&dummy_csum, 4);
+
+ if (ret_t)
+ *ret_t = t;
+ return 0;
+}
+
+static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ __u32 calculated;
+ errcode_t retval;
+ struct ext2_dx_tail *t;
+
+ retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, &t);
+ if (retval)
+ return 0;
+
+ return ext2fs_le32_to_cpu(t->dt_checksum) == calculated;
+}
+
+static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ __u32 crc;
+ errcode_t retval = 0;
+ struct ext2_dx_tail *t;
+
+ retval = ext2fs_dx_csum(fs, inum, dirent, &crc, &t);
+ if (retval)
+ return retval;
+ t->dt_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
+int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+
+ if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+ return ext2fs_dirent_csum_verify(fs, inum, dirent);
+ if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+ return ext2fs_dx_csum_verify(fs, inum, dirent);
+
+ return 0;
+}
+
+errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent)
+{
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ if (__get_dirent_tail(fs, dirent, NULL, 1) == 0)
+ return ext2fs_dirent_csum_set(fs, inum, dirent);
+ if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0)
+ return ext2fs_dx_csum_set(fs, inum, dirent);
+
+ if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)
+ return 0;
+ return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
+}
+
+#define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \
+ (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max)))
+
+static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h)
+{
+ return (struct ext3_extent_tail *)(((char *)h) +
+ EXT3_EXTENT_TAIL_OFFSET(h));
+}
+
+static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh,
+ __u32 *crc)
+{
+ int size;
+ __u32 gen;
+ errcode_t retval;
+ struct ext2_inode inode;
+
+ size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail,
+ et_checksum);
+
+ retval = ext2fs_read_inode(fs, inum, &inode);
+ if (retval)
+ return retval;
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = ext2fs_cpu_to_le32(inode.i_generation);
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)eh, size);
+
+ return 0;
+}
+
+int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+
+ provided = ext2fs_le32_to_cpu(t->et_checksum);
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated);
+ if (retval)
+ return 0;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext3_extent_header *eh)
+{
+ errcode_t retval;
+ __u32 crc;
+ struct ext3_extent_tail *t = get_extent_tail(eh);
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ /*
+ * The extent tree structures are accessed in LE order, so we must
+ * swap the checksum bytes here.
+ */
+ retval = ext2fs_extent_block_csum(fs, inum, eh, &crc);
+ if (retval)
+ return retval;
+ t->et_checksum = ext2fs_cpu_to_le32(crc);
+ return retval;
+}
+
+int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+ __u32 provided, calculated;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+ provided = gdp->bg_inode_bitmap_csum_lo;
+ calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+ size);
+ if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16;
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+ gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
+ if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
+ gdp->bg_inode_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
+int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+ __u32 provided, calculated;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+ provided = gdp->bg_block_bitmap_csum_lo;
+ calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
+ size);
+ if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16;
+ else
+ calculated &= 0xFFFF;
+
+ return provided == calculated;
+}
+
+errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size)
+{
+ __u32 crc;
+ struct ext4_group_desc *gdp = (struct ext4_group_desc *)
+ ext2fs_group_desc(fs, fs->group_desc, group);
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
+ gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF;
+ if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
+ gdp->bg_block_bitmap_csum_hi = crc >> 16;
+
+ return 0;
+}
+
+static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode,
+ __u32 *crc, int has_hi)
+{
+ __u32 gen;
+ struct ext2_inode_large *desc = inode;
+ size_t size = EXT2_INODE_SIZE(fs->super);
+ __u16 old_lo;
+ __u16 old_hi = 0;
+
+ old_lo = inode->i_checksum_lo;
+ inode->i_checksum_lo = 0;
+ if (has_hi) {
+ old_hi = inode->i_checksum_hi;
+ inode->i_checksum_hi = 0;
+ }
+
+ inum = ext2fs_cpu_to_le32(inum);
+ gen = inode->i_generation;
+ *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
+ sizeof(inum));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
+ *crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size);
+
+ inode->i_checksum_lo = old_lo;
+ if (has_hi)
+ inode->i_checksum_hi = old_hi;
+ return 0;
+}
+
+int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 provided, calculated;
+ unsigned int i, has_hi;
+ char *cp;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+
+ has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+ provided = ext2fs_le16_to_cpu(inode->i_checksum_lo);
+ retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi);
+ if (retval)
+ return 0;
+ if (has_hi) {
+ __u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi);
+ provided |= hi << 16;
+ } else
+ calculated &= 0xFFFF;
+
+ if (provided == calculated)
+ return 1;
+
+ /*
+ * If the checksum didn't match, it's possible it was due to
+ * the inode being all zero's. It's unlikely this is the
+ * case, but it can happen. So check for it here. (We only
+ * check the base inode since that's good enough, and it's not
+ * worth the bother to figure out how much of the extended
+ * inode, if any, is present.)
+ */
+ for (cp = (char *) inode, i = 0;
+ i < sizeof(struct ext2_inode);
+ cp++, i++)
+ if (*cp)
+ return 0;
+ return 1; /* Inode must have been all zero's */
+}
+
+errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode)
+{
+ errcode_t retval;
+ __u32 crc;
+ int has_hi;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
+ inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
+
+ retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi);
+ if (retval)
+ return retval;
+ inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF);
+ if (has_hi)
+ inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16);
+ return 0;
+}
+
+__u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
+{
+ struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc,
+ group);
+ size_t offset, size = EXT2_DESC_SIZE(fs->super);
+ __u16 crc = 0;
+#ifdef WORDS_BIGENDIAN
+ struct ext4_group_desc swabdesc;
+ size_t save_size = size;
+ const size_t ext4_bg_size = sizeof(struct ext4_group_desc);
+ struct ext2_group_desc *save_desc = desc;
+
+ /* Have to swab back to little-endian to do the checksum */
+ if (size > ext4_bg_size)
+ size = ext4_bg_size;
+ 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
+
+ if (ext2fs_has_feature_metadata_csum(fs->super)) {
+ /* new metadata csum code */
+ __u16 old_crc;
+ __u32 crc32;
+
+ old_crc = desc->bg_checksum;
+ desc->bg_checksum = 0;
+ crc32 = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&group,
+ sizeof(group));
+ crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc,
+ size);
+ desc->bg_checksum = old_crc;
+#ifdef WORDS_BIGENDIAN
+ if (save_size > ext4_bg_size)
+ crc32 = ext2fs_crc32c_le(crc32,
+ (unsigned char *)save_desc + ext4_bg_size,
+ save_size - ext4_bg_size);
+#endif
+ crc = crc32 & 0xFFFF;
+ goto out;
+ }
+
+ /* old crc16 code */
+ offset = offsetof(struct ext2_group_desc, bg_checksum);
+ 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);
+ }
+#ifdef WORDS_BIGENDIAN
+ /*
+ * If the size of the bg descriptor is greater than 64
+ * bytes, which is the size of the traditional ext4 bg
+ * descriptor, checksum the rest of the descriptor here
+ */
+ if (save_size > ext4_bg_size)
+ crc = ext2fs_crc16(crc, (char *)save_desc + ext4_bg_size,
+ save_size - ext4_bg_size);
+#endif
+
+out:
+ return crc;
+}
+
+int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group)
+{
+ if (ext2fs_has_group_desc_csum(fs) &&
+ (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 (!ext2fs_has_group_desc_csum(fs))
+ 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 (!ext2fs_has_group_desc_csum(fs))
+ return 0;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ __u32 old_csum = ext2fs_bg_checksum(fs, i);
+ __u32 old_unused = ext2fs_bg_itable_unused(fs, i);
+ __u32 old_flags = ext2fs_bg_flags(fs, i);
+ __u32 old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i);
+ __u32 old_free_blocks_count = ext2fs_bg_free_blocks_count(fs, i);
+
+ if (old_free_blocks_count == sb->s_blocks_per_group &&
+ i != fs->group_desc_count - 1)
+ ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT);
+
+ 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 = EXT2_DESC_SIZE(fs->super);
+ struct ext2_super_block *sb = fs->super;
+ int offset = offsetof(struct ext2_group_desc, bg_checksum);
+#ifdef WORDS_BIGENDIAN
+ struct ext4_group_desc swabdesc;
+ struct ext2_group_desc *save_desc = desc;
+ const size_t ext4_bg_size = sizeof(struct ext4_group_desc);
+ size_t save_size = size;
+#endif
+
+#ifdef WORDS_BIGENDIAN
+ /* Have to swab back to little-endian to do the checksum */
+ if (size > ext4_bg_size)
+ size = ext4_bg_size;
+ 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);
+#ifdef WORDS_BIGENDIAN
+ if (save_size > ext4_bg_size)
+ crc3 = ext2fs_crc16(crc3, (char *)save_desc + ext4_bg_size,
+ save_size - ext4_bg_size);
+#endif
+
+ 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(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, 32768);
+#if 0
+ param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
+ param.s_desc_size = 128;
+ csum_known = 0x5b6e;
+#endif
+
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ 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);
+ }
+ ext2fs_free(fs);
+
+ return 0;
+}
+#endif
diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
new file mode 100644
index 0000000..bbdb221
--- /dev/null
+++ b/lib/ext2fs/dblist.c
@@ -0,0 +1,403 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#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);
+
+/*
+ * 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 = NULL;
+ 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)
+ goto cleanup;
+ 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 = old_size / sizeof(struct ext2_db_entry2);
+ 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_iterate3(ext2_dblist dblist,
+ int (*func)(ext2_filsys fs,
+ struct ext2_db_entry2 *db_info,
+ void *priv_data),
+ unsigned long long start,
+ unsigned long long count,
+ void *priv_data)
+{
+ unsigned long long i, end;
+ int ret;
+
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+ end = start + count;
+ if (!dblist->sorted)
+ ext2fs_dblist_sort2(dblist, 0);
+ if (end > dblist->count)
+ end = dblist->count;
+ for (i = start; i < end; i++) {
+ ret = (*func)(dblist->fs, &dblist->list[i], priv_data);
+ if (ret & DBLIST_ABORT)
+ return 0;
+ }
+ return 0;
+}
+
+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)
+{
+ return ext2fs_dblist_iterate3(dblist, func, 0, dblist->count,
+ priv_data);
+}
+
+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)
+{
+ static struct ext2_db_entry ret_entry;
+ struct ext2_db_entry2 *last;
+
+ EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+ 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/ext2fs/dblist_dir.c b/lib/ext2fs/dblist_dir.c
new file mode 100644
index 0000000..864a3ca
--- /dev/null
+++ b/lib/ext2fs/dblist_dir.c
@@ -0,0 +1,88 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#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 ext2_inode inode;
+ struct dir_context *ctx;
+ int ret;
+
+ ctx = (struct dir_context *) priv_data;
+ ctx->dir = db_info->ino;
+ ctx->errcode = 0;
+
+ ctx->errcode = ext2fs_read_inode(fs, ctx->dir, &inode);
+ if (ctx->errcode)
+ return DBLIST_ABORT;
+ if (inode.i_flags & EXT4_INLINE_DATA_FL)
+ ret = ext2fs_inline_data_dir_iterate(fs, ctx->dir, ctx);
+ else
+ 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/ext2fs/digest_encode.c b/lib/ext2fs/digest_encode.c
new file mode 100644
index 0000000..075963f
--- /dev/null
+++ b/lib/ext2fs/digest_encode.c
@@ -0,0 +1,187 @@
+/*
+ * lib/ext2fs/digest_encode.c
+ *
+ * A function to encode a digest using 64 characters that are valid in a
+ * filename per ext2fs rules.
+ *
+ * Written by Uday Savagaonkar, 2014.
+ *
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "ext2fs.h"
+
+static const char *lookup_table =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+/**
+ * ext2fs_digest_encode() -
+ *
+ * Encodes the input digest using characters from the set [a-zA-Z0-9_+].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ */
+int ext2fs_digest_encode(const char *src, int len, char *dst)
+{
+ int i = 0, bits = 0, ac = 0;
+ char *cp = dst;
+
+ while (i < len) {
+ ac += (((unsigned char) src[i]) << bits);
+ bits += 8;
+ do {
+ *cp++ = lookup_table[ac & 0x3f];
+ ac >>= 6;
+ bits -= 6;
+ } while (bits >= 6);
+ i++;
+ }
+ if (bits)
+ *cp++ = lookup_table[ac & 0x3f];
+ return cp - dst;
+}
+
+int ext2fs_digest_decode(const char *src, int len, char *dst)
+{
+ int i = 0, bits = 0, ac = 0;
+ const char *p;
+ char *cp = dst;
+
+ while (i < len) {
+ p = strchr(lookup_table, src[i]);
+ if (p == NULL || src[i] == 0)
+ return -1;
+ ac += (p - lookup_table) << bits;
+ bits += 6;
+ if (bits >= 8) {
+ *cp++ = ac & 0xff;
+ ac >>= 8;
+ bits -= 8;
+ }
+ i++;
+ }
+ if (ac)
+ return -1;
+ return cp - dst;
+}
+
+
+#ifdef UNITTEST
+static const struct {
+ unsigned char d[32];
+ unsigned int len;
+ const char *ed;
+} tests[] = {
+ { { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+ 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+ 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 }, 32,
+ "jDLxChJ,cQhm7TPyZ+WukcirBROZbOJTkWZmbgnU4WF"
+ },
+ { { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }, 32,
+ "6inF,+YAPreQBBk3d5qIjA7AhNqlXoHn0Cx,hJPAV0K"
+ },
+ { { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }, 32,
+ "k0oahJtB4gb5AbykM4DY5MKPknFZ,HyZ2ze7Unx2GEM"
+ },
+ { { 0x00, }, 1,
+ "AA"
+ },
+ { { 0x01, }, 1,
+ "BA"
+ },
+ { { 0x01, 0x02 }, 2,
+ "BIA"
+ },
+ { { 0x01, 0x02, 0x03 }, 3,
+ "BIwA"
+ },
+ { { 0x01, 0x02, 0x03, 0x04 }, 4,
+ "BIwAEA"
+ },
+ { { 0x01, 0x02, 0x03, 0x04, 0xff }, 5,
+ "BIwAE8P"
+ },
+ { { 0x01, 0x02, 0x03, 0x04, 0xff, 0xfe }, 6,
+ "BIwAE8v,"
+ },
+ { { 0x01, 0x02, 0x03, 0x04, 0xff, 0xfe, 0xfd }, 7,
+ "BIwAE8v,9D"
+ },
+};
+
+int main(int argc, char **argv)
+{
+ int i, ret, len;
+ int errors = 0;
+ char tmp[1024], tmp2[1024];
+
+ if (argc == 3 && !strcmp(argv[1], "encode")) {
+ memset(tmp, 0, sizeof(tmp));
+ ext2fs_digest_encode(argv[2], strlen(argv[2]), tmp);
+ puts(tmp);
+ exit(0);
+ }
+ if (argc == 3 && !strcmp(argv[1], "decode")) {
+ memset(tmp, 0, sizeof(tmp));
+ ret = ext2fs_digest_decode(argv[2], strlen(argv[2]), tmp);
+ puts(tmp);
+ fprintf(stderr, "returned %d\n", ret);
+ exit(0);
+ }
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ memset(tmp, 0, sizeof(tmp));
+ ret = ext2fs_digest_encode((const char *) tests[i].d,
+ tests[i].len, tmp);
+ len = strlen(tmp);
+ printf("Test Digest %d (returned %d): ", i, ret);
+ if (ret != len) {
+ printf("FAILED returned %d, string length was %d\n",
+ ret, len);
+ errors++;
+ continue;
+ } else if (strcmp(tmp, tests[i].ed) != 0) {
+ printf("FAILED: got %s, expected %s\n", tmp,
+ tests[i].ed);
+ errors++;
+ continue;
+ }
+ ret = ext2fs_digest_decode(tmp, len, tmp2);
+ if (ret != tests[i].len) {
+ printf("FAILED decode returned %d, expected %d\n",
+ ret, tests[i].len);
+ errors++;
+ continue;
+ }
+ if (memcmp(tmp2, tests[i].d, ret) != 0) {
+ puts("FAILED: decode mismatched");
+ errors++;
+ continue;
+ }
+ printf("OK\n");
+ }
+ for (i = 1; i < argc; i++) {
+ memset(tmp, 0, sizeof(tmp));
+ ret = ext2fs_digest_encode(argv[i], strlen(argv[i]), tmp);
+ len = strlen(tmp);
+ printf("Digest of '%s' is '%s' (returned %d, length %d)\n",
+ argv[i], tmp, ret, len);
+ }
+ return errors;
+}
+
+#endif /* UNITTEST */
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
new file mode 100644
index 0000000..7798a48
--- /dev/null
+++ b/lib/ext2fs/dir_iterate.c
@@ -0,0 +1,315 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#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) ||
+ ((ext2fs_dirent_name_len(dirent)+8) > (int) 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 == EXT2_ET_INLINE_DATA_CANT_ITERATE) {
+ (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx);
+ retval = 0;
+ }
+ 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);
+}
+
+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, buflen;
+ int entry;
+ struct ext2_dir_entry *dirent;
+ int csum_size = 0;
+ int inline_data;
+ errcode_t retval = 0;
+
+ if (blockcnt < 0)
+ return 0;
+
+ entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
+
+ /* If a dir has inline data, we don't need to read block */
+ inline_data = !!(ctx->flags & DIRENT_FLAG_INCLUDE_INLINE_DATA);
+ if (!inline_data) {
+ ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+ ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ /* If we handle a normal dir, we traverse the entire block */
+ buflen = fs->blocksize;
+ } else {
+ buflen = ctx->buflen;
+ }
+
+ if (ext2fs_has_feature_metadata_csum(fs->super))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ if (buflen < 8) {
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ return BLOCK_ABORT;
+ }
+ while (offset < buflen - 8) {
+ dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
+ if (ext2fs_get_rec_len(fs, dirent, &rec_len))
+ return BLOCK_ABORT;
+ if (((offset + rec_len) > buflen) ||
+ (rec_len < 8) ||
+ ((rec_len % 4) != 0) ||
+ ((ext2fs_dirent_name_len(dirent)+8) > (int) rec_len)) {
+ ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+ return BLOCK_ABORT;
+ }
+ if (!dirent->inode) {
+ /*
+ * We just need to check metadata_csum when this
+ * dir hasn't inline data. That means that 'buflen'
+ * should be blocksize.
+ */
+ if (!inline_data &&
+ (offset == buflen - csum_size) &&
+ (dirent->rec_len == csum_size) &&
+ (dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) {
+ if (!(ctx->flags & DIRENT_FLAG_INCLUDE_CSUM))
+ goto next;
+ entry = DIRENT_CHECKSUM;
+ } else if (!(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
+ goto next;
+ }
+
+ ret = (ctx->func)(ctx->dir,
+ (next_real_entry > offset) ?
+ DIRENT_DELETED_FILE : entry,
+ dirent, offset,
+ buflen, 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 = (ext2fs_dirent_name_len(dirent) + 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) {
+ if (!inline_data) {
+ ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr,
+ ctx->buf,
+ 0, ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ } else {
+ /*
+ * return BLOCK_INLINE_DATA_CHANGED to notify caller
+ * that inline data has been changed.
+ */
+ retval = BLOCK_INLINE_DATA_CHANGED;
+ }
+ }
+ if (do_abort)
+ return retval | BLOCK_ABORT;
+ return retval;
+}
diff --git a/lib/ext2fs/dirblock.c b/lib/ext2fs/dirblock.c
new file mode 100644
index 0000000..54b2777
--- /dev/null
+++ b/lib/ext2fs/dirblock.c
@@ -0,0 +1,113 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)),
+ ext2_ino_t ino)
+{
+ errcode_t retval;
+ int corrupt = 0;
+
+ retval = io_channel_read_blk64(fs->io, block, 1, buf);
+ if (retval)
+ return retval;
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_dir_block_csum_verify(fs, ino,
+ (struct ext2_dir_entry *)buf))
+ corrupt = 1;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in(fs, buf, flags);
+#endif
+ if (!retval && corrupt)
+ retval = EXT2_ET_DIR_CSUM_INVALID;
+ return retval;
+}
+
+errcode_t ext2fs_read_dir_block3(ext2_filsys fs, blk64_t block,
+ void *buf, int flags EXT2FS_ATTR((unused)))
+{
+ return ext2fs_read_dir_block4(fs, block, buf, flags, 0);
+}
+
+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_block4(ext2_filsys fs, blk64_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)),
+ ext2_ino_t ino)
+{
+ errcode_t retval;
+ char *buf = inbuf;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_get_mem(fs->blocksize, &buf);
+ if (retval)
+ return retval;
+ memcpy(buf, inbuf, fs->blocksize);
+ retval = ext2fs_dirent_swab_out(fs, buf, flags);
+ if (retval)
+ return retval;
+#endif
+ retval = ext2fs_dir_block_csum_set(fs, ino,
+ (struct ext2_dir_entry *)buf);
+ if (retval)
+ goto out;
+
+ retval = io_channel_write_blk64(fs->io, block, 1, buf);
+
+out:
+#ifdef WORDS_BIGENDIAN
+ ext2fs_free_mem(&buf);
+#endif
+ return retval;
+}
+
+errcode_t ext2fs_write_dir_block3(ext2_filsys fs, blk64_t block,
+ void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+ return ext2fs_write_dir_block4(fs, block, inbuf, flags, 0);
+}
+
+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/ext2fs/dirhash.c b/lib/ext2fs/dirhash.c
new file mode 100644
index 0000000..42fe98b
--- /dev/null
+++ b/lib/ext2fs/dirhash.c
@@ -0,0 +1,307 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+/*
+ * 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 <jeremy@zip.com.au> 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 (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.
+ *
+ * This function doesn't do any normalization or casefolding of the
+ * input string. To take charset encoding into account, use
+ * ext2fs_dirhash2.
+ *
+ */
+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++;
+ /* fallthrough */
+ case EXT2_HASH_LEGACY:
+ hash = dx_hack_hash(name, len, unsigned_flag);
+ break;
+ case EXT2_HASH_HALF_MD4_UNSIGNED:
+ unsigned_flag++;
+ /* fallthrough */
+ 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++;
+ /* fallthrough */
+ 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;
+}
+
+/*
+ * Returns the hash of a filename considering normalization and
+ * casefolding. This is a wrapper around ext2fs_dirhash with string
+ * encoding support based on the nls_table and the flags. Check
+ * ext2fs_dirhash for documentation on the input and output parameters.
+ */
+errcode_t ext2fs_dirhash2(int version, const char *name, int len,
+ const struct ext2fs_nls_table *charset,
+ int hash_flags, const __u32 *seed,
+ ext2_dirhash_t *ret_hash,
+ ext2_dirhash_t *ret_minor_hash)
+{
+ errcode_t r;
+ int dlen;
+
+ if (len && charset && (hash_flags & EXT4_CASEFOLD_FL)) {
+ char buff[PATH_MAX];
+
+ dlen = charset->ops->casefold(charset,
+ (const unsigned char *) name, len,
+ (unsigned char *) buff, sizeof(buff));
+ if (dlen < 0) {
+ if (dlen == -EINVAL)
+ goto opaque_seq;
+
+ return dlen;
+ }
+ r = ext2fs_dirhash(version, buff, dlen, seed, ret_hash,
+ ret_minor_hash);
+ return r;
+ }
+
+opaque_seq:
+ return ext2fs_dirhash(version, name, len, seed, ret_hash,
+ ret_minor_hash);
+}
diff --git a/lib/ext2fs/dosio.c b/lib/ext2fs/dosio.c
new file mode 100644
index 0000000..d0cf269
--- /dev/null
+++ b/lib/ext2fs/dosio.c
@@ -0,0 +1,459 @@
+/*
+ * dosio.c -- Disk I/O module for the ext2fs/DOS library.
+ *
+ * Copyright (c) 1997 by Theodore Ts'o.
+ *
+ * Copyright (c) 1997 Mark Habersack
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <bios.h>
+#include <string.h>
+#include <ctype.h>
+#include <io.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <ext2fs/ext2_types.h>
+#include "utils.h"
+#include "dosio.h"
+#include "et/com_err.h"
+#include "ext2_err.h"
+#include "ext2fs/io.h"
+
+/*
+ * Some helper macros
+ */
+#define LINUX_EXT2FS 0x83
+#define LINUX_SWAP 0x82
+#define WRITE_ERR(_msg_) write(2, _msg_, strlen(_msg_))
+#define WRITE_ERR_S(_msg_) write(2, _msg_, sizeof(_msg_))
+
+/*
+ * Exported variables
+ */
+unsigned long _dio_error;
+unsigned long _dio_hw_error;
+
+/*
+ * Array of all opened partitions
+ */
+static PARTITION **partitions = NULL;
+static unsigned short npart = 0; /* Number of mapped partitions */
+static PARTITION *active = NULL;
+
+/*
+ * I/O Manager routine prototypes
+ */
+static errcode_t dos_open(const char *dev, int flags, io_channel *channel);
+static errcode_t dos_close(io_channel channel);
+static errcode_t dos_set_blksize(io_channel channel, int blksize);
+static errcode_t dos_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf);
+static errcode_t dos_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf);
+static errcode_t dos_flush(io_channel channel);
+
+static struct struct_io_manager struct_dos_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "DOS I/O Manager",
+ .open = dos_open,
+ .close = dos_close,
+ .set_blksize = dos_set_blksize,
+ .read_blk = dos_read_blk,
+ .write_blk = dos_write_blk,
+ .flush = dos_flush
+};
+
+io_manager dos_io_manager = &struct_dos_manager;
+
+/*
+ * Macro taken from unix_io.c
+ */
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+/*
+ * Calculates a CHS address of a sector from its LBA
+ * offset for the given partition.
+ */
+static void lba2chs(unsigned long lba_addr, CHS *chs, PARTITION *part)
+{
+ unsigned long abss;
+
+ chs->offset = lba_addr & 0x000001FF;
+ abss = (lba_addr >> 9) + part->start;
+ chs->cyl = abss / (part->sects * part->heads);
+ chs->head = (abss / part->sects) % part->heads;
+ chs->sector = (abss % part->sects) + 1;
+}
+
+#ifdef __TURBOC__
+#pragma argsused
+#endif
+/*
+ * Scans the passed partition table looking for *pno partition
+ * that has LINUX_EXT2FS type.
+ *
+ * TODO:
+ * For partition numbers >5 Linux uses DOS extended partitions -
+ * dive into them an return an appropriate entry. Also dive into
+ * extended partitions when scanning for a first Linux/ext2fs.
+ */
+static PTABLE_ENTRY *scan_partition_table(PTABLE_ENTRY *pentry,
+ unsigned short phys,
+ unsigned char *pno)
+{
+ unsigned i;
+
+ if(*pno != 0xFF && *pno >= 5)
+ return NULL; /* We don't support extended partitions for now */
+
+ if(*pno != 0xFF)
+ {
+ if(pentry[*pno].type == LINUX_EXT2FS)
+ return &pentry[*pno];
+ else
+ {
+ if(!pentry[*pno].type)
+ *pno = 0xFE;
+ else if(pentry[*pno].type == LINUX_SWAP)
+ *pno = 0xFD;
+ return NULL;
+ }
+ }
+
+ for(i = 0; i < 4; i++)
+ if(pentry[i].type == LINUX_EXT2FS)
+ {
+ *pno = i;
+ return &pentry[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * Allocate libext2fs structures associated with I/O manager
+ */
+static io_channel alloc_io_channel(PARTITION *part)
+{
+ io_channel ioch;
+
+ ioch = (io_channel)malloc(sizeof(struct struct_io_channel));
+ if (!ioch)
+ return NULL;
+ memset(ioch, 0, sizeof(struct struct_io_channel));
+ ioch->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+ ioch->manager = dos_io_manager;
+ ioch->name = (char *)malloc(strlen(part->dev)+1);
+ if (!ioch->name) {
+ free(ioch);
+ return NULL;
+ }
+ strcpy(ioch->name, part->dev);
+ ioch->private_data = part;
+ ioch->block_size = 1024; /* The smallest ext2fs block size */
+ ioch->read_error = 0;
+ ioch->write_error = 0;
+
+ return ioch;
+}
+
+#ifdef __TURBOC__
+#pragma argsused
+#endif
+/*
+ * Open the 'name' partition, initialize all information structures
+ * we need to keep and create libext2fs I/O manager.
+ */
+static errcode_t dos_open(const char *dev, int flags, io_channel *channel)
+{
+ unsigned char *tmp, sec[512];
+ PARTITION *part;
+ PTABLE_ENTRY *pent;
+ PARTITION **newparts;
+
+ if(!dev)
+ {
+ _dio_error = ERR_BADDEV;
+ return EXT2_ET_BAD_DEVICE_NAME;
+ }
+
+ /*
+ * First check whether the dev name is OK
+ */
+ tmp = (unsigned char*)strrchr(dev, '/');
+ if(!tmp)
+ {
+ _dio_error = ERR_BADDEV;
+ return EXT2_ET_BAD_DEVICE_NAME;
+ }
+ *tmp = 0;
+ if(strcmp(dev, "/dev"))
+ {
+ _dio_error = ERR_BADDEV;
+ return EXT2_ET_BAD_DEVICE_NAME;
+ }
+ *tmp++ = '/';
+
+ /*
+ * Check whether the partition data is already in cache
+ */
+
+ part = (PARTITION*)malloc(sizeof(PARTITION));
+ if (!part)
+ return ENOMEM;
+ {
+ int i = 0;
+
+ for(;i < npart; i++)
+ if(!strcmp(partitions[i]->dev, dev))
+ {
+ /* Found it! Make it the active one */
+ active = partitions[i];
+ *channel = alloc_io_channel(active);
+ if (!*channel)
+ return ENOMEM;
+ return 0;
+ }
+ }
+
+ /*
+ * Drive number & optionally partn number
+ */
+ switch(tmp[0])
+ {
+ case 'h':
+ case 's':
+ part->phys = 0x80;
+ part->phys += toupper(tmp[2]) - 'A';
+ /*
+ * Do we have the partition number?
+ */
+ if(tmp[3])
+ part->pno = isdigit((int)tmp[3]) ? tmp[3] - '0' - 1: 0;
+ else
+ part->pno = 0xFF;
+ break;
+
+ case 'f':
+ if(tmp[2])
+ part->phys = isdigit((int)tmp[2]) ? tmp[2] - '0' : 0;
+ else
+ part->phys = 0x00; /* We'll assume /dev/fd0 */
+ break;
+
+ default:
+ _dio_error = ERR_BADDEV;
+ return ENODEV;
+ }
+
+ if(part->phys < 0x80)
+ {
+ /* We don't support floppies for now */
+ _dio_error = ERR_NOTSUPP;
+ return EINVAL;
+ }
+
+ part->dev = strdup(dev);
+
+ /*
+ * Get drive's geometry
+ */
+ _dio_hw_error = biosdisk(DISK_GET_GEOMETRY,
+ part->phys,
+ 0, /* head */
+ 0, /* cylinder */
+ 1, /* sector */
+ 1, /* just one sector */
+ sec);
+
+ if(!HW_OK())
+ {
+ _dio_error = ERR_HARDWARE;
+ free(part->dev);
+ free(part);
+ return EFAULT;
+ }
+
+ /*
+ * Calculate the geometry
+ */
+ part->cyls = (unsigned short)(((sec[0] >> 6) << 8) + sec[1] + 1);
+ part->heads = sec[3] + 1;
+ part->sects = sec[0] & 0x3F;
+
+ /*
+ * Now that we know all we need, let's look for the partition
+ */
+ _dio_hw_error = biosdisk(DISK_READ, part->phys, 0, 0, 1, 1, sec);
+
+ if(!HW_OK())
+ {
+ _dio_error = ERR_HARDWARE;
+ free(part->dev);
+ free(part);
+ return EFAULT;
+ }
+
+ pent = (PTABLE_ENTRY*)&sec[0x1BE];
+ pent = scan_partition_table(pent, part->phys, &part->pno);
+
+ if(!pent)
+ {
+ _dio_error = part->pno == 0xFE ? ERR_EMPTYPART :
+ part->pno == 0xFD ? ERR_LINUXSWAP : ERR_NOTEXT2FS;
+ free(part->dev);
+ free(part);
+ return ENODEV;
+ }
+
+ /*
+ * Calculate the remaining figures
+ */
+ {
+ unsigned long fsec, fhead, fcyl;
+
+ fsec = (unsigned long)(pent->start_sec & 0x3F);
+ fhead = (unsigned long)pent->start_head;
+ fcyl = ((pent->start_sec >> 6) << 8) + pent->start_cyl;
+ part->start = fsec + fhead * part->sects + fcyl *
+ (part->heads * part->sects) - 1;
+ part->len = pent->size;
+ }
+
+ /*
+ * Add the partition to the table
+ */
+ newparts = (PARTITION**)realloc(partitions, sizeof(PARTITION) * npart);
+ if (!newparts) {
+ free(part);
+ return ENOMEM;
+ }
+ partitions = newparts;
+ partitions[npart++] = active = part;
+
+ /*
+ * Now alloc all libe2fs structures
+ */
+ *channel = alloc_io_channel(active);
+ if (!*channel)
+ return ENOMEM;
+
+ return 0;
+}
+
+static errcode_t dos_close(io_channel channel)
+{
+ free(channel->name);
+ free(channel);
+
+ return 0;
+}
+
+static errcode_t dos_set_blksize(io_channel channel, int blksize)
+{
+ channel->block_size = blksize;
+
+ return 0;
+}
+
+static errcode_t dos_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ PARTITION *part;
+ size_t size;
+ ext2_loff_t loc;
+ CHS chs;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ part = (PARTITION*)channel->private_data;
+
+ size = (size_t)((count < 0) ? -count : count * channel->block_size);
+ loc = (ext2_loff_t) block * channel->block_size;
+
+ lba2chs(loc, &chs, part);
+ /*
+ * Potential bug here:
+ * If DJGPP is used then reads of >18 sectors will fail!
+ * Have to rewrite biosdisk.
+ */
+ _dio_hw_error = biosdisk(DISK_READ,
+ part->phys,
+ chs.head,
+ chs.cyl,
+ chs.sector,
+ size < 512 ? 1 : size/512,
+ buf);
+
+ if(!HW_OK())
+ {
+ _dio_error = ERR_HARDWARE;
+ return EFAULT;
+ }
+
+ return 0;
+}
+
+static errcode_t dos_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ PARTITION *part;
+ size_t size;
+ ext2_loff_t loc;
+ CHS chs;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ part = (PARTITION*)channel->private_data;
+
+ if(count == 1)
+ size = (size_t)channel->block_size;
+ else
+ {
+ if (count < 0)
+ size = (size_t)-count;
+ else
+ size = (size_t)(count * channel->block_size);
+ }
+
+ loc = (ext2_loff_t)block * channel->block_size;
+ lba2chs(loc, &chs, part);
+ _dio_hw_error = biosdisk(DISK_WRITE,
+ part->phys,
+ chs.head,
+ chs.cyl,
+ chs.sector,
+ size < 512 ? 1 : size/512,
+ (void*)buf);
+
+ if(!HW_OK())
+ {
+ _dio_error = ERR_HARDWARE;
+ return EFAULT;
+ }
+
+ return 0;
+}
+
+#ifdef __TURBOC__
+#pragma argsused
+#endif
+static errcode_t dos_flush(io_channel channel)
+{
+ /*
+ * No buffers, no flush...
+ */
+ return 0;
+}
diff --git a/lib/ext2fs/dosio.h b/lib/ext2fs/dosio.h
new file mode 100644
index 0000000..d2a8f03
--- /dev/null
+++ b/lib/ext2fs/dosio.h
@@ -0,0 +1,157 @@
+/*
+ * v1.0
+ *
+ * Disk I/O include file for the ext2fs/DOS library.
+ *
+ * Copyright (c) 1997 Mark Habersack
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifndef __diskio_h
+#define __diskio_h
+#ifdef __TURBOC__
+#ifndef __LARGE__
+# error "ext2fs/DOS library requires LARGE model!"
+#endif
+#endif
+
+#ifdef __TURBOC__
+#include "msdos.h"
+#endif
+
+/*
+ * A helper structure used in LBA => CHS conversion
+ */
+typedef struct
+{
+ unsigned short cyl; /* Cylinder (or track) */
+ unsigned short head;
+ unsigned short sector;
+ unsigned short offset; /* Offset of byte within the sector */
+} CHS;
+
+/*
+ * All partition data we need is here
+ */
+typedef struct
+{
+ char *dev; /* _Linux_ device name (like "/dev/hda1") */
+ unsigned char phys; /* Physical DOS drive number */
+ unsigned long start; /* LBA address of partition start */
+ unsigned long len; /* length of partition in sectors */
+ unsigned char pno; /* Partition number (read from *dev) */
+
+ /* This partition's drive geometry */
+ unsigned short cyls;
+ unsigned short heads;
+ unsigned short sects;
+} PARTITION;
+
+/*
+ * PC partition table entry format
+ */
+#ifdef __DJGPP__
+#pragma pack(1)
+#endif
+typedef struct
+{
+ unsigned char active;
+ unsigned char start_head;
+ unsigned char start_sec;
+ unsigned char start_cyl;
+ unsigned char type;
+ unsigned char end_head;
+ unsigned char end_sec;
+ unsigned char end_cyl;
+ unsigned long first_sec_rel;
+ unsigned long size;
+} PTABLE_ENTRY;
+#ifdef __DJGPP__
+#pragma pack()
+#endif
+
+/*
+ * INT 0x13 operation codes
+ */
+#define DISK_READ 0x02
+#define DISK_WRITE 0x03
+#define DISK_GET_GEOMETRY 0x08
+#define DISK_READY 0x10
+
+/*
+ * Errors to put in _dio_error
+ */
+#define ERR_BADDEV 0x00000001L
+#define ERR_HARDWARE 0x00000002L
+#define ERR_NOTSUPP 0x00000003L
+#define ERR_NOTEXT2FS 0x00000004L
+#define ERR_EMPTYPART 0x00000005L
+#define ERR_LINUXSWAP 0x00000006L
+
+/*
+ * Functions in diskio.c
+ */
+
+/*
+ * Variable contains last module's error
+ */
+extern unsigned long _dio_error;
+
+/*
+ * This one contains last hardware error (if _dio_error == ERR_HARDWARE)
+ */
+extern unsigned long _dio_hw_error;
+
+/*
+ * Macros to check for disk hardware errors
+ */
+#define HW_OK() ((unsigned char)_dio_hw_error == 0x00)
+#define HW_BAD_CMD() ((unsigned char)_dio_hw_error == 0x01)
+#define HW_NO_ADDR_MARK() ((unsigned char)_dio_hw_error == 0x02)
+#define HW_WRITE_PROT() ((unsigned char)_dio_hw_error == 0x03)
+#define HW_NO_SECTOR() ((unsigned char)_dio_hw_error == 0x04)
+#define HW_RESET_FAIL() ((unsigned char)_dio_hw_error == 0x05)
+#define HW_DISK_CHANGED() ((unsigned char)_dio_hw_error == 0x06)
+#define HW_DRIVE_FAIL() ((unsigned char)_dio_hw_error == 0x07)
+#define HW_DMA_OVERRUN() ((unsigned char)_dio_hw_error == 0x08)
+#define HW_DMA_BOUNDARY() ((unsigned char)_dio_hw_error == 0x09)
+#define HW_BAD_SECTOR() ((unsigned char)_dio_hw_error == 0x0A)
+#define HW_BAD_TRACK() ((unsigned char)_dio_hw_error == 0x0B)
+#define HW_UNSUPP_TRACK() ((unsigned char)_dio_hw_error == 0x0C)
+#define HW_BAD_CRC_ECC() ((unsigned char)_dio_hw_error == 0x10)
+#define HW_CRC_ECC_CORR() ((unsigned char)_dio_hw_error == 0x11)
+#define HW_CONTR_FAIL() ((unsigned char)_dio_hw_error == 0x20)
+#define HW_SEEK_FAIL() ((unsigned char)_dio_hw_error == 0x40)
+#define HW_ATTACH_FAIL() ((unsigned char)_dio_hw_error == 0x80)
+#define HW_DRIVE_NREADY() ((unsigned char)_dio_hw_error == 0xAA)
+#define HW_UNDEF_ERROR() ((unsigned char)_dio_hw_error == 0xBB)
+#define HW_WRITE_FAULT() ((unsigned char)_dio_hw_error == 0xCC)
+#define HW_STATUS_ERROR() ((unsigned char)_dio_hw_error == 0xE0)
+#define HW_SENSE_FAIL() ((unsigned char)_dio_hw_error == 0xFF)
+
+
+/*
+ * Open the specified partition.
+ * String 'dev' must have a format:
+ *
+ * /dev/{sd|hd|fd}[X]
+ *
+ * where,
+ *
+ * only one of the option in curly braces can be used and X is an optional
+ * partition number for the given device. If X is not specified, function
+ * scans the drive's partition table in search for the first Linux ext2fs
+ * partition (signature 0x83). Along the way it dives into every extended
+ * partition encountered.
+ * Scan ends if either (a) there are no more used partition entries, or
+ * (b) there is no Xth partition.
+ *
+ * Routine returns 0 on success and !=0 otherwise.
+ */
+int open_partition(char *dev);
+
+#endif /* __diskio_h */
diff --git a/lib/ext2fs/dupfs.c b/lib/ext2fs/dupfs.c
new file mode 100644
index 0000000..02721e1
--- /dev/null
+++ b/lib/ext2fs/dupfs.c
@@ -0,0 +1,122 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#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;
+ fs->mmp_buf = 0;
+ fs->mmp_cmp = 0;
+ fs->mmp_fd = -1;
+
+ 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;
+ }
+ if (src->mmp_buf) {
+ retval = ext2fs_get_mem(src->blocksize, &fs->mmp_buf);
+ if (retval)
+ goto errout;
+ memcpy(fs->mmp_buf, src->mmp_buf, src->blocksize);
+ }
+ if (src->mmp_fd >= 0) {
+ fs->mmp_fd = dup(src->mmp_fd);
+ if (fs->mmp_fd < 0) {
+ retval = EXT2_ET_MMP_OPEN_DIRECT;
+ goto errout;
+ }
+ }
+ if (src->mmp_cmp) {
+ int align = ext2fs_get_dio_alignment(src->mmp_fd);
+
+ retval = ext2fs_get_memalign(src->blocksize, align,
+ &fs->mmp_cmp);
+ if (retval)
+ goto errout;
+ memcpy(fs->mmp_cmp, src->mmp_cmp, src->blocksize);
+ }
+ *dest = fs;
+ return 0;
+errout:
+ ext2fs_free(fs);
+ return retval;
+
+}
+
diff --git a/lib/ext2fs/e2image.h b/lib/ext2fs/e2image.h
new file mode 100644
index 0000000..53b20cc
--- /dev/null
+++ b/lib/ext2fs/e2image.h
@@ -0,0 +1,37 @@
+/*
+ * 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/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
new file mode 100644
index 0000000..b5d5abd
--- /dev/null
+++ b/lib/ext2fs/expanddir.c
@@ -0,0 +1,143 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+struct expand_dir_struct {
+ int done;
+ int newblocks;
+ blk64_t goal;
+ errcode_t err;
+ ext2_ino_t dir;
+};
+
+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;
+ char *block;
+ errcode_t retval;
+
+ if (*blocknr) {
+ if (blockcnt >= 0)
+ es->goal = *blocknr;
+ return 0;
+ }
+ if (blockcnt &&
+ (EXT2FS_B2C(fs, es->goal) == EXT2FS_B2C(fs, es->goal+1)))
+ new_blk = es->goal+1;
+ else {
+ es->goal &= ~EXT2FS_CLUSTER_MASK(fs);
+ retval = ext2fs_new_block2(fs, es->goal, 0, &new_blk);
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ es->newblocks++;
+ ext2fs_block_alloc_stats2(fs, new_blk, +1);
+ }
+ 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_block4(fs, new_blk, block, 0,
+ es->dir);
+ ext2fs_free_mem(&block);
+ } else
+ retval = ext2fs_zero_blocks2(fs, new_blk, 1, NULL, NULL);
+ if (blockcnt >= 0)
+ es->goal = new_blk;
+ if (retval) {
+ es->err = retval;
+ return BLOCK_ABORT;
+ }
+ *blocknr = new_blk;
+
+ 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;
+
+ retval = ext2fs_read_inode(fs, dir, &inode);
+ if (retval)
+ return retval;
+
+ es.done = 0;
+ es.err = 0;
+ es.goal = ext2fs_find_inode_goal(fs, dir, &inode, 0);
+ es.newblocks = 0;
+ es.dir = dir;
+
+ retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
+ 0, expand_dir_proc, &es);
+ if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
+ return ext2fs_inline_data_expand(fs, dir);
+
+ 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;
+
+ retval = ext2fs_inode_size_set(fs, &inode,
+ EXT2_I_SIZE(&inode) + fs->blocksize);
+ if (retval)
+ return retval;
+ 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/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
new file mode 100644
index 0000000..de14019
--- /dev/null
+++ b/lib/ext2fs/ext2_err.et.in
@@ -0,0 +1,560 @@
+#
+# Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+#
+# %Begin-Header%
+# This file may be redistributed under the terms of the GNU Public
+# License.
+# %End-Header%
+#
+ error_table ext2
+
+ec EXT2_ET_BASE,
+ "EXT2FS Library version @E2FSPROGS_VERSION@"
+
+ec EXT2_ET_MAGIC_EXT2FS_FILSYS,
+ "Wrong magic number for ext2_filsys structure"
+
+ec EXT2_ET_MAGIC_BADBLOCKS_LIST,
+ "Wrong magic number for badblocks_list structure"
+
+ec EXT2_ET_MAGIC_BADBLOCKS_ITERATE,
+ "Wrong magic number for badblocks_iterate structure"
+
+ec EXT2_ET_MAGIC_INODE_SCAN,
+ "Wrong magic number for inode_scan structure"
+
+ec EXT2_ET_MAGIC_IO_CHANNEL,
+ "Wrong magic number for io_channel structure"
+
+ec EXT2_ET_MAGIC_UNIX_IO_CHANNEL,
+ "Wrong magic number for unix io_channel structure"
+
+ec EXT2_ET_MAGIC_IO_MANAGER,
+ "Wrong magic number for io_manager structure"
+
+ec EXT2_ET_MAGIC_BLOCK_BITMAP,
+ "Wrong magic number for block_bitmap structure"
+
+ec EXT2_ET_MAGIC_INODE_BITMAP,
+ "Wrong magic number for inode_bitmap structure"
+
+ec EXT2_ET_MAGIC_GENERIC_BITMAP,
+ "Wrong magic number for generic_bitmap structure"
+
+ec EXT2_ET_MAGIC_TEST_IO_CHANNEL,
+ "Wrong magic number for test io_channel structure"
+
+ec EXT2_ET_MAGIC_DBLIST,
+ "Wrong magic number for directory block list structure"
+
+ec EXT2_ET_MAGIC_ICOUNT,
+ "Wrong magic number for icount structure"
+
+ec EXT2_ET_MAGIC_PQ_IO_CHANNEL,
+ "Wrong magic number for Powerquest io_channel structure"
+
+ec EXT2_ET_MAGIC_EXT2_FILE,
+ "Wrong magic number for ext2 file structure"
+
+ec EXT2_ET_MAGIC_E2IMAGE,
+ "Wrong magic number for Ext2 Image Header"
+
+ec EXT2_ET_MAGIC_INODE_IO_CHANNEL,
+ "Wrong magic number for inode io_channel structure"
+
+ec EXT2_ET_MAGIC_EXTENT_HANDLE,
+ "Wrong magic number for ext4 extent handle"
+
+ec EXT2_ET_BAD_MAGIC,
+ "Bad magic number in super-block"
+
+ec EXT2_ET_REV_TOO_HIGH,
+ "Filesystem revision too high"
+
+ec EXT2_ET_RO_FILSYS,
+ "Attempt to write to filesystem opened read-only"
+
+ec EXT2_ET_GDESC_READ,
+ "Can't read group descriptors"
+
+ec EXT2_ET_GDESC_WRITE,
+ "Can't write group descriptors"
+
+ec EXT2_ET_GDESC_BAD_BLOCK_MAP,
+ "Corrupt group descriptor: bad block for block bitmap"
+
+ec EXT2_ET_GDESC_BAD_INODE_MAP,
+ "Corrupt group descriptor: bad block for inode bitmap"
+
+ec EXT2_ET_GDESC_BAD_INODE_TABLE,
+ "Corrupt group descriptor: bad block for inode table"
+
+ec EXT2_ET_INODE_BITMAP_WRITE,
+ "Can't write an inode bitmap"
+
+ec EXT2_ET_INODE_BITMAP_READ,
+ "Can't read an inode bitmap"
+
+ec EXT2_ET_BLOCK_BITMAP_WRITE,
+ "Can't write a block bitmap"
+
+ec EXT2_ET_BLOCK_BITMAP_READ,
+ "Can't read a block bitmap"
+
+ec EXT2_ET_INODE_TABLE_WRITE,
+ "Can't write an inode table"
+
+ec EXT2_ET_INODE_TABLE_READ,
+ "Can't read an inode table"
+
+ec EXT2_ET_NEXT_INODE_READ,
+ "Can't read next inode"
+
+ec EXT2_ET_UNEXPECTED_BLOCK_SIZE,
+ "Filesystem has unexpected block size"
+
+ec EXT2_ET_DIR_CORRUPTED,
+ "EXT2 directory corrupted"
+
+ec EXT2_ET_SHORT_READ,
+ "Attempt to read block from filesystem resulted in short read"
+
+ec EXT2_ET_SHORT_WRITE,
+ "Attempt to write block to filesystem resulted in short write"
+
+ec EXT2_ET_DIR_NO_SPACE,
+ "No free space in the directory"
+
+ec EXT2_ET_NO_INODE_BITMAP,
+ "Inode bitmap not loaded"
+
+ec EXT2_ET_NO_BLOCK_BITMAP,
+ "Block bitmap not loaded"
+
+ec EXT2_ET_BAD_INODE_NUM,
+ "Illegal inode number"
+
+ec EXT2_ET_BAD_BLOCK_NUM,
+ "Illegal block number"
+
+ec EXT2_ET_EXPAND_DIR_ERR,
+ "Internal error in ext2fs_expand_dir"
+
+ec EXT2_ET_TOOSMALL,
+ "Not enough space to build proposed filesystem"
+
+ec EXT2_ET_BAD_BLOCK_MARK,
+ "Illegal block number passed to ext2fs_mark_block_bitmap"
+
+ec EXT2_ET_BAD_BLOCK_UNMARK,
+ "Illegal block number passed to ext2fs_unmark_block_bitmap"
+
+ec EXT2_ET_BAD_BLOCK_TEST,
+ "Illegal block number passed to ext2fs_test_block_bitmap"
+
+ec EXT2_ET_BAD_INODE_MARK,
+ "Illegal inode number passed to ext2fs_mark_inode_bitmap"
+
+ec EXT2_ET_BAD_INODE_UNMARK,
+ "Illegal inode number passed to ext2fs_unmark_inode_bitmap"
+
+ec EXT2_ET_BAD_INODE_TEST,
+ "Illegal inode number passed to ext2fs_test_inode_bitmap"
+
+ec EXT2_ET_FUDGE_BLOCK_BITMAP_END,
+ "Attempt to fudge end of block bitmap past the real end"
+
+ec EXT2_ET_FUDGE_INODE_BITMAP_END,
+ "Attempt to fudge end of inode bitmap past the real end"
+
+ec EXT2_ET_BAD_IND_BLOCK,
+ "Illegal indirect block found"
+
+ec EXT2_ET_BAD_DIND_BLOCK,
+ "Illegal doubly indirect block found"
+
+ec EXT2_ET_BAD_TIND_BLOCK,
+ "Illegal triply indirect block found"
+
+ec EXT2_ET_NEQ_BLOCK_BITMAP,
+ "Block bitmaps are not the same"
+
+ec EXT2_ET_NEQ_INODE_BITMAP,
+ "Inode bitmaps are not the same"
+
+ec EXT2_ET_BAD_DEVICE_NAME,
+ "Illegal or malformed device name"
+
+ec EXT2_ET_MISSING_INODE_TABLE,
+ "A block group is missing an inode table"
+
+ec EXT2_ET_CORRUPT_SUPERBLOCK,
+ "The ext2 superblock is corrupt"
+
+ec EXT2_ET_BAD_GENERIC_MARK,
+ "Illegal generic bit number passed to ext2fs_mark_generic_bitmap"
+
+ec EXT2_ET_BAD_GENERIC_UNMARK,
+ "Illegal generic bit number passed to ext2fs_unmark_generic_bitmap"
+
+ec EXT2_ET_BAD_GENERIC_TEST,
+ "Illegal generic bit number passed to ext2fs_test_generic_bitmap"
+
+ec EXT2_ET_SYMLINK_LOOP,
+ "Too many symbolic links encountered."
+
+ec EXT2_ET_CALLBACK_NOTHANDLED,
+ "The callback function will not handle this case"
+
+ec EXT2_ET_BAD_BLOCK_IN_INODE_TABLE,
+ "The inode is from a bad block in the inode table"
+
+ec EXT2_ET_UNSUPP_FEATURE,
+ "Filesystem has unsupported feature(s)"
+
+ec EXT2_ET_RO_UNSUPP_FEATURE,
+ "Filesystem has unsupported read-only feature(s)"
+
+ec EXT2_ET_LLSEEK_FAILED,
+ "IO Channel failed to seek on read or write"
+
+ec EXT2_ET_NO_MEMORY,
+ "Memory allocation failed"
+
+ec EXT2_ET_INVALID_ARGUMENT,
+ "Invalid argument passed to ext2 library"
+
+ec EXT2_ET_BLOCK_ALLOC_FAIL,
+ "Could not allocate block in ext2 filesystem"
+
+ec EXT2_ET_INODE_ALLOC_FAIL,
+ "Could not allocate inode in ext2 filesystem"
+
+ec EXT2_ET_NO_DIRECTORY,
+ "Ext2 inode is not a directory"
+
+ec EXT2_ET_TOO_MANY_REFS,
+ "Too many references in table"
+
+ec EXT2_ET_FILE_NOT_FOUND,
+ "File not found by ext2_lookup"
+
+ec EXT2_ET_FILE_RO,
+ "File open read-only"
+
+ec EXT2_ET_DB_NOT_FOUND,
+ "Ext2 directory block not found"
+
+ec EXT2_ET_DIR_EXISTS,
+ "Ext2 directory already exists"
+
+ec EXT2_ET_UNIMPLEMENTED,
+ "Unimplemented ext2 library function"
+
+ec EXT2_ET_CANCEL_REQUESTED,
+ "User cancel requested"
+
+ec EXT2_ET_FILE_TOO_BIG,
+ "Ext2 file too big"
+
+ec EXT2_ET_JOURNAL_NOT_BLOCK,
+ "Supplied journal device not a block device"
+
+ec EXT2_ET_NO_JOURNAL_SB,
+ "Journal superblock not found"
+
+ec EXT2_ET_JOURNAL_TOO_SMALL,
+ "Journal must be at least 1024 blocks"
+
+ec EXT2_ET_JOURNAL_UNSUPP_VERSION,
+ "Unsupported journal version"
+
+ec EXT2_ET_LOAD_EXT_JOURNAL,
+ "Error loading external journal"
+
+ec EXT2_ET_NO_JOURNAL,
+ "Journal not found"
+
+ec EXT2_ET_DIRHASH_UNSUPP,
+ "Directory hash unsupported"
+
+ec EXT2_ET_BAD_EA_BLOCK_NUM,
+ "Illegal extended attribute block number"
+
+ec EXT2_ET_TOO_MANY_INODES,
+ "Cannot create filesystem with requested number of inodes"
+
+ec EXT2_ET_NOT_IMAGE_FILE,
+ "E2image snapshot not in use"
+
+ec EXT2_ET_RES_GDT_BLOCKS,
+ "Too many reserved group descriptor blocks"
+
+ec EXT2_ET_RESIZE_INODE_CORRUPT,
+ "Resize inode is corrupt"
+
+ec EXT2_ET_SET_BMAP_NO_IND,
+ "Tried to set block bmap with missing indirect block"
+
+ec EXT2_ET_TDB_SUCCESS,
+ "TDB: Success"
+
+ec EXT2_ET_TDB_ERR_CORRUPT,
+ "TDB: Corrupt database"
+
+ec EXT2_ET_TDB_ERR_IO,
+ "TDB: IO Error"
+
+ec EXT2_ET_TDB_ERR_LOCK,
+ "TDB: Locking error"
+
+ec EXT2_ET_TDB_ERR_OOM,
+ "TDB: Out of memory"
+
+ec EXT2_ET_TDB_ERR_EXISTS,
+ "TDB: Record exists"
+
+ec EXT2_ET_TDB_ERR_NOLOCK,
+ "TDB: Lock exists on other keys"
+
+ec EXT2_ET_TDB_ERR_EINVAL,
+ "TDB: Invalid parameter"
+
+ec EXT2_ET_TDB_ERR_NOEXIST,
+ "TDB: Record does not exist"
+
+ec EXT2_ET_TDB_ERR_RDONLY,
+ "TDB: Write not permitted"
+
+ec EXT2_ET_DBLIST_EMPTY,
+ "Ext2fs directory block list is empty"
+
+ec EXT2_ET_RO_BLOCK_ITERATE,
+ "Attempt to modify a block mapping via a read-only block iterator"
+
+ec EXT2_ET_MAGIC_EXTENT_PATH,
+ "Wrong magic number for ext4 extent saved path"
+
+ec EXT2_ET_MAGIC_GENERIC_BITMAP64,
+ "Wrong magic number for 64-bit generic bitmap"
+
+ec EXT2_ET_MAGIC_BLOCK_BITMAP64,
+ "Wrong magic number for 64-bit block bitmap"
+
+ec EXT2_ET_MAGIC_INODE_BITMAP64,
+ "Wrong magic number for 64-bit inode bitmap"
+
+ec EXT2_ET_MAGIC_RESERVED_13,
+ "Wrong magic number --- RESERVED_13"
+
+ec EXT2_ET_MAGIC_RESERVED_14,
+ "Wrong magic number --- RESERVED_14"
+
+ec EXT2_ET_MAGIC_RESERVED_15,
+ "Wrong magic number --- RESERVED_15"
+
+ec EXT2_ET_MAGIC_RESERVED_16,
+ "Wrong magic number --- RESERVED_16"
+
+ec EXT2_ET_MAGIC_RESERVED_17,
+ "Wrong magic number --- RESERVED_17"
+
+ec EXT2_ET_MAGIC_RESERVED_18,
+ "Wrong magic number --- RESERVED_18"
+
+ec EXT2_ET_MAGIC_RESERVED_19,
+ "Wrong magic number --- RESERVED_19"
+
+ec EXT2_ET_EXTENT_HEADER_BAD,
+ "Corrupt extent header"
+
+ec EXT2_ET_EXTENT_INDEX_BAD,
+ "Corrupt extent index"
+
+ec EXT2_ET_EXTENT_LEAF_BAD,
+ "Corrupt extent"
+
+ec EXT2_ET_EXTENT_NO_SPACE,
+ "No free space in extent map"
+
+ec EXT2_ET_INODE_NOT_EXTENT,
+ "Inode does not use extents"
+
+ec EXT2_ET_EXTENT_NO_NEXT,
+ "No 'next' extent"
+
+ec EXT2_ET_EXTENT_NO_PREV,
+ "No 'previous' extent"
+
+ec EXT2_ET_EXTENT_NO_UP,
+ "No 'up' extent"
+
+ec EXT2_ET_EXTENT_NO_DOWN,
+ "No 'down' extent"
+
+ec EXT2_ET_NO_CURRENT_NODE,
+ "No current node"
+
+ec EXT2_ET_OP_NOT_SUPPORTED,
+ "Ext2fs operation not supported"
+
+ec EXT2_ET_CANT_INSERT_EXTENT,
+ "No room to insert extent in node"
+
+ec EXT2_ET_CANT_SPLIT_EXTENT,
+ "Splitting would result in empty node"
+
+ec EXT2_ET_EXTENT_NOT_FOUND,
+ "Extent not found"
+
+ec EXT2_ET_EXTENT_NOT_SUPPORTED,
+ "Operation not supported for inodes containing extents"
+
+ec EXT2_ET_EXTENT_INVALID_LENGTH,
+ "Extent length is invalid"
+
+ec EXT2_ET_IO_CHANNEL_NO_SUPPORT_64,
+ "I/O Channel does not support 64-bit block numbers"
+
+ec EXT2_ET_NO_MTAB_FILE,
+ "Can't check if filesystem is mounted due to missing mtab file"
+
+ec EXT2_ET_CANT_USE_LEGACY_BITMAPS,
+ "Filesystem too large to use legacy bitmaps"
+
+ec EXT2_ET_MMP_MAGIC_INVALID,
+ "MMP: invalid magic number"
+
+ec EXT2_ET_MMP_FAILED,
+ "MMP: device currently active"
+
+ec EXT2_ET_MMP_FSCK_ON,
+ "MMP: e2fsck being run"
+
+ec EXT2_ET_MMP_BAD_BLOCK,
+ "MMP: block number beyond filesystem range"
+
+ec EXT2_ET_MMP_UNKNOWN_SEQ,
+ "MMP: undergoing an unknown operation"
+
+ec EXT2_ET_MMP_CHANGE_ABORT,
+ "MMP: filesystem still in use"
+
+ec EXT2_ET_MMP_OPEN_DIRECT,
+ "MMP: open with O_DIRECT failed"
+
+ec EXT2_ET_BAD_DESC_SIZE,
+ "Block group descriptor size incorrect"
+
+ec EXT2_ET_INODE_CSUM_INVALID,
+ "Inode checksum does not match inode"
+
+ec EXT2_ET_INODE_BITMAP_CSUM_INVALID,
+ "Inode bitmap checksum does not match bitmap"
+
+ec EXT2_ET_EXTENT_CSUM_INVALID,
+ "Extent block checksum does not match extent block"
+
+ec EXT2_ET_DIR_NO_SPACE_FOR_CSUM,
+ "Directory block does not have space for checksum"
+
+ec EXT2_ET_DIR_CSUM_INVALID,
+ "Directory block checksum does not match directory block"
+
+ec EXT2_ET_EXT_ATTR_CSUM_INVALID,
+ "Extended attribute block checksum does not match block"
+
+ec EXT2_ET_SB_CSUM_INVALID,
+ "Superblock checksum does not match superblock"
+
+ec EXT2_ET_UNKNOWN_CSUM,
+ "Unknown checksum algorithm"
+
+ec EXT2_ET_MMP_CSUM_INVALID,
+ "MMP block checksum does not match"
+
+ec EXT2_ET_FILE_EXISTS,
+ "Ext2 file already exists"
+
+ec EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
+ "Block bitmap checksum does not match bitmap"
+
+ec EXT2_ET_INLINE_DATA_CANT_ITERATE,
+ "Cannot iterate data blocks of an inode containing inline data"
+
+ec EXT2_ET_EA_BAD_NAME_LEN,
+ "Extended attribute has an invalid name length"
+
+ec EXT2_ET_EA_BAD_VALUE_SIZE,
+ "Extended attribute has an invalid value length"
+
+ec EXT2_ET_BAD_EA_HASH,
+ "Extended attribute has an incorrect hash"
+
+ec EXT2_ET_BAD_EA_HEADER,
+ "Extended attribute block has a bad header"
+
+ec EXT2_ET_EA_KEY_NOT_FOUND,
+ "Extended attribute key not found"
+
+ec EXT2_ET_EA_NO_SPACE,
+ "Insufficient space to store extended attribute data"
+
+ec EXT2_ET_MISSING_EA_FEATURE,
+ "Filesystem is missing ext_attr or inline_data feature"
+
+ec EXT2_ET_NO_INLINE_DATA,
+ "Inode doesn't have inline data"
+
+ec EXT2_ET_INLINE_DATA_NO_BLOCK,
+ "No block for an inode with inline data"
+
+ec EXT2_ET_INLINE_DATA_NO_SPACE,
+ "No free space in inline data"
+
+ec EXT2_ET_MAGIC_EA_HANDLE,
+ "Wrong magic number for extended attribute structure"
+
+ec EXT2_ET_INODE_IS_GARBAGE,
+ "Inode seems to contain garbage"
+
+ec EXT2_ET_EA_BAD_VALUE_OFFSET,
+ "Extended attribute has an invalid value offset"
+
+ec EXT2_ET_JOURNAL_FLAGS_WRONG,
+ "Journal flags inconsistent"
+
+ec EXT2_ET_UNDO_FILE_CORRUPT,
+ "Undo file corrupt"
+
+ec EXT2_ET_UNDO_FILE_WRONG,
+ "Wrong undo file for this filesystem"
+
+ec EXT2_ET_FILESYSTEM_CORRUPTED,
+ "File system is corrupted"
+
+ec EXT2_ET_BAD_CRC,
+ "Bad CRC detected in file system"
+
+ec EXT2_ET_CORRUPT_JOURNAL_SB,
+ "The journal superblock is corrupt"
+
+ec EXT2_ET_INODE_CORRUPTED,
+ "Inode is corrupted"
+
+ec EXT2_ET_EA_INODE_CORRUPTED,
+ "Inode containing extended attribute value is corrupted"
+
+ec EXT2_ET_NO_GDESC,
+ "Group descriptors not loaded"
+
+ec EXT2_FILSYS_CORRUPTED,
+ "The internal ext2_filsys data structure appears to be corrupted"
+
+ec EXT2_ET_EXTENT_CYCLE,
+ "Found cyclic loop in extent tree"
+
+ec EXT2_ET_EXTERNAL_JOURNAL_NOSUPP,
+ "Operation not supported on an external journal"
+
+ end
diff --git a/lib/ext2fs/ext2_ext_attr.h b/lib/ext2fs/ext2_ext_attr.h
new file mode 100644
index 0000000..c6068c4
--- /dev/null
+++ b/lib/ext2fs/ext2_ext_attr.h
@@ -0,0 +1,84 @@
+/*
+ File: linux/ext2_ext_attr.h
+
+ On-disk format of extended attributes for the ext2 filesystem.
+
+ (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+#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_checksum; /* crc32c(uuid+id+xattrs) */
+ /* id = inum if refcount = 1, else blknum */
+ __u32 h_reserved[3]; /* 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_inum; /* inode in which the value is stored */
+ __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<<EXT2_EXT_ATTR_PAD_BITS)
+#define EXT2_EXT_ATTR_ROUND (EXT2_EXT_ATTR_PAD-1)
+#define EXT2_EXT_ATTR_LEN(name_len) \
+ (((name_len) + EXT2_EXT_ATTR_ROUND + \
+ sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_ATTR_NEXT(entry) \
+ ( (struct ext2_ext_attr_entry *)( \
+ (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->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)
+
+/*
+ * XATTR_SIZE_MAX is currently 64k, but for the purposes of checking
+ * for file system consistency errors, we use a somewhat bigger value.
+ * This allows XATTR_SIZE_MAX to grow in the future, but by using this
+ * instead of INT_MAX for certain consistency checks, we don't need to
+ * worry about arithmetic overflows. (Actually XATTR_SIZE_MAX is
+ * defined in include/uapi/linux/limits.h, so changing it is going
+ * not going to be trivial....)
+ */
+#define EXT2_XATTR_SIZE_MAX (1 << 24)
+
+#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/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
new file mode 100644
index 0000000..0fc9c09
--- /dev/null
+++ b/lib/ext2fs/ext2_fs.h
@@ -0,0 +1,1201 @@
+/*
+ * 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 <ext2fs/ext2_types.h> /* Changed from linux/types.h */
+
+#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
+
+#ifndef __nonstring
+#ifdef __has_attribute
+#if __has_attribute(__nonstring__)
+#define __nonstring __attribute__((__nonstring__))
+#else
+#define __nonstring
+#endif /* __has_attribute(__nonstring__) */
+#else
+# define __nonstring
+#endif /* __has_attribute */
+#endif /* __nonstring */
+
+/*
+ * 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 */
+#define EXT4_REPLICA_INO 10 /* Used by non-upstream feature */
+
+/* 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)
+
+/*
+ * Macro-instructions used to manage fragments
+ *
+ * Note: for backwards compatibility only, for the dump program.
+ * Ext2/3/4 will never support fragments....
+ */
+#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
+#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
+#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
+#define EXT2_FRAG_SIZE(s) EXT2_BLOCK_SIZE(s)
+#define EXT2_FRAGS_PER_BLOCK(s) 1
+
+/*
+ * 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_exclude_bitmap_lo; /* Exclude bitmap for snapshots */
+ __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
+ __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
+ __u16 bg_itable_unused; /* Unused inodes count */
+ __u16 bg_checksum; /* crc16(s_uuid+group_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_exclude_bitmap_lo; /* Exclude bitmap for snapshots */
+ __u16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
+ __u16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bitmap) LSB */
+ __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_exclude_bitmap_hi; /* Exclude bitmap block MSB */
+ __u16 bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bitmap) MSB */
+ __u16 bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bitmap) MSB */
+ __u32 bg_reserved;
+};
+
+#define EXT4_BG_INODE_BITMAP_CSUM_HI_END \
+ (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \
+ sizeof(__u16))
+#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \
+ (offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \
+ sizeof(__u16))
+
+#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_SIPHASH 6
+
+#define EXT2_HASH_FLAG_INCOMPAT 0x1
+
+#define EXT4_DX_BLOCK_MASK 0x0fffffff
+
+struct ext2_dx_entry {
+ __le32 hash;
+ __le32 block;
+};
+
+struct ext2_dx_countlimit {
+ __le16 limit;
+ __le16 count;
+};
+
+/*
+ * This goes at the end of each htree block.
+ */
+struct ext2_dx_tail {
+ __le32 dt_reserved;
+ __le32 dt_checksum; /* crc32c(uuid+inum+dxblock) */
+};
+
+/*
+ * 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) \
+ (ext2fs_has_feature_64bit(s) ? (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_CLUSTERS_PER_GROUP(s) (EXT2_SB(s)->s_clusters_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) ((((unsigned) 1 << 16) - 8) * \
+ (EXT2_CLUSTER_SIZE(s) / \
+ EXT2_BLOCK_SIZE(s)))
+#define EXT2_MAX_CLUSTERS_PER_GROUP(s) (((unsigned) 1 << 16) - 8)
+#define EXT2_MAX_INODES_PER_GROUP(s) (((unsigned) 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
+
+#define EXT2_GROUPS_TO_BLOCKS(s, g) ((blk64_t) EXT2_BLOCKS_PER_GROUP(s) * \
+ (g))
+#define EXT2_GROUPS_TO_CLUSTERS(s, g) ((blk64_t) EXT2_CLUSTERS_PER_GROUP(s) * \
+ (g))
+
+/*
+ * 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 */
+ /* nb: was previously EXT2_ECOMPR_FL */
+#define EXT4_ENCRYPT_FL 0x00000800 /* encrypted inode */
+/* 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_VERITY_FL 0x00100000 /* Verity protected inode */
+#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */
+/* EXT4_EOFBLOCKS_FL 0x00400000 was here */
+#define FS_NOCOW_FL 0x00800000 /* Do not cow file */
+#define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */
+#define FS_DAX_FL 0x02000000 /* Inode is DAX */
+#define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */
+#define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */
+#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data */
+#define EXT4_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
+#define EXT4_CASEFOLD_FL 0x40000000 /* Casefolded file */
+#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE 0x604BDFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE 0x604B80FF /* 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)
+#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+/*00*/ __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 */
+/*10*/ __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 */
+/*20*/ __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 */
+/*28*/ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+/*64*/ __u32 i_generation; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_size_high;
+/*70*/ __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] */
+ __u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */
+ __u16 l_i_reserved;
+ } 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 {
+/*00*/ __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 */
+/*10*/ __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 */
+/*20*/ __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 */
+/*28*/ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+/*64*/ __u32 i_generation; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_size_high;
+/*70*/ __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] */
+ __u16 l_i_checksum_lo; /* crc32c(uuid+inum+inode) */
+ __u16 l_i_reserved;
+ } 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 */
+/*80*/ __u16 i_extra_isize;
+ __u16 i_checksum_hi; /* crc32c(uuid+inum+inode) */
+ __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) */
+/*90*/ __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 */
+/*9c*/ __u32 i_projid; /* Project ID */
+};
+
+#define EXT4_INODE_CSUM_HI_EXTRA_END \
+ (offsetof(struct ext2_inode_large, i_checksum_hi) + sizeof(__u16) - \
+ EXT2_GOOD_OLD_INODE_SIZE)
+
+#define EXT4_EPOCH_BITS 2
+#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
+
+#define i_checksum_lo osd2.linux2.l_i_checksum_lo
+
+#define inode_includes(size, field) \
+ (size >= (sizeof(((struct ext2_inode_large *)0)->field) + \
+ offsetof(struct ext2_inode_large, field)))
+
+#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
+#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 | (unsigned)(inode).osd2.linux2.l_i_uid_high << 16)
+#define inode_gid(inode) ((inode).i_gid | (unsigned)(inode).osd2.linux2.l_i_gid_high << 16)
+#define inode_projid(inode) ((inode).i_projid)
+#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))
+
+static inline
+struct ext2_inode *EXT2_INODE(struct ext2_inode_large *large_inode)
+{
+ return (struct ext2_inode *) large_inode;
+}
+
+/*
+ * 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 */
+#define EXT4_FC_REPLAY 0x0020 /* Ext4 fast commit replay ongoing */
+
+/*
+ * 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
+
+/* Metadata checksum algorithms */
+#define EXT2_CRC32C_CHKSUM 1
+
+/* Encryption algorithms, key size and key reference len */
+#define EXT4_ENCRYPTION_MODE_INVALID 0
+#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
+#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
+#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
+#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
+
+#define EXT4_AES_256_XTS_KEY_SIZE 64
+#define EXT4_AES_256_GCM_KEY_SIZE 32
+#define EXT4_AES_256_CBC_KEY_SIZE 32
+#define EXT4_AES_256_CTS_KEY_SIZE 32
+#define EXT4_MAX_KEY_SIZE 64
+
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_CRYPTO_BLOCK_SIZE 16
+
+/* Password derivation constants */
+#define EXT4_MAX_PASSPHRASE_SIZE 1024
+#define EXT4_MAX_SALT_SIZE 256
+#define EXT4_PBKDF2_ITERATIONS 0xFFFF
+
+#define EXT2_LABEL_LEN 16
+
+/*
+ * Policy provided via an ioctl on the topmost directory. This
+ * structure is also in the kernel.
+ */
+struct ext4_encryption_policy {
+ char version;
+ char contents_encryption_mode;
+ char filenames_encryption_mode;
+ char flags;
+ char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+struct ext4_encryption_key {
+ __u32 mode;
+ char raw[EXT4_MAX_KEY_SIZE];
+ __u32 size;
+} __attribute__((__packed__));
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+/*000*/ __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 */
+/*010*/ __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 */
+/*020*/ __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 */
+/*030*/ __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 */
+/*040*/ __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 */
+/*050*/ __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 */
+/*060*/ __u32 s_feature_incompat; /* incompatible feature set */
+ __u32 s_feature_ro_compat; /* readonly-compatible feature set */
+/*068*/ __u8 s_uuid[16] __nonstring; /* 128-bit uuid for volume */
+/*078*/ __u8 s_volume_name[EXT2_LABEL_LEN] __nonstring; /* volume name, no NUL? */
+/*088*/ __u8 s_last_mounted[64] __nonstring; /* directory last mounted on, no NUL? */
+/*0c8*/ __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.
+ */
+/*0d0*/ __u8 s_journal_uuid[16] __nonstring; /* uuid of journal superblock */
+/*0e0*/ __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 */
+/*0ec*/ __u32 s_hash_seed[4]; /* HTREE hash seed */
+/*0fc*/ __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 */
+/*100*/ __u32 s_default_mount_opts; /* default EXT2_MOUNT_* flags used */
+ __u32 s_first_meta_bg; /* First metablock group */
+ __u32 s_mkfs_time; /* When the filesystem was created */
+/*10c*/ __u32 s_jnl_blocks[17]; /* Backup of the journal inode */
+/*150*/ __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 */
+/*160*/ __u32 s_flags; /* Miscellaneous flags */
+ __u16 s_raid_stride; /* RAID stride in blocks */
+ __u16 s_mmp_update_interval; /* # seconds to wait in MMP checking */
+ __u64 s_mmp_block; /* Block for multi-mount protection */
+/*170*/ __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
+ __u8 s_log_groups_per_flex; /* FLEX_BG group size */
+ __u8 s_checksum_type; /* metadata checksum algorithm */
+ __u8 s_encryption_level; /* versioning level for encryption */
+ __u8 s_reserved_pad; /* Padding to next 32bits */
+ __u64 s_kbytes_written; /* nr of lifetime kilobytes written */
+/*180*/ __u32 s_snapshot_inum; /* Inode number of active snapshot */
+ __u32 s_snapshot_id; /* sequential ID of active snapshot */
+ __u64 s_snapshot_r_blocks_count; /* active snapshot reserved blocks */
+/*190*/ __u32 s_snapshot_list; /* inode number of 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 */
+/*1a0*/ __u64 s_first_error_block; /* block involved in first error */
+ __u8 s_first_error_func[32] __nonstring; /* function where error hit, no NUL? */
+/*1c8*/ __u32 s_first_error_line; /* line number where error happened */
+ __u32 s_last_error_time; /* most recent time of an error */
+/*1d0*/ __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 */
+/*1e0*/ __u8 s_last_error_func[32] __nonstring; /* function where error hit, no NUL? */
+#define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts)
+/*200*/ __u8 s_mount_opts[64] __nonstring; /* default mount options, no NUL? */
+/*240*/ __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_clusters; /* overhead blocks/clusters in fs */
+/*24c*/ __u32 s_backup_bgs[2]; /* If sparse_super2 enabled */
+/*254*/ __u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
+/*258*/ __u8 s_encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
+/*268*/ __le32 s_lpf_ino; /* Location of the lost+found inode */
+ __le32 s_prj_quota_inum; /* inode for tracking project quota */
+/*270*/ __le32 s_checksum_seed; /* crc32c(orig_uuid) if csum_seed set */
+/*274*/ __u8 s_wtime_hi;
+ __u8 s_mtime_hi;
+ __u8 s_mkfs_time_hi;
+ __u8 s_lastcheck_hi;
+ __u8 s_first_error_time_hi;
+ __u8 s_last_error_time_hi;
+ __u8 s_first_error_errcode;
+ __u8 s_last_error_errcode;
+/*27c*/ __le16 s_encoding; /* Filename charset encoding */
+ __le16 s_encoding_flags; /* Filename charset encoding flags */
+ __le32 s_orphan_file_inum; /* Inode for tracking orphan inodes */
+ __le32 s_reserved[94]; /* Padding to the end of the block */
+/*3fc*/ __u32 s_checksum; /* crc32c(superblock) */
+};
+
+#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START)
+#define EXT2_LEN_STR(buf) (int)sizeof(buf), (char *)buf
+
+/*
+ * 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 not used, legacy */
+#define EXT2_FEATURE_COMPAT_EXCLUDE_BITMAP 0x0100
+#define EXT4_FEATURE_COMPAT_SPARSE_SUPER2 0x0200
+#define EXT4_FEATURE_COMPAT_FAST_COMMIT 0x0400
+#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
+#define EXT4_FEATURE_COMPAT_ORPHAN_FILE 0x1000
+
+#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
+/*
+ * METADATA_CSUM implies GDT_CSUM. When METADATA_CSUM is set, group
+ * descriptor checksums use the same algorithm as all other data
+ * structures' checksums.
+ */
+#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
+#define EXT4_FEATURE_RO_COMPAT_REPLICA 0x0800
+#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000
+#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000 /* Project quota */
+#define EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS 0x4000
+#define EXT4_FEATURE_RO_COMPAT_VERITY 0x8000
+#define EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT 0x10000
+
+#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 EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x2000
+#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
+#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
+#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
+#define EXT4_FEATURE_INCOMPAT_CASEFOLD 0x20000
+
+#define EXT4_FEATURE_COMPAT_FUNCS(name, ver, flagname) \
+static inline int ext2fs_has_feature_##name(struct ext2_super_block *sb) \
+{ \
+ return ((EXT2_SB(sb)->s_feature_compat & \
+ EXT##ver##_FEATURE_COMPAT_##flagname) != 0); \
+} \
+static inline void ext2fs_set_feature_##name(struct ext2_super_block *sb) \
+{ \
+ EXT2_SB(sb)->s_feature_compat |= \
+ EXT##ver##_FEATURE_COMPAT_##flagname; \
+} \
+static inline void ext2fs_clear_feature_##name(struct ext2_super_block *sb) \
+{ \
+ EXT2_SB(sb)->s_feature_compat &= \
+ ~EXT##ver##_FEATURE_COMPAT_##flagname; \
+}
+
+#define EXT4_FEATURE_RO_COMPAT_FUNCS(name, ver, flagname) \
+static inline int ext2fs_has_feature_##name(struct ext2_super_block *sb) \
+{ \
+ return ((EXT2_SB(sb)->s_feature_ro_compat & \
+ EXT##ver##_FEATURE_RO_COMPAT_##flagname) != 0); \
+} \
+static inline void ext2fs_set_feature_##name(struct ext2_super_block *sb) \
+{ \
+ EXT2_SB(sb)->s_feature_ro_compat |= \
+ EXT##ver##_FEATURE_RO_COMPAT_##flagname; \
+} \
+static inline void ext2fs_clear_feature_##name(struct ext2_super_block *sb) \
+{ \
+ EXT2_SB(sb)->s_feature_ro_compat &= \
+ ~EXT##ver##_FEATURE_RO_COMPAT_##flagname; \
+}
+
+#define EXT4_FEATURE_INCOMPAT_FUNCS(name, ver, flagname) \
+static inline int ext2fs_has_feature_##name(struct ext2_super_block *sb) \
+{ \
+ return ((EXT2_SB(sb)->s_feature_incompat & \
+ EXT##ver##_FEATURE_INCOMPAT_##flagname) != 0); \
+} \
+static inline void ext2fs_set_feature_##name(struct ext2_super_block *sb) \
+{ \
+ EXT2_SB(sb)->s_feature_incompat |= \
+ EXT##ver##_FEATURE_INCOMPAT_##flagname; \
+} \
+static inline void ext2fs_clear_feature_##name(struct ext2_super_block *sb) \
+{ \
+ EXT2_SB(sb)->s_feature_incompat &= \
+ ~EXT##ver##_FEATURE_INCOMPAT_##flagname; \
+}
+
+EXT4_FEATURE_COMPAT_FUNCS(dir_prealloc, 2, DIR_PREALLOC)
+EXT4_FEATURE_COMPAT_FUNCS(imagic_inodes, 2, IMAGIC_INODES)
+EXT4_FEATURE_COMPAT_FUNCS(journal, 3, HAS_JOURNAL)
+EXT4_FEATURE_COMPAT_FUNCS(xattr, 2, EXT_ATTR)
+EXT4_FEATURE_COMPAT_FUNCS(resize_inode, 2, RESIZE_INODE)
+EXT4_FEATURE_COMPAT_FUNCS(dir_index, 2, DIR_INDEX)
+EXT4_FEATURE_COMPAT_FUNCS(lazy_bg, 2, LAZY_BG)
+EXT4_FEATURE_COMPAT_FUNCS(exclude_bitmap, 2, EXCLUDE_BITMAP)
+EXT4_FEATURE_COMPAT_FUNCS(sparse_super2, 4, SPARSE_SUPER2)
+EXT4_FEATURE_COMPAT_FUNCS(fast_commit, 4, FAST_COMMIT)
+EXT4_FEATURE_COMPAT_FUNCS(stable_inodes, 4, STABLE_INODES)
+EXT4_FEATURE_COMPAT_FUNCS(orphan_file, 4, ORPHAN_FILE)
+
+EXT4_FEATURE_RO_COMPAT_FUNCS(sparse_super, 2, SPARSE_SUPER)
+EXT4_FEATURE_RO_COMPAT_FUNCS(large_file, 2, LARGE_FILE)
+EXT4_FEATURE_RO_COMPAT_FUNCS(huge_file, 4, HUGE_FILE)
+EXT4_FEATURE_RO_COMPAT_FUNCS(gdt_csum, 4, GDT_CSUM)
+EXT4_FEATURE_RO_COMPAT_FUNCS(dir_nlink, 4, DIR_NLINK)
+EXT4_FEATURE_RO_COMPAT_FUNCS(extra_isize, 4, EXTRA_ISIZE)
+EXT4_FEATURE_RO_COMPAT_FUNCS(has_snapshot, 4, HAS_SNAPSHOT)
+EXT4_FEATURE_RO_COMPAT_FUNCS(quota, 4, QUOTA)
+EXT4_FEATURE_RO_COMPAT_FUNCS(bigalloc, 4, BIGALLOC)
+EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum, 4, METADATA_CSUM)
+EXT4_FEATURE_RO_COMPAT_FUNCS(replica, 4, REPLICA)
+EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, 4, READONLY)
+EXT4_FEATURE_RO_COMPAT_FUNCS(project, 4, PROJECT)
+EXT4_FEATURE_RO_COMPAT_FUNCS(shared_blocks, 4, SHARED_BLOCKS)
+EXT4_FEATURE_RO_COMPAT_FUNCS(verity, 4, VERITY)
+EXT4_FEATURE_RO_COMPAT_FUNCS(orphan_present, 4, ORPHAN_PRESENT)
+
+EXT4_FEATURE_INCOMPAT_FUNCS(compression, 2, COMPRESSION)
+EXT4_FEATURE_INCOMPAT_FUNCS(filetype, 2, FILETYPE)
+EXT4_FEATURE_INCOMPAT_FUNCS(journal_needs_recovery, 3, RECOVER)
+EXT4_FEATURE_INCOMPAT_FUNCS(journal_dev, 3, JOURNAL_DEV)
+EXT4_FEATURE_INCOMPAT_FUNCS(meta_bg, 2, META_BG)
+EXT4_FEATURE_INCOMPAT_FUNCS(extents, 3, EXTENTS)
+EXT4_FEATURE_INCOMPAT_FUNCS(64bit, 4, 64BIT)
+EXT4_FEATURE_INCOMPAT_FUNCS(mmp, 4, MMP)
+EXT4_FEATURE_INCOMPAT_FUNCS(flex_bg, 4, FLEX_BG)
+EXT4_FEATURE_INCOMPAT_FUNCS(ea_inode, 4, EA_INODE)
+EXT4_FEATURE_INCOMPAT_FUNCS(dirdata, 4, DIRDATA)
+EXT4_FEATURE_INCOMPAT_FUNCS(csum_seed, 4, CSUM_SEED)
+EXT4_FEATURE_INCOMPAT_FUNCS(largedir, 4, LARGEDIR)
+EXT4_FEATURE_INCOMPAT_FUNCS(inline_data, 4, INLINE_DATA)
+EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, 4, ENCRYPT)
+EXT4_FEATURE_INCOMPAT_FUNCS(casefold, 4, CASEFOLD)
+
+#define EXT2_FEATURE_COMPAT_SUPP 0
+#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
+ EXT4_FEATURE_INCOMPAT_MMP| \
+ EXT4_FEATURE_INCOMPAT_LARGEDIR| \
+ EXT4_FEATURE_INCOMPAT_EA_INODE)
+#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| \
+ EXT4_FEATURE_RO_COMPAT_VERITY)
+
+/*
+ * 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
+
+static inline int ext4_hash_in_dirent(const struct ext2_inode *inode)
+{
+ return (inode->i_flags & EXT4_ENCRYPT_FL) &&
+ (inode->i_flags & EXT4_CASEFOLD_FL);
+}
+
+/*
+ * 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.
+ *
+ * This structure is deprecated due to endian issues. Please use struct
+ * ext2_dir_entry and accessor functions
+ * ext2fs_dirent_name_len
+ * ext2fs_dirent_set_name_len
+ * ext2fs_dirent_file_type
+ * ext2fs_dirent_set_file_type
+ * to get and set name_len and file_type fields.
+ */
+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 */
+};
+
+/*
+ * Hashes for ext4_dir_entry for casefolded and ecrypted directories.
+ * This is located at the first 4 bit aligned location after the name.
+ */
+
+struct ext2_dir_entry_hash {
+ __le32 hash;
+ __le32 minor_hash;
+};
+
+#define EXT2_DIRENT_HASHES(entry) \
+ ((struct ext2_dir_entry_hash *) &entry->name[\
+ (ext2fs_dirent_name_len(entry) + \
+ EXT2_DIR_ROUND) & ~EXT2_DIR_ROUND])
+#define EXT2_DIRENT_HASH(entry) \
+ ext2fs_le32_to_cpu(EXT2_DIRENT_HASHES(entry)->hash)
+#define EXT2_DIRENT_MINOR_HASH(entry) \
+ ext2fs_le32_to_cpu(EXT2_DIRENT_HASHES(entry)->minor_hash)
+
+/*
+ * This is a bogus directory entry at the end of each leaf block that
+ * records checksums.
+ */
+struct ext2_dir_entry_tail {
+ __u32 det_reserved_zero1; /* Pretend to be unused */
+ __u16 det_rec_len; /* 12 */
+ __u16 det_reserved_name_len; /* 0xDE00, fake namelen/filetype */
+ __u32 det_checksum; /* crc32c(uuid+inode+dirent) */
+};
+
+/*
+ * 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
+
+/*
+ * Annoyingly, e2fsprogs always swab16s ext2_dir_entry.name_len, so we
+ * have to build ext2_dir_entry_tail with that assumption too. This
+ * constant helps to build the dir_entry_tail to look like it has an
+ * "invalid" file type.
+ */
+#define EXT2_DIR_NAME_LEN_CSUM 0xDE00
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_ENTRY_HEADER_LEN 8
+#define EXT2_DIR_ENTRY_HASH_LEN 8
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) ext2fs_dir_rec_len(name_len, 0)
+
+static inline unsigned int ext2fs_dir_rec_len(__u8 name_len,
+ int extended)
+{
+ int rec_len = (name_len + EXT2_DIR_ENTRY_HEADER_LEN + EXT2_DIR_ROUND);
+
+ rec_len &= ~EXT2_DIR_ROUND;
+ if (extended)
+ rec_len += EXT2_DIR_ENTRY_HASH_LEN;
+ return rec_len;
+}
+
+#define EXT4_ORPHAN_BLOCK_MAGIC 0x0b10ca04
+
+/* Structure at the tail of orphan block */
+struct ext4_orphan_block_tail {
+ __u32 ob_magic;
+ __u32 ob_checksum;
+};
+
+/*
+ * Constants for ext4's extended time encoding
+ */
+#define EXT4_EPOCH_BITS 2
+#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
+#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
+
+/*
+ * This structure is used for multiple mount protection. It is written
+ * into the block number saved in the s_mmp_block field in the superblock.
+ * Programs that check MMP should assume that if SEQ_FSCK (or any unknown
+ * code above SEQ_MAX) is present then it is NOT safe to use the filesystem,
+ * regardless of how old the timestamp is.
+ *
+ * The timestamp in the MMP structure will be updated by e2fsck at some
+ * arbitrary intervals (start of passes, after every few groups of inodes
+ * in pass1 and pass1b). There is no guarantee that e2fsck is updating
+ * the MMP block in a timely manner, and the updates it does are purely
+ * for the convenience of the sysadmin and not for automatic validation.
+ *
+ * Note: Only the mmp_seq value is used to determine whether the MMP block
+ * is being updated. The mmp_time, mmp_nodename, and mmp_bdevname
+ * fields are only for informational purposes for the administrator,
+ * due to clock skew between nodes and hostname HA service takeover.
+ */
+#define EXT4_MMP_MAGIC 0x004D4D50U /* ASCII for MMP */
+#define EXT4_MMP_SEQ_CLEAN 0xFF4D4D50U /* mmp_seq value for clean unmount */
+#define EXT4_MMP_SEQ_FSCK 0xE24D4D50U /* mmp_seq value when being fscked */
+#define EXT4_MMP_SEQ_MAX 0xE24D4D4FU /* maximum valid mmp_seq value */
+
+/* Not endian-annotated; it's swapped at read/write time */
+struct mmp_struct {
+ __u32 mmp_magic; /* Magic number for MMP */
+ __u32 mmp_seq; /* Sequence no. updated periodically */
+ __u64 mmp_time; /* Time last updated (seconds) */
+ __u8 mmp_nodename[64] __nonstring; /* Node updating MMP block, no NUL? */
+ __u8 mmp_bdevname[32] __nonstring; /* Bdev updating MMP block, no NUL? */
+ __u16 mmp_check_interval; /* Changed mmp_check_interval */
+ __u16 mmp_pad1;
+ __u32 mmp_pad2[226];
+ __u32 mmp_checksum; /* crc32c(uuid+mmp_block) */
+};
+
+/*
+ * Default interval for MMP update in seconds.
+ */
+#define EXT4_MMP_UPDATE_INTERVAL 5
+
+/*
+ * Maximum interval for MMP update in seconds.
+ */
+#define EXT4_MMP_MAX_UPDATE_INTERVAL 300
+
+/*
+ * Minimum interval for MMP checking in seconds.
+ */
+#define EXT4_MMP_MIN_CHECK_INTERVAL 5
+
+/*
+ * Minimum size of inline data.
+ */
+#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__u32) * EXT2_N_BLOCKS))
+
+/*
+ * Size of a parent inode in inline data directory.
+ */
+#define EXT4_INLINE_DATA_DOTDOT_SIZE (4)
+
+#define EXT4_ENC_UTF8_12_1 1
+
+#define EXT4_ENC_STRICT_MODE_FL (1 << 0) /* Reject invalid sequences */
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/lib/ext2fs/ext2_io.h b/lib/ext2fs/ext2_io.h
new file mode 100644
index 0000000..679184e
--- /dev/null
+++ b/lib/ext2fs/ext2_io.h
@@ -0,0 +1,179 @@
+/*
+ * 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/ext2_types.h>
+
+/*
+ * ext2_loff_t is defined here since unix_io.c needs it.
+ */
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long ext2_loff_t;
+#else
+typedef long ext2_loff_t;
+#endif
+
+/* 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 CHANNEL_FLAGS_BLOCK_DEVICE 0x04
+#define CHANNEL_FLAGS_THREADS 0x08
+
+#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;
+ int align;
+};
+
+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);
+ errcode_t (*cache_readahead)(io_channel channel,
+ unsigned long long block,
+ unsigned long long count);
+ errcode_t (*zeroout)(io_channel channel, unsigned long long block,
+ unsigned long long count);
+ long reserved[14];
+};
+
+#define IO_FLAG_RW 0x0001
+#define IO_FLAG_EXCLUSIVE 0x0002
+#define IO_FLAG_DIRECT_IO 0x0004
+#define IO_FLAG_FORCE_BOUNCE 0x0008
+#define IO_FLAG_THREADS 0x0010
+#define IO_FLAG_NOCACHE 0x0020
+
+/*
+ * 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);
+extern errcode_t io_channel_zeroout(io_channel channel,
+ unsigned long long block,
+ unsigned long long count);
+extern errcode_t io_channel_alloc_buf(io_channel channel,
+ int count, void *ptr);
+extern errcode_t io_channel_cache_readahead(io_channel io,
+ unsigned long long block,
+ unsigned long long count);
+
+#ifdef _WIN32
+/* windows_io.c */
+extern io_manager windows_io_manager;
+#define default_io_manager windows_io_manager
+#else
+/* unix_io.c */
+extern io_manager unix_io_manager;
+extern io_manager unixfd_io_manager;
+#define default_io_manager unix_io_manager
+#endif
+
+/* sparse_io.c */
+extern io_manager sparse_io_manager;
+extern io_manager sparsefd_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/ext2fs/ext2_types.h.in b/lib/ext2fs/ext2_types.h.in
new file mode 100644
index 0000000..98cc65b
--- /dev/null
+++ b/lib/ext2fs/ext2_types.h.in
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+#if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \
+ !defined(_EXT2_TYPES_H))
+#define _EXT2_TYPES_H
+
+@ASM_TYPES_HEADER@
+
+#ifndef HAVE___U8
+#define HAVE___U8
+#ifdef __U8_TYPEDEF
+typedef __U8_TYPEDEF __u8;
+#else
+typedef unsigned char __u8;
+#endif
+#endif /* HAVE___U8 */
+
+#ifndef HAVE___S8
+#define HAVE___S8
+#ifdef __S8_TYPEDEF
+typedef __S8_TYPEDEF __s8;
+#else
+typedef signed char __s8;
+#endif
+#endif /* HAVE___S8 */
+
+#ifndef HAVE___U16
+#define HAVE___U16
+#ifdef __U16_TYPEDEF
+typedef __U16_TYPEDEF __u16;
+#else
+#if (@SIZEOF_INT@ == 2)
+typedef unsigned int __u16;
+#else
+#if (@SIZEOF_SHORT@ == 2)
+typedef unsigned short __u16;
+#else
+#undef HAVE___U16
+ ?==error: undefined 16 bit type
+#endif /* SIZEOF_SHORT == 2 */
+#endif /* SIZEOF_INT == 2 */
+#endif /* __U16_TYPEDEF */
+#endif /* HAVE___U16 */
+
+#ifndef HAVE___S16
+#define HAVE___S16
+#ifdef __S16_TYPEDEF
+typedef __S16_TYPEDEF __s16;
+#else
+#if (@SIZEOF_INT@ == 2)
+typedef int __s16;
+#else
+#if (@SIZEOF_SHORT@ == 2)
+typedef short __s16;
+#else
+#undef HAVE___S16
+ ?==error: undefined 16 bit type
+#endif /* SIZEOF_SHORT == 2 */
+#endif /* SIZEOF_INT == 2 */
+#endif /* __S16_TYPEDEF */
+#endif /* HAVE___S16 */
+
+#ifndef HAVE___U32
+#define HAVE___U32
+#ifdef __U32_TYPEDEF
+typedef __U32_TYPEDEF __u32;
+#else
+#if (@SIZEOF_INT@ == 4)
+typedef unsigned int __u32;
+#else
+#if (@SIZEOF_LONG@ == 4)
+typedef unsigned long __u32;
+#else
+#if (@SIZEOF_SHORT@ == 4)
+typedef unsigned short __u32;
+#else
+#undef HAVE___U32
+ ?== error: undefined 32 bit type
+#endif /* SIZEOF_SHORT == 4 */
+#endif /* SIZEOF_LONG == 4 */
+#endif /* SIZEOF_INT == 4 */
+#endif /* __U32_TYPEDEF */
+#endif /* HAVE___U32 */
+
+#ifndef HAVE___S32
+#define HAVE___S32
+#ifdef __S32_TYPEDEF
+typedef __S32_TYPEDEF __s32;
+#else
+#if (@SIZEOF_INT@ == 4)
+typedef int __s32;
+#else
+#if (@SIZEOF_LONG@ == 4)
+typedef long __s32;
+#else
+#if (@SIZEOF_SHORT@ == 4)
+typedef short __s32;
+#else
+#undef HAVE___S32
+ ?== error: undefined 32 bit type
+#endif /* SIZEOF_SHORT == 4 */
+#endif /* SIZEOF_LONG == 4 */
+#endif /* SIZEOF_INT == 4 */
+#endif /* __S32_TYPEDEF */
+#endif /* HAVE___S32 */
+
+#ifndef HAVE___U64
+#define HAVE___U64
+#ifdef __U64_TYPEDEF
+typedef __U64_TYPEDEF __u64;
+#else
+#if (@SIZEOF_INT@ == 8)
+typedef unsigned int __u64;
+#else
+#if (@SIZEOF_LONG_LONG@ == 8)
+typedef unsigned long long __u64;
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef unsigned long __u64;
+#else
+#undef HAVE___U64
+ ?== error: undefined 64 bit type
+#endif /* SIZEOF_LONG_LONG == 8 */
+#endif /* SIZEOF_LONG == 8 */
+#endif /* SIZEOF_INT == 8 */
+#endif /* __U64_TYPEDEF */
+#endif /* HAVE___U64 */
+
+#ifndef HAVE___S64
+#define HAVE___S64
+#ifdef __S64_TYPEDEF
+typedef __S64_TYPEDEF __s64;
+#else
+#if (@SIZEOF_INT@ == 8)
+typedef int __s64;
+#else
+#if (@SIZEOF_LONG_LONG@ == 8)
+#if defined(__GNUC__)
+typedef __signed__ long long __s64;
+#else
+typedef signed long long __s64;
+#endif /* __GNUC__ */
+#else
+#if (@SIZEOF_LONG@ == 8)
+typedef long __s64;
+#else
+#undef HAVE___S64
+ ?== error: undefined 64 bit type
+#endif /* SIZEOF_LONG_LONG == 8 */
+#endif /* SIZEOF_LONG == 8 */
+#endif /* SIZEOF_INT == 8 */
+#endif /* __S64_TYPEDEF */
+#endif /* HAVE___S64 */
+
+#undef __S8_TYPEDEF
+#undef __U8_TYPEDEF
+#undef __S16_TYPEDEF
+#undef __U16_TYPEDEF
+#undef __S32_TYPEDEF
+#undef __U32_TYPEDEF
+#undef __S64_TYPEDEF
+#undef __U64_TYPEDEF
+
+#endif /* _*_TYPES_H */
+
+#include <stdint.h>
+
+/* endian checking stuff */
+#ifndef EXT2_ENDIAN_H_
+#define EXT2_ENDIAN_H_
+
+#ifdef __CHECKER__
+# ifndef __bitwise
+# define __bitwise __attribute__((bitwise))
+# endif
+#define __force __attribute__((force))
+#else
+# ifndef __bitwise
+# define __bitwise
+# endif
+#define __force
+#endif
+
+typedef __u16 __bitwise __le16;
+typedef __u32 __bitwise __le32;
+typedef __u64 __bitwise __le64;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __be64;
+
+#endif /* EXT2_ENDIAN_H_ */
+
+@PUBLIC_CONFIG_HEADER@
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
new file mode 100644
index 0000000..72c60d2
--- /dev/null
+++ b/lib/ext2fs/ext2fs.h
@@ -0,0 +1,2224 @@
+/*
+ * 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
+
+#ifndef __nonstring
+#ifdef __has_attribute
+#if __has_attribute(__nonstring__)
+#define __nonstring __attribute__((__nonstring__))
+#else
+#define __nonstring
+#endif /* __has_attribute(__nonstring__) */
+#else
+# define __nonstring
+#endif /* __has_attribute */
+#endif /* __nonstring */
+
+#ifdef CONFIG_TDB
+#define EXT2FS_NO_TDB_UNUSED
+#else
+#define EXT2FS_NO_TDB_UNUSED EXT2FS_ATTR((unused))
+#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
+
+#define UUID_STR_SIZE 37
+
+/*
+ * 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 <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#if EXT2_FLAT_INCLUDES
+#include "e2_types.h"
+#include "ext2_fs.h"
+#include "ext3_extents.h"
+#else
+#include <ext2fs/ext2_types.h>
+#include <ext2fs/ext2_fs.h>
+#include <ext2fs/ext3_extents.h>
+#endif /* EXT2_FLAT_INCLUDES */
+
+typedef __u32 __bitwise ext2_ino_t;
+typedef __u32 __bitwise blk_t;
+typedef __u64 __bitwise blk64_t;
+typedef __u32 __bitwise dgrp_t;
+typedef __s32 __bitwise ext2_off_t;
+typedef __s64 __bitwise ext2_off64_t;
+typedef __s64 __bitwise e2_blkcnt_t;
+typedef __u32 __bitwise ext2_dirhash_t;
+
+#if EXT2_FLAT_INCLUDES
+#include "com_err.h"
+#include "ext2_io.h"
+#include "ext2_err.h"
+#include "ext2_ext_attr.h"
+#else
+#include <et/com_err.h>
+#include <ext2fs/ext2_io.h>
+#include <ext2fs/ext2_err.h>
+#include <ext2fs/ext2_ext_attr.h>
+#endif
+
+#include "hashmap.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
+
+struct ext2fs_struct_generic_bitmap_base {
+ errcode_t magic;
+ ext2_filsys fs;
+};
+
+typedef struct ext2fs_struct_generic_bitmap_base *ext2fs_generic_bitmap;
+typedef struct ext2fs_struct_generic_bitmap_base *ext2fs_inode_bitmap;
+typedef struct ext2fs_struct_generic_bitmap_base *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
+#define EXT2_FLAG_SKIP_MMP 0x100000
+#define EXT2_FLAG_IGNORE_CSUM_ERRORS 0x200000
+#define EXT2_FLAG_SHARE_DUP 0x400000
+#define EXT2_FLAG_IGNORE_SB_ERRORS 0x800000
+#define EXT2_FLAG_BBITMAP_TAIL_PROBLEM 0x1000000
+#define EXT2_FLAG_IBITMAP_TAIL_PROBLEM 0x2000000
+#define EXT2_FLAG_THREADS 0x4000000
+#define EXT2_FLAG_IGNORE_SWAP_DIRENT 0x8000000
+
+/*
+ * 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
+ */
+#define EXT2_MKJOURNAL_V1_SUPER 0x0000001 /* create V1 superblock (deprecated) */
+#define EXT2_MKJOURNAL_LAZYINIT 0x0000002 /* don't zero journal inode before use*/
+#define EXT2_MKJOURNAL_NO_MNT_CHECK 0x0000004 /* don't check mount status */
+
+/*
+ * Normal journal area size to fast commit area size ratio. This is used to
+ * set default size of fast commit area.
+ */
+#define EXT2_JOURNAL_TO_FC_BLKS_RATIO 64
+
+struct blk_alloc_ctx;
+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 fragsize;
+ dgrp_t group_desc_count;
+ unsigned long desc_blocks;
+ struct opaque_ext2_group_desc * group_desc;
+ unsigned 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;
+ int cluster_ratio_bits;
+ __u16 default_bitmap_type;
+ __u16 pad;
+ /*
+ * Reserved for future expansion
+ */
+ __u32 reserved[5];
+
+ /*
+ * 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);
+ errcode_t (*get_alloc_block2)(ext2_filsys fs, blk64_t goal,
+ blk64_t *ret, struct blk_alloc_ctx *ctx);
+ void (*block_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse);
+
+ /*
+ * Buffers for Multiple mount protection(MMP) block.
+ */
+ void *mmp_buf;
+ void *mmp_cmp;
+ int mmp_fd;
+
+ /*
+ * Time at which e2fsck last updated the MMP block.
+ */
+ long mmp_last_written;
+
+ /* progress operation functions */
+ struct ext2fs_progress_ops *progress_ops;
+
+ /* Precomputed FS UUID checksum for seeding other checksums */
+ __u32 csum_seed;
+
+ io_channel journal_io;
+ char *journal_name;
+
+ /* New block range allocation hooks */
+ errcode_t (*new_range)(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, blk64_t *pblk, blk64_t *plen);
+ void (*block_alloc_stats_range)(ext2_filsys fs, blk64_t blk, blk_t num,
+ int inuse);
+
+ /* hashmap for SHA of data blocks */
+ struct ext2fs_hashmap* block_sha_map;
+
+ const struct ext2fs_nls_table *encoding;
+};
+
+#if EXT2_FLAT_INCLUDES
+#include "e2_bitops.h"
+#else
+#include <ext2fs/bitops.h>
+#endif
+
+/*
+ * 64-bit bitmap backend types
+ */
+#define EXT2FS_BMAP64_BITARRAY 1
+#define EXT2FS_BMAP64_RBTREE 2
+#define EXT2FS_BMAP64_AUTODIR 3
+
+/*
+ * Return flags for the block iterator functions
+ */
+#define BLOCK_CHANGED 1
+#define BLOCK_ABORT 2
+#define BLOCK_ERROR 4
+#define BLOCK_INLINE_DATA_CHANGED 8
+
+/*
+ * Block iterate flags
+ *
+ * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the iterator
+ * 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 contained 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)
+
+#define BLOCK_ALLOC_UNKNOWN 0
+#define BLOCK_ALLOC_DATA 1
+#define BLOCK_ALLOC_METADATA 2
+
+struct blk_alloc_ctx {
+ ext2_ino_t ino;
+ struct ext2_inode *inode;
+ blk64_t lblk;
+ int flags;
+};
+
+#if 0
+/*
+ * Flags for ext2fs_move_blocks
+ */
+#define EXT2_BMOVE_GET_DBLIST 0x0001
+#define EXT2_BMOVE_DEBUG 0x0002
+#endif
+
+/*
+ * 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 extent 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_FLAG_INCLUDE_CSUM 4
+#define DIRENT_FLAG_INCLUDE_INLINE_DATA 8
+
+#define DIRENT_DOT_FILE 1
+#define DIRENT_DOT_DOT_FILE 2
+#define DIRENT_OTHER_FILE 3
+#define DIRENT_DELETED_FILE 4
+#define DIRENT_CHECKSUM 5
+
+/*
+ * 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
+#define EXT2_SF_WARN_GARBAGE_INODES 0x0020
+
+/*
+ * 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
+#define EXT2_MF_EXTFS 32
+
+/*
+ * 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
+#define EXT2_ICOUNT_OPT_FULLMAP 0x02
+
+typedef struct ext2_icount *ext2_icount_t;
+
+/*
+ * Flags for ext2fs_bmap
+ */
+#define BMAP_ALLOC 0x0001
+#define BMAP_SET 0x0002
+#define BMAP_UNINIT 0x0004
+#define BMAP_ZERO 0x0008
+
+/*
+ * Returned flags from ext2fs_bmap
+ */
+#define BMAP_RET_UNINIT 0x0001
+
+/*
+ * Flags for ext2fs_read_inode2
+ */
+#define READ_INODE_NOCSUM 0x0001
+
+/*
+ * Flags for ext2fs_write_inode2
+ */
+#define WRITE_INODE_NOCSUM 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)
+
+/*
+ * 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|\
+ EXT4_FEATURE_COMPAT_SPARSE_SUPER2|\
+ EXT4_FEATURE_COMPAT_FAST_COMMIT|\
+ EXT4_FEATURE_COMPAT_STABLE_INODES|\
+ EXT4_FEATURE_COMPAT_ORPHAN_FILE)
+
+#ifdef CONFIG_MMP
+#define EXT4_LIB_INCOMPAT_MMP EXT4_FEATURE_INCOMPAT_MMP
+#else
+#define EXT4_LIB_INCOMPAT_MMP (0)
+#endif
+
+#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_EA_INODE|\
+ EXT4_LIB_INCOMPAT_MMP|\
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA|\
+ EXT4_FEATURE_INCOMPAT_ENCRYPT|\
+ EXT4_FEATURE_INCOMPAT_CASEFOLD|\
+ EXT4_FEATURE_INCOMPAT_CSUM_SEED|\
+ EXT4_FEATURE_INCOMPAT_LARGEDIR)
+
+#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|\
+ EXT4_FEATURE_RO_COMPAT_BIGALLOC|\
+ EXT4_FEATURE_RO_COMPAT_QUOTA|\
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM|\
+ EXT4_FEATURE_RO_COMPAT_READONLY |\
+ EXT4_FEATURE_RO_COMPAT_PROJECT |\
+ EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS |\
+ EXT4_FEATURE_RO_COMPAT_VERITY |\
+ EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT)
+
+/*
+ * 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_REPLICA)
+
+
+/* Translate a block number to a cluster number */
+#define EXT2FS_CLUSTER_RATIO(fs) (1 << (fs)->cluster_ratio_bits)
+#define EXT2FS_CLUSTER_MASK(fs) (EXT2FS_CLUSTER_RATIO(fs) - 1)
+#define EXT2FS_B2C(fs, blk) ((blk) >> (fs)->cluster_ratio_bits)
+/* Translate a cluster number to a block number */
+#define EXT2FS_C2B(fs, cluster) ((cluster) << (fs)->cluster_ratio_bits)
+/* Translate # of blks to # of clusters */
+#define EXT2FS_NUM_B2C(fs, blks) (((blks) + EXT2FS_CLUSTER_MASK(fs)) >> \
+ (fs)->cluster_ratio_bits)
+
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+typedef struct stat64 ext2fs_struct_stat;
+#else
+typedef struct stat ext2fs_struct_stat;
+#endif
+
+/*
+ * For ext2fs_close2() and ext2fs_flush2(), this flag allows you to
+ * avoid the fsync call.
+ */
+#define EXT2_FLAG_FLUSH_NO_SYNC 1
+
+/*
+ * Modify and iterate extended attributes
+ */
+struct ext2_xattr_handle;
+#define XATTR_ABORT 1
+#define XATTR_CHANGED 2
+
+/*
+ * flags for ext2fs_rw_bitmaps()
+ */
+#define EXT2FS_BITMAPS_WRITE 0x0001
+#define EXT2FS_BITMAPS_BLOCK 0x0002
+#define EXT2FS_BITMAPS_INODE 0x0004
+#define EXT2FS_BITMAPS_VALID_FLAGS 0x0007
+
+/*
+ * function prototypes
+ */
+static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
+{
+ return ext2fs_has_feature_metadata_csum(fs->super) ||
+ ext2fs_has_feature_gdt_csum(fs->super);
+}
+
+/* The LARGE_FILE feature should be set if we have stored files 2GB+ in size */
+static inline int ext2fs_needs_large_file_feature(unsigned long long file_size)
+{
+ return file_size >= 0x80000000ULL;
+}
+
+/* alloc.c */
+extern void ext2fs_clear_block_uninit(ext2_filsys fs, dgrp_t group);
+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_new_block3(ext2_filsys fs, blk64_t goal,
+ ext2fs_block_bitmap map, blk64_t *ret,
+ struct blk_alloc_ctx *ctx);
+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 errcode_t ext2fs_alloc_block3(ext2_filsys fs, blk64_t goal,
+ char *block_buf, blk64_t *ret,
+ struct blk_alloc_ctx *ctx);
+
+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));
+blk64_t ext2fs_find_inode_goal(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t lblk);
+extern void ext2fs_set_new_range_callback(ext2_filsys fs,
+ errcode_t (*func)(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, blk64_t *pblk, blk64_t *plen),
+ errcode_t (**old)(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, blk64_t *pblk, blk64_t *plen));
+extern void ext2fs_set_block_alloc_stats_range_callback(ext2_filsys fs,
+ void (*func)(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse),
+ void (**old)(ext2_filsys fs, blk64_t blk,
+ blk_t num, int inuse));
+#define EXT2_NEWRANGE_FIXED_GOAL (0x1)
+#define EXT2_NEWRANGE_MIN_LENGTH (0x2)
+#define EXT2_NEWRANGE_ALL_FLAGS (0x3)
+errcode_t ext2fs_new_range(ext2_filsys fs, int flags, blk64_t goal,
+ blk64_t len, ext2fs_block_bitmap map, blk64_t *pblk,
+ blk64_t *plen);
+#define EXT2_ALLOCRANGE_FIXED_GOAL (0x1)
+#define EXT2_ALLOCRANGE_ZERO_BLOCKS (0x2)
+#define EXT2_ALLOCRANGE_ALL_FLAGS (0x3)
+errcode_t ext2fs_alloc_range(ext2_filsys fs, int flags, blk64_t goal,
+ blk_t len, 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);
+void ext2fs_block_alloc_stats_range(ext2_filsys fs, blk64_t blk,
+ blk_t num, 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_allocate_block_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_block_bitmap *ret);
+extern errcode_t ext2fs_allocate_subcluster_bitmap(ext2_filsys fs,
+ const char *descr,
+ ext2fs_block_bitmap *ret);
+extern int ext2fs_get_bitmap_granularity(ext2fs_block_bitmap bitmap);
+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_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 __u32 ext2fs_inode_bitmap_checksum(ext2_filsys fs, dgrp_t group);
+extern __u32 ext2fs_block_bitmap_checksum(ext2_filsys fs, dgrp_t group);
+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 int ext2fs_group_blocks_count(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_get_stat_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_csum(ext2_filsys fs, 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 __u32 ext2fs_inode_bitmap_csum(ext2_filsys fs, dgrp_t group);
+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(ext2_filsys fs,
+ const struct ext2_inode *inode);
+extern void ext2fs_file_acl_block_set(ext2_filsys fs,
+ struct ext2_inode *inode, blk64_t blk);
+extern errcode_t ext2fs_inode_size_set(ext2_filsys fs, struct ext2_inode *inode,
+ ext2_off64_t size);
+
+/* 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);
+errcode_t ext2fs_map_cluster_block(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t lblk,
+ blk64_t *pblk);
+
+#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_close2(ext2_filsys fs, int flags);
+extern errcode_t ext2fs_close_free(ext2_filsys *fs);
+extern errcode_t ext2fs_flush(ext2_filsys fs);
+extern errcode_t ext2fs_flush2(ext2_filsys fs, int flags);
+extern int ext2fs_bg_has_super(ext2_filsys fs, dgrp_t 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);
+
+/* crc32c.c */
+extern __u32 ext2fs_crc32_be(__u32 crc, unsigned char const *p, size_t len);
+extern __u32 ext2fs_crc32c_le(__u32 crc, unsigned char const *p, size_t len);
+
+/* csum.c */
+extern void ext2fs_init_csum_seed(ext2_filsys fs);
+extern errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp);
+extern int ext2fs_mmp_csum_verify(ext2_filsys, struct mmp_struct *mmp);
+extern int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb);
+extern errcode_t ext2fs_superblock_csum_set(ext2_filsys fs,
+ struct ext2_super_block *sb);
+extern int ext2fs_superblock_csum_verify(ext2_filsys fs,
+ struct ext2_super_block *sb);
+extern errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs,
+ ext2_ino_t inum, blk64_t block,
+ struct ext2_ext_attr_header *hdr);
+extern int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ blk64_t block,
+ struct ext2_ext_attr_header *hdr);
+#define EXT2_DIRENT_TAIL(block, blocksize) \
+ ((struct ext2_dir_entry_tail *)(((char *)(block)) + \
+ (blocksize) - sizeof(struct ext2_dir_entry_tail)))
+
+extern void ext2fs_initialize_dirent_tail(ext2_filsys fs,
+ struct ext2_dir_entry_tail *t);
+extern int ext2fs_dirent_has_tail(ext2_filsys fs,
+ struct ext2_dir_entry *dirent);
+extern int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
+extern int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
+extern errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent);
+extern errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs,
+ struct ext2_dir_entry *dirent,
+ struct ext2_dx_countlimit **cc,
+ int *offset);
+extern errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_dir_entry *dirent,
+ __u32 *crc, struct ext2_dx_tail **ret_t);
+extern errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs,
+ ext2_ino_t inum,
+ struct ext3_extent_header *eh);
+extern int ext2fs_extent_block_csum_verify(ext2_filsys fs,
+ ext2_ino_t inum,
+ struct ext3_extent_header *eh);
+extern errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
+extern int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
+extern errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
+extern int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
+ char *bitmap, int size);
+extern errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
+extern int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
+ struct ext2_inode_large *inode);
+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);
+extern __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group);
+
+/* dblist.c */
+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_dblist_iterate3(ext2_dblist dblist,
+ int (*func)(ext2_filsys fs, struct ext2_db_entry2 *db_info,
+ void *priv_data),
+ unsigned long long start,
+ unsigned long long count,
+ 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);
+
+#if 0
+/* digest_encode.c */
+#define EXT2FS_DIGEST_SIZE EXT2FS_SHA256_LENGTH
+extern int ext2fs_digest_encode(const char *src, int len, char *dst);
+extern int ext2fs_digest_decode(const char *src, int len, char *dst);
+#endif
+
+/* 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_read_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags, ext2_ino_t ino);
+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);
+extern errcode_t ext2fs_write_dir_block4(ext2_filsys fs, blk64_t block,
+ void *buf, int flags, ext2_ino_t ino);
+
+/* 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);
+
+extern errcode_t ext2fs_dirhash2(int version, const char *name, int len,
+ const struct ext2fs_nls_table *charset,
+ int hash_flags,
+ 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 __u32 ext2fs_ext_attr_hash_entry_signed(struct ext2_ext_attr_entry *entry,
+ void *data);
+extern errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ void *data, __u32 *hash);
+extern errcode_t ext2fs_ext_attr_hash_entry3(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ void *data, __u32 *hash,
+ __u32 *signed_hash);
+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_read_ext_attr3(ext2_filsys fs, blk64_t block,
+ void *buf, ext2_ino_t inum);
+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_write_ext_attr3(ext2_filsys fs, blk64_t block,
+ void *buf, ext2_ino_t inum);
+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);
+extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
+ char *block_buf,
+ int adjust, __u32 *newcount,
+ ext2_ino_t inum);
+errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattrs_read_inode(struct ext2_xattr_handle *handle,
+ struct ext2_inode_large *inode);
+errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
+ int (*func)(char *name, char *value,
+ size_t value_len, void *data),
+ void *data);
+errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
+ void **value, size_t *value_len);
+errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
+ const char *key,
+ const void *value,
+ size_t value_len);
+errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
+ const char *key);
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle);
+errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
+errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode);
+errcode_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle, size_t *count);
+errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size);
+#define XATTR_HANDLE_FLAG_RAW 0x0001
+errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
+ unsigned int *new_flags, unsigned int *old_flags);
+extern void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
+ struct ext2_ext_attr_entry *end);
+extern __u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode);
+extern void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash);
+extern __u64 ext2fs_get_ea_inode_ref(struct ext2_inode *inode);
+extern void ext2fs_set_ea_inode_ref(struct ext2_inode *inode, __u64 ref_count);
+
+/* 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_node_split(ext2_extent_handle_t handle);
+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);
+extern errcode_t ext2fs_extent_goto2(ext2_extent_handle_t handle,
+ int leaf_level, blk64_t blk);
+extern errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle);
+extern size_t ext2fs_max_extent_depth(ext2_extent_handle_t handle);
+extern errcode_t ext2fs_fix_extents_checksums(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode);
+extern errcode_t ext2fs_count_blocks(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t *ret_count);
+extern errcode_t ext2fs_decode_extent(struct ext2fs_extent *to, void *from,
+ int len);
+
+/* fallocate.c */
+#define EXT2_FALLOCATE_ZERO_BLOCKS (0x1)
+#define EXT2_FALLOCATE_FORCE_INIT (0x2)
+#define EXT2_FALLOCATE_FORCE_UNINIT (0x4)
+#define EXT2_FALLOCATE_INIT_BEYOND_EOF (0x8)
+#define EXT2_FALLOCATE_ALL_FLAGS (0xF)
+errcode_t ext2fs_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t goal,
+ blk64_t start, blk64_t len);
+
+/* 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 ext2_ino_t ext2fs_file_get_inode_num(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);
+extern errcode_t ext2fs_find_first_zero_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 start, __u32 end,
+ __u32 *out);
+extern errcode_t ext2fs_find_first_set_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 start, __u32 end,
+ __u32 *out);
+
+/* 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);
+errcode_t ext2fs_convert_subcluster_bitmap(ext2_filsys fs,
+ ext2fs_block_bitmap *bitmap);
+errcode_t ext2fs_count_used_clusters(ext2_filsys fs, blk64_t start,
+ blk64_t end, blk64_t *out);
+
+/* get_num_dirs.c */
+extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
+
+/* 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 */
+extern int ext2fs_get_dio_alignment(int fd);
+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);
+extern errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs, int super_only);
+
+/* 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 *);
+
+/* inline.c */
+
+extern errcode_t ext2fs_get_memalign(unsigned long size,
+ unsigned long align, void *ptr);
+
+/* inline_data.c */
+extern errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino);
+extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size);
+extern errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t *size);
+extern errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t size);
+
+/* inode.c */
+extern errcode_t ext2fs_create_inode_cache(ext2_filsys fs,
+ unsigned int cache_size);
+extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
+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);
+#define EXT2_INODE_SCAN_DEFAULT_BUFFER_BLOCKS 8
+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_read_inode2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode,
+ int bufsize, int flags);
+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_inode2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode,
+ int bufsize, int flags);
+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 */
+/*
+ * NOTE: This function removes from an inode the blocks "start", "end", and
+ * every block in between.
+ */
+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);
+extern errcode_t ext2fs_new_dir_inline_data(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, __u32 *iblock);
+
+/* nls_utf8.c */
+extern const struct ext2fs_nls_table *ext2fs_load_nls_table(int encoding);
+extern int ext2fs_check_encoded_name(const struct ext2fs_nls_table *table,
+ char *s, size_t len, char **pos);
+extern int ext2fs_casefold_cmp(const struct ext2fs_nls_table *table,
+ const unsigned char *str1, size_t len1,
+ const unsigned char *str2, size_t len2);
+
+/* mkdir.c */
+extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+ const char *name);
+
+/* mkjournal.c */
+struct ext2fs_journal_params {
+ blk_t num_journal_blocks;
+ blk_t num_fc_blocks;
+};
+extern errcode_t ext2fs_get_journal_params(
+ struct ext2fs_journal_params *params, ext2_filsys fs);
+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 num_blocks, int flags,
+ char **ret_jsb);
+extern errcode_t ext2fs_create_journal_superblock2(ext2_filsys fs,
+ struct ext2fs_journal_params *params,
+ 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 num_blocks,
+ int flags);
+extern errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
+ blk64_t goal, int flags);
+extern errcode_t ext2fs_add_journal_inode3(ext2_filsys fs,
+ struct ext2fs_journal_params *params,
+ blk64_t goal, int flags);
+extern int ext2fs_default_journal_size(__u64 num_blocks);
+extern int ext2fs_journal_sb_start(int blocksize);
+
+/* openfs.c */
+extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
+ unsigned int block_size, io_manager manager,
+ 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_manager manager,
+ ext2_filsys *ret_fs);
+/*
+ * The dgrp_t argument to these two functions is not actually a group number
+ * but a block number offset within a group table! Convert with the formula
+ * (group_number / groups_per_block).
+ */
+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);
+
+/* orphan.c */
+extern errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks);
+extern errcode_t ext2fs_truncate_orphan_file(ext2_filsys fs);
+extern e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs);
+extern __u32 ext2fs_do_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino,
+ __u32 gen, blk64_t blk,
+ char *buf);
+extern errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs,
+ ext2_ino_t ino, blk64_t blk,
+ char *buf);
+extern int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino,
+ blk64_t blk, char *buf);
+
+/* get_pathname.c */
+extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+ char **name);
+
+/* link.c */
+#define EXT2FS_UNLINK_FORCE 0x1 /* Forcefully unlink even if
+ * the inode number doesn't
+ * match the dirent
+ */
+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);
+
+/* symlink.c */
+errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
+ const char *name, const char *target);
+int ext2fs_is_fast_symlink(struct ext2_inode *inode);
+
+/* mmp.c */
+errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf);
+errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf);
+errcode_t ext2fs_mmp_clear(ext2_filsys fs);
+errcode_t ext2fs_mmp_init(ext2_filsys fs);
+errcode_t ext2fs_mmp_start(ext2_filsys fs);
+errcode_t ext2fs_mmp_update(ext2_filsys fs);
+errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately);
+errcode_t ext2fs_mmp_stop(ext2_filsys fs);
+unsigned ext2fs_mmp_new_seq(void);
+
+/* 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);
+
+/* rw_bitmaps.c */
+extern errcode_t ext2fs_rw_bitmaps(ext2_filsys fs, int flags, int num_threads);
+extern errcode_t ext2fs_read_bitmaps(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_write_bitmaps(ext2_filsys fs);
+extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
+
+/*sha256.c */
+#define EXT2FS_SHA256_LENGTH 32
+#if 0
+extern void ext2fs_sha256(const unsigned char *in, unsigned long in_size,
+ unsigned char out[EXT2FS_SHA256_LENGTH]);
+#endif
+
+/* sha512.c */
+#define EXT2FS_SHA512_LENGTH 64
+extern void ext2fs_sha512(const unsigned char *in, unsigned long in_size,
+ unsigned char out[EXT2FS_SHA512_LENGTH]);
+
+/* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf, size_t size,
+ int flags);
+extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out2(ext2_filsys fs, char *buf, size_t size,
+ int flags);
+extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
+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);
+extern void ext2fs_swap_mmp(struct mmp_struct *mmp);
+
+/* unix_io.c */
+extern int ext2fs_open_file(const char *pathname, int flags, mode_t mode);
+extern int ext2fs_stat(const char *path, ext2fs_struct_stat *buf);
+extern int ext2fs_fstat(int fd, ext2fs_struct_stat *buf);
+
+/* valid_blk.c */
+extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
+extern int ext2fs_inode_has_valid_blocks2(ext2_filsys fs,
+ 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 */
+#ifdef NO_INLINE_FUNCS
+extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
+extern errcode_t ext2fs_get_memzero(unsigned long size, void *ptr);
+extern errcode_t ext2fs_get_array(unsigned long count,
+ unsigned long size, void *ptr);
+extern errcode_t ext2fs_get_arrayzero(unsigned long count,
+ unsigned long size, 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 errcode_t ext2fs_resize_array(unsigned long old_count, unsigned long count,
+ 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 dgrp_t ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
+extern dgrp_t 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 int ext2fs_htree_intnode_maxrecs(ext2_filsys fs, int blocks);
+extern unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b);
+extern __u64 ext2fs_div64_ceil(__u64 a, __u64 b);
+extern int ext2fs_dirent_name_len(const struct ext2_dir_entry *entry);
+extern void ext2fs_dirent_set_name_len(struct ext2_dir_entry *entry, int len);
+extern int ext2fs_dirent_file_type(const struct ext2_dir_entry *entry);
+extern void ext2fs_dirent_set_file_type(struct ext2_dir_entry *entry, int type);
+extern struct ext2_inode *ext2fs_inode(struct ext2_inode_large * large_inode);
+extern const struct ext2_inode *ext2fs_const_inode(const struct ext2_inode_large * large_inode);
+extern int ext2fs_inodes_per_orphan_block(ext2_filsys fs);
+extern struct ext4_orphan_block_tail *ext2fs_orphan_block_tail(ext2_filsys fs,
+ char *buf);
+#endif
+
+/*
+ * 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
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ inline
+#else
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else /* For Watcom C */
+#define _INLINE_ extern inline
+#endif /* __GNUC__ */
+#endif /* __STDC_VERSION__ >= 199901L */
+#endif
+
+#ifndef EXT2_CUSTOM_MEMORY_ROUTINES
+#include <string.h>
+/*
+ * Allocate memory. The 'ptr' arg must point to a pointer.
+ */
+_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
+{
+ void *pp;
+
+ pp = malloc(size);
+ if (!pp)
+ return EXT2_ET_NO_MEMORY;
+ memcpy(ptr, &pp, sizeof (pp));
+ return 0;
+}
+
+_INLINE_ errcode_t ext2fs_get_memzero(unsigned long size, void *ptr)
+{
+ void *pp;
+
+ pp = malloc(size);
+ if (!pp)
+ return EXT2_ET_NO_MEMORY;
+ memset(pp, 0, size);
+ memcpy(ptr, &pp, sizeof(pp));
+ return 0;
+}
+
+_INLINE_ errcode_t ext2fs_get_array(unsigned long count, unsigned long size,
+ void *ptr)
+{
+ if (count && (~0UL)/count < size)
+ return EXT2_ET_NO_MEMORY;
+ return ext2fs_get_mem(count*size, ptr);
+}
+
+_INLINE_ errcode_t ext2fs_get_arrayzero(unsigned long count,
+ unsigned long size, void *ptr)
+{
+ if (count && (~0UL)/count < size)
+ return EXT2_ET_NO_MEMORY;
+
+ return ext2fs_get_memzero((size_t)count * size, ptr);
+}
+
+/*
+ * Free memory. The 'ptr' arg must point to a pointer.
+ */
+_INLINE_ errcode_t ext2fs_free_mem(void *ptr)
+{
+ void *p;
+
+ memcpy(&p, ptr, sizeof(p));
+ free(p);
+ p = 0;
+ memcpy(ptr, &p, sizeof(p));
+ return 0;
+}
+
+/*
+ * Resize memory. The 'ptr' arg must point to a pointer.
+ */
+_INLINE_ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
+ unsigned long size, void *ptr)
+{
+ void *p;
+
+ /* Use "memcpy" for pointer assignments here to avoid problems
+ * with C99 strict type aliasing rules. */
+ memcpy(&p, ptr, sizeof(p));
+ p = realloc(p, size);
+ if (!p)
+ return EXT2_ET_NO_MEMORY;
+ memcpy(ptr, &p, sizeof(p));
+ return 0;
+}
+
+/*
+ * Resize array. The 'ptr' arg must point to a pointer.
+ */
+_INLINE_ errcode_t ext2fs_resize_array(unsigned long size,
+ unsigned long old_count,
+ unsigned long count, void *ptr)
+{
+ unsigned long old_size;
+ errcode_t retval;
+
+ if (count && (~0UL)/count < size)
+ return EXT2_ET_NO_MEMORY;
+
+ size *= count;
+ old_size = size * old_count;
+ retval = ext2fs_resize_mem(old_size, size, ptr);
+ if (retval)
+ return retval;
+
+ if (size > old_size) {
+ void *p;
+
+ memcpy(&p, ptr, sizeof(p));
+ memset((char *)p + old_size, 0, size - old_size);
+ memcpy(ptr, &p, sizeof(p));
+ }
+
+ return 0;
+}
+#endif /* Custom memory routines */
+
+/*
+ * Mark a filesystem superblock as dirty
+ */
+_INLINE_ void ext2fs_mark_super_dirty(ext2_filsys fs)
+{
+ fs->flags |= 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_ dgrp_t 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_ dgrp_t 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 (blk_t) 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 (blk_t) ext2fs_group_last_block2(fs, group);
+}
+
+_INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
+ struct ext2_inode *inode)
+{
+ return (blk_t) ext2fs_inode_data_blocks2(fs, inode);
+}
+
+_INLINE_ int ext2fs_htree_intnode_maxrecs(ext2_filsys fs, int blocks)
+{
+ int csum_size = 0;
+
+ if ((EXT2_SB(fs->super)->s_feature_ro_compat &
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) != 0)
+ csum_size = sizeof(struct ext2_dx_tail);
+ return blocks * ((fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry));
+}
+
+/*
+ * 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;
+}
+
+_INLINE_ int ext2fs_dirent_name_len(const struct ext2_dir_entry *entry)
+{
+ return entry->name_len & 0xff;
+}
+
+_INLINE_ void ext2fs_dirent_set_name_len(struct ext2_dir_entry *entry, int len)
+{
+ entry->name_len = (entry->name_len & 0xff00) | (len & 0xff);
+}
+
+_INLINE_ int ext2fs_dirent_file_type(const struct ext2_dir_entry *entry)
+{
+ return entry->name_len >> 8;
+}
+
+_INLINE_ void ext2fs_dirent_set_file_type(struct ext2_dir_entry *entry, int type)
+{
+ entry->name_len = (entry->name_len & 0xff) | (type << 8);
+}
+
+_INLINE_ struct ext2_inode *ext2fs_inode(struct ext2_inode_large * large_inode)
+{
+ /* It is always safe to convert large inode to a small inode */
+ return (struct ext2_inode *) large_inode;
+}
+
+_INLINE_ const struct ext2_inode *
+ext2fs_const_inode(const struct ext2_inode_large * large_inode)
+{
+ /* It is always safe to convert large inode to a small inode */
+ return (const struct ext2_inode *) large_inode;
+}
+
+_INLINE_ int ext2fs_inodes_per_orphan_block(ext2_filsys fs)
+{
+ return (fs->blocksize - sizeof(struct ext4_orphan_block_tail)) /
+ sizeof(__u32);
+}
+
+_INLINE_ struct ext4_orphan_block_tail *
+ext2fs_orphan_block_tail(ext2_filsys fs, char *buf)
+{
+ return (struct ext4_orphan_block_tail *)(buf + fs->blocksize -
+ sizeof(struct ext4_orphan_block_tail));
+}
+
+#undef _INLINE_
+#endif
+
+/* htree levels for ext4 */
+#define EXT4_HTREE_LEVEL_COMPAT 2
+#define EXT4_HTREE_LEVEL 3
+
+static inline unsigned int ext2_dir_htree_level(ext2_filsys fs)
+{
+ if (ext2fs_has_feature_largedir(fs->super))
+ return EXT4_HTREE_LEVEL;
+
+ return EXT4_HTREE_LEVEL_COMPAT;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EXT2FS_EXT2FS_H */
diff --git a/lib/ext2fs/ext2fs.pc.in b/lib/ext2fs/ext2fs.pc.in
new file mode 100644
index 0000000..efac85e
--- /dev/null
+++ b/lib/ext2fs/ext2fs.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ext2fs
+Description: Ext2fs library
+Version: @E2FSPROGS_VERSION@
+Requires.private: com_err
+Cflags: -I${includedir}/ext2fs -I${includedir}
+Libs: -L${libdir} -lext2fs
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
new file mode 100644
index 0000000..0687384
--- /dev/null
+++ b/lib/ext2fs/ext2fsP.h
@@ -0,0 +1,210 @@
+/*
+ * 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%
+ */
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "ext2fs.h"
+
+#define EXT2FS_MAX_NESTED_LINKS 8
+
+static inline int ext2fsP_is_disk_device(mode_t mode)
+{
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ return S_ISBLK(mode) || S_ISCHR(mode);
+#else
+ return S_ISBLK(mode);
+#endif
+}
+
+/*
+ * 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;
+ unsigned int buflen;
+ 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;
+ blk64_t buffer_blk;
+ int cache_last;
+ unsigned int cache_size;
+ int refcount;
+ struct ext2_inode_cache_ent *cache;
+};
+
+struct ext2_inode_cache_ent {
+ ext2_ino_t ino;
+ struct ext2_inode *inode;
+};
+
+/*
+ * NLS definitions
+ */
+struct ext2fs_nls_table {
+ int version;
+ const struct ext2fs_nls_ops *ops;
+};
+
+struct ext2fs_nls_ops {
+ int (*casefold)(const struct ext2fs_nls_table *charset,
+ const unsigned char *str, size_t len,
+ unsigned char *dest, size_t dlen);
+ int (*validate)(const struct ext2fs_nls_table *table,
+ char *s, size_t len, char **pos);
+ int (*casefold_cmp)(const struct ext2fs_nls_table *table,
+ const unsigned char *str1, size_t len1,
+ const unsigned char *str2, size_t len2);
+};
+
+/* 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);
+
+extern errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino);
+extern errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);
+extern int ext2fs_inline_data_dir_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ void *priv_data);
+
+/* Generic numeric progress meter */
+
+struct ext2fs_numeric_progress_struct {
+ __u64 max;
+ int log_max;
+ int skip_progress;
+};
+
+/*
+ * progress callback functions
+ */
+struct ext2fs_progress_ops {
+ void (*init)(ext2_filsys fs,
+ struct ext2fs_numeric_progress_struct * progress,
+ const char *label, __u64 max);
+ void (*update)(ext2_filsys fs,
+ struct ext2fs_numeric_progress_struct * progress,
+ __u64 val);
+ void (*close)(ext2_filsys fs,
+ struct ext2fs_numeric_progress_struct * progress,
+ const char *message);
+};
+
+extern struct ext2fs_progress_ops ext2fs_numeric_progress_ops;
+
+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
+ */
+
+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 void ext2fs_warn_bitmap32(ext2fs_generic_bitmap bitmap,const char *func);
+
+extern int ext2fs_mem_is_zero(const char *mem, size_t len);
+
+extern int ext2fs_file_block_offset_too_big(ext2_filsys fs,
+ struct ext2_inode *inode,
+ blk64_t offset);
+
+/* atexit support */
+typedef void (*ext2_exit_fn)(void *);
+errcode_t ext2fs_add_exit_fn(ext2_exit_fn fn, void *data);
+errcode_t ext2fs_remove_exit_fn(ext2_exit_fn fn, void *data);
+
+#define EXT2FS_BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2*!!(cond)]))
diff --git a/lib/ext2fs/ext3_extents.h b/lib/ext2fs/ext3_extents.h
new file mode 100644
index 0000000..309fbc8
--- /dev/null
+++ b/lib/ext2fs/ext3_extents.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2003,2004 Cluster File Systems, Inc, info@clusterfs.com
+ * Written by Alex Tomas <alex@clusterfs.com>
+ *
+ * %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 tail on-disk structure.
+ * All other extent structures are 12 bytes long. It turns out that
+ * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which
+ * covers all valid ext4 block sizes. Therefore, this tail structure can be
+ * crammed into the end of the block without having to rebalance the tree.
+ */
+struct ext3_extent_tail {
+ __le32 et_checksum; /* crc32c(uuid+inum+extent_block) */
+};
+
+/*
+ * this is extent on-disk structure
+ * it's used at the bottom of the tree
+ */
+struct ext3_extent {
+ __le32 ee_block; /* first logical block extent covers */
+ __le16 ee_len; /* number of blocks covered by extent */
+ __le16 ee_start_hi; /* high 16 bits of physical block */
+ __le32 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 {
+ __le32 ei_block; /* index covers logical blocks from 'block' */
+ __le32 ei_leaf; /* pointer to the physical block of the next *
+ * level. leaf or next index could bet here */
+ __le16 ei_leaf_hi; /* high 16 bits of physical block */
+ __le16 ei_unused;
+};
+
+/*
+ * each block (leaves and indexes), even inode-stored has header
+ */
+struct ext3_extent_header {
+ __le16 eh_magic; /* probably will support different formats */
+ __le16 eh_entries; /* number of valid entries */
+ __le16 eh_max; /* capacity of store in entries */
+ __le16 eh_depth; /* has tree real underlying blocks? */
+ __le32 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_MAX_EXTENT_LBLK (((__u64) 1 << 32) - 1)
+#define EXT_MAX_EXTENT_PBLK (((__u64) 1 << 48) - 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__) \
+ (ext2fs_le16_to_cpu((__path__)->p_hdr->eh_entries) < \
+ ext2fs_le16_to_cpu((__path__)->p_hdr->eh_max))
+#define EXT_LAST_EXTENT(__hdr__) \
+ (EXT_FIRST_EXTENT((__hdr__)) + \
+ ext2fs_le16_to_cpu((__hdr__)->eh_entries) - 1)
+#define EXT_LAST_INDEX(__hdr__) \
+ (EXT_FIRST_INDEX((__hdr__)) + \
+ ext2fs_le16_to_cpu((__hdr__)->eh_entries) - 1)
+#define EXT_MAX_EXTENT(__hdr__) \
+ (EXT_FIRST_EXTENT((__hdr__)) + \
+ ext2fs_le16_to_cpu((__hdr__)->eh_max) - 1)
+#define EXT_MAX_INDEX(__hdr__) \
+ (EXT_FIRST_INDEX((__hdr__)) + \
+ ext2fs_le16_to_cpu((__hdr__)->eh_max) - 1)
+
+#endif /* _LINUX_EXT3_EXTENTS */
+
diff --git a/lib/ext2fs/ext4_acl.h b/lib/ext2fs/ext4_acl.h
new file mode 100644
index 0000000..8d4d974
--- /dev/null
+++ b/lib/ext2fs/ext4_acl.h
@@ -0,0 +1,62 @@
+/*
+ * Ext4's on-disk acl format. From linux/fs/ext4/acl.h
+ */
+
+#define EXT4_ACL_VERSION 0x0001
+
+/* 23.2.5 acl_tag_t values */
+
+#define ACL_UNDEFINED_TAG (0x00)
+#define ACL_USER_OBJ (0x01)
+#define ACL_USER (0x02)
+#define ACL_GROUP_OBJ (0x04)
+#define ACL_GROUP (0x08)
+#define ACL_MASK (0x10)
+#define ACL_OTHER (0x20)
+
+/* 23.3.6 acl_type_t values */
+
+#define ACL_TYPE_ACCESS (0x8000)
+#define ACL_TYPE_DEFAULT (0x4000)
+
+/* 23.2.7 ACL qualifier constants */
+
+#define ACL_UNDEFINED_ID ((id_t)-1)
+
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+ __le32 e_id;
+ } ext4_acl_entry;
+
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+} ext4_acl_entry_short;
+
+typedef struct {
+ __le32 a_version;
+} ext4_acl_header;
+
+
+/* Supported ACL a_version fields */
+ #define POSIX_ACL_XATTR_VERSION 0x0002
+
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+ __le32 e_id;
+} posix_acl_xattr_entry;
+
+typedef struct {
+ __le32 a_version;
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+ posix_acl_xattr_entry a_entries[0];
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic pop
+#endif
+} posix_acl_xattr_header;
+
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
new file mode 100644
index 0000000..3494046
--- /dev/null
+++ b/lib/ext2fs/ext_attr.c
@@ -0,0 +1,1784 @@
+/*
+ * ext_attr.c --- extended attribute blocks
+ *
+ * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2_ext_attr.h"
+#include "ext4_acl.h"
+
+#include "ext2fs.h"
+
+static errcode_t read_ea_inode_hash(ext2_filsys fs, ext2_ino_t ino, __u32 *hash)
+{
+ struct ext2_inode inode;
+ errcode_t retval;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+ *hash = ext2fs_get_ea_inode_hash(&inode);
+ return 0;
+}
+
+#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;
+ unsigned char *name = (((unsigned 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_inum == 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;
+}
+
+__u32 ext2fs_ext_attr_hash_entry_signed(struct ext2_ext_attr_entry *entry,
+ void *data)
+{
+ __u32 hash = 0;
+ signed char *name = (((signed 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_inum == 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;
+}
+
+
+/*
+ * ext2fs_ext_attr_hash_entry3()
+ *
+ * Compute the hash of an extended attribute. This version of the
+ * function supports hashing entries that reference external inodes
+ * (ea_inode feature) as well as calculating the old legacy signed
+ * hash variant.
+ */
+errcode_t ext2fs_ext_attr_hash_entry3(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ void *data, __u32 *hash,
+ __u32 *signed_hash)
+{
+ *hash = ext2fs_ext_attr_hash_entry(entry, data);
+ if (signed_hash)
+ *signed_hash = ext2fs_ext_attr_hash_entry_signed(entry, data);
+
+ if (entry->e_value_inum) {
+ __u32 ea_inode_hash;
+ errcode_t retval;
+
+ retval = read_ea_inode_hash(fs, entry->e_value_inum,
+ &ea_inode_hash);
+ if (retval)
+ return retval;
+
+ *hash = (*hash << VALUE_HASH_SHIFT) ^
+ (*hash >> (8*sizeof(*hash) - VALUE_HASH_SHIFT)) ^
+ ea_inode_hash;
+ if (signed_hash)
+ *signed_hash = (*signed_hash << VALUE_HASH_SHIFT) ^
+ (*signed_hash >> (8*sizeof(*hash) -
+ VALUE_HASH_SHIFT)) ^
+ ea_inode_hash;
+ }
+ return 0;
+}
+
+/*
+ * ext2fs_ext_attr_hash_entry2()
+ *
+ * Compute the hash of an extended attribute.
+ * This version of the function supports hashing entries that reference
+ * external inodes (ea_inode feature).
+ */
+errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ void *data, __u32 *hash)
+{
+ return ext2fs_ext_attr_hash_entry3(fs, entry, data, hash, NULL);
+}
+
+#undef NAME_HASH_SHIFT
+#undef VALUE_HASH_SHIFT
+
+#define BLOCK_HASH_SHIFT 16
+
+/* Mirrors ext4_xattr_rehash() implementation in kernel. */
+void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
+ struct ext2_ext_attr_entry *end)
+{
+ struct ext2_ext_attr_entry *here;
+ __u32 hash = 0;
+
+ here = (struct ext2_ext_attr_entry *)(header+1);
+ while (here < end && !EXT2_EXT_IS_LAST_ENTRY(here)) {
+ if (!here->e_hash) {
+ /* Block is not shared if an entry's hash value == 0 */
+ hash = 0;
+ break;
+ }
+ hash = (hash << BLOCK_HASH_SHIFT) ^
+ (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
+ here->e_hash;
+ here = EXT2_EXT_ATTR_NEXT(here);
+ }
+ header->h_hash = hash;
+}
+
+#undef BLOCK_HASH_SHIFT
+
+__u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode)
+{
+ return inode->i_atime;
+}
+
+void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash)
+{
+ inode->i_atime = hash;
+}
+
+__u64 ext2fs_get_ea_inode_ref(struct ext2_inode *inode)
+{
+ return ((__u64)inode->i_ctime << 32) | inode->osd1.linux1.l_i_version;
+}
+
+void ext2fs_set_ea_inode_ref(struct ext2_inode *inode, __u64 ref_count)
+{
+ inode->i_ctime = (__u32)(ref_count >> 32);
+ inode->osd1.linux1.l_i_version = (__u32)ref_count;
+}
+
+static errcode_t check_ext_attr_header(struct ext2_ext_attr_header *header)
+{
+ if ((header->h_magic != EXT2_EXT_ATTR_MAGIC_v1 &&
+ header->h_magic != EXT2_EXT_ATTR_MAGIC) ||
+ header->h_blocks != 1)
+ return EXT2_ET_BAD_EA_HEADER;
+
+ return 0;
+}
+
+errcode_t ext2fs_read_ext_attr3(ext2_filsys fs, blk64_t block, void *buf,
+ ext2_ino_t inum)
+{
+ int csum_failed = 0;
+ errcode_t retval;
+
+ retval = io_channel_read_blk64(fs->io, block, 1, buf);
+ if (retval)
+ return retval;
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_ext_attr_block_csum_verify(fs, inum, block, buf))
+ csum_failed = 1;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
+#endif
+
+ retval = check_ext_attr_header(buf);
+ if (retval == 0 && csum_failed)
+ retval = EXT2_ET_EXT_ATTR_CSUM_INVALID;
+
+ return retval;
+}
+
+errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block, void *buf)
+{
+ return ext2fs_read_ext_attr3(fs, block, buf, 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_attr3(ext2_filsys fs, blk64_t block, void *inbuf,
+ ext2_ino_t inum)
+{
+ errcode_t retval;
+ char *write_buf;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_get_mem(fs->blocksize, &write_buf);
+ if (retval)
+ return retval;
+ ext2fs_swap_ext_attr(write_buf, inbuf, fs->blocksize, 1);
+#else
+ write_buf = (char *) inbuf;
+#endif
+
+ retval = ext2fs_ext_attr_block_csum_set(fs, inum, block,
+ (struct ext2_ext_attr_header *)write_buf);
+ if (retval)
+ return retval;
+
+ retval = io_channel_write_blk64(fs->io, block, 1, write_buf);
+#ifdef WORDS_BIGENDIAN
+ ext2fs_free_mem(&write_buf);
+#endif
+ if (!retval)
+ ext2fs_mark_changed(fs);
+ return retval;
+}
+
+errcode_t ext2fs_write_ext_attr2(ext2_filsys fs, blk64_t block, void *inbuf)
+{
+ return ext2fs_write_ext_attr3(fs, block, inbuf, 0);
+}
+
+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_refcount3(ext2_filsys fs, blk64_t blk,
+ char *block_buf, int adjust,
+ __u32 *newcount, ext2_ino_t inum)
+{
+ 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_attr3(fs, blk, block_buf, inum);
+ 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_attr3(fs, blk, block_buf, inum);
+ if (retval)
+ goto errout;
+
+errout:
+ if (buf)
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+errcode_t ext2fs_adjust_ea_refcount2(ext2_filsys fs, blk64_t blk,
+ char *block_buf, int adjust,
+ __u32 *newcount)
+{
+ return ext2fs_adjust_ea_refcount3(fs, blk, block_buf, adjust,
+ newcount, 0);
+}
+
+errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+ char *block_buf, int adjust,
+ __u32 *newcount)
+{
+ return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
+ newcount);
+}
+
+/* Manipulate the contents of extended attribute regions */
+struct ext2_xattr {
+ int name_index;
+ char *name;
+ char *short_name;
+ void *value;
+ unsigned int value_len;
+ ext2_ino_t ea_ino;
+};
+
+struct ext2_xattr_handle {
+ errcode_t magic;
+ ext2_filsys fs;
+ struct ext2_xattr *attrs;
+ int capacity;
+ int count;
+ int ibody_count;
+ ext2_ino_t ino;
+ unsigned int flags;
+};
+
+static errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
+ unsigned int expandby)
+{
+ struct ext2_xattr *new_attrs;
+ errcode_t err;
+
+ err = ext2fs_get_arrayzero(h->capacity + expandby,
+ sizeof(struct ext2_xattr), &new_attrs);
+ if (err)
+ return err;
+
+ memcpy(new_attrs, h->attrs, h->capacity * sizeof(struct ext2_xattr));
+ ext2fs_free_mem(&h->attrs);
+ h->capacity += expandby;
+ h->attrs = new_attrs;
+
+ return 0;
+}
+
+struct ea_name_index {
+ int index;
+ const char *name;
+};
+
+/* Keep these names sorted in order of decreasing specificity. */
+static struct ea_name_index ea_names[] = {
+ {10, "gnu."},
+ {3, "system.posix_acl_default"},
+ {2, "system.posix_acl_access"},
+ {8, "system.richacl"},
+ {6, "security."},
+ {4, "trusted."},
+ {7, "system."},
+ {1, "user."},
+ {0, NULL},
+};
+
+static const char *find_ea_prefix(int index)
+{
+ struct ea_name_index *e;
+
+ for (e = ea_names; e->name; e++)
+ if (e->index == index)
+ return e->name;
+
+ return NULL;
+}
+
+static int find_ea_index(const char *fullname, const char **name, int *index)
+{
+ struct ea_name_index *e;
+
+ for (e = ea_names; e->name; e++) {
+ if (strncmp(fullname, e->name, strlen(e->name)) == 0) {
+ *name = fullname + strlen(e->name);
+ *index = e->index;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ void *block_buf = NULL;
+ blk64_t blk;
+ errcode_t err;
+ struct ext2_inode_large i;
+
+ /* Read inode? */
+ if (inode == NULL) {
+ err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
+ sizeof(struct ext2_inode_large));
+ if (err)
+ return err;
+ inode = &i;
+ }
+
+ /* Do we already have an EA block? */
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (blk == 0)
+ return 0;
+
+ /* Find block, zero it, write back */
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ /* We only know how to deal with v2 EA blocks */
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out2;
+ }
+
+ header->h_refcount--;
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ /* Erase link to block */
+ ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
+ if (header->h_refcount == 0)
+ ext2fs_block_alloc_stats2(fs, blk, -1);
+ err = ext2fs_iblk_sub_blocks(fs, (struct ext2_inode *)inode, 1);
+ if (err)
+ goto out2;
+
+ /* Write inode? */
+ if (inode == &i) {
+ err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
+ sizeof(struct ext2_inode_large));
+ if (err)
+ goto out2;
+ }
+
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ void *block_buf = NULL;
+ blk64_t blk, goal;
+ errcode_t err;
+
+ /* Do we already have an EA block? */
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (blk != 0) {
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ /* We only know how to deal with v2 EA blocks */
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out2;
+ }
+
+ /* Single-user block. We're done here. */
+ if (header->h_refcount == 1)
+ goto out2;
+
+ /* We need to CoW the block. */
+ header->h_refcount--;
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+ } else {
+ /* No block, we must increment i_blocks */
+ err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
+ 1);
+ if (err)
+ goto out;
+ }
+
+ /* Allocate a block */
+ goal = ext2fs_find_inode_goal(fs, ino, (struct ext2_inode *)inode, 0);
+ err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
+ if (err)
+ goto out2;
+ ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
+out2:
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+
+static inline int
+posix_acl_xattr_count(size_t size)
+{
+ if (size < sizeof(posix_acl_xattr_header))
+ return -1;
+ size -= sizeof(posix_acl_xattr_header);
+ if (size % sizeof(posix_acl_xattr_entry))
+ return -1;
+ return size / sizeof(posix_acl_xattr_entry);
+}
+
+/*
+ * The lgetxattr function returns data formatted in the POSIX extended
+ * attribute format. The on-disk format uses a more compact encoding.
+ * See the ext4_acl_to_disk in fs/ext4/acl.c.
+ */
+static errcode_t convert_posix_acl_to_disk_buffer(const void *value, size_t size,
+ void *out_buf, size_t *size_out)
+{
+ const posix_acl_xattr_header *header =
+ (const posix_acl_xattr_header*) value;
+ const posix_acl_xattr_entry *end, *entry =
+ (const posix_acl_xattr_entry *)(header+1);
+ ext4_acl_header *ext_acl;
+ size_t s;
+ char *e;
+
+ int count;
+
+ if (!value)
+ return EINVAL;
+ if (size < sizeof(posix_acl_xattr_header))
+ return ENOMEM;
+ if (header->a_version != ext2fs_cpu_to_le32(POSIX_ACL_XATTR_VERSION))
+ return EINVAL;
+
+ count = posix_acl_xattr_count(size);
+ ext_acl = out_buf;
+ ext_acl->a_version = ext2fs_cpu_to_le32(EXT4_ACL_VERSION);
+
+ if (count <= 0)
+ return EINVAL;
+
+ e = (char *) out_buf + sizeof(ext4_acl_header);
+ s = sizeof(ext4_acl_header);
+ for (end = entry + count; entry != end;entry++) {
+ ext4_acl_entry *disk_entry = (ext4_acl_entry*) e;
+ disk_entry->e_tag = entry->e_tag;
+ disk_entry->e_perm = entry->e_perm;
+
+ switch(ext2fs_le16_to_cpu(entry->e_tag)) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ e += sizeof(ext4_acl_entry_short);
+ s += sizeof(ext4_acl_entry_short);
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ disk_entry->e_id = entry->e_id;
+ e += sizeof(ext4_acl_entry);
+ s += sizeof(ext4_acl_entry);
+ break;
+ default:
+ return EINVAL;
+ }
+ }
+ *size_out = s;
+ return 0;
+}
+
+static errcode_t convert_disk_buffer_to_posix_acl(const void *value, size_t size,
+ void **out_buf, size_t *size_out)
+{
+ posix_acl_xattr_header *header;
+ posix_acl_xattr_entry *entry;
+ const ext4_acl_header *ext_acl = (const ext4_acl_header *) value;
+ errcode_t err;
+ const char *cp;
+ char *out;
+
+ if ((!value) ||
+ (size < sizeof(ext4_acl_header)) ||
+ (ext_acl->a_version != ext2fs_cpu_to_le32(EXT4_ACL_VERSION)))
+ return EINVAL;
+
+ err = ext2fs_get_mem(size * 2, &out);
+ if (err)
+ return err;
+
+ header = (posix_acl_xattr_header *) out;
+ header->a_version = ext2fs_cpu_to_le32(POSIX_ACL_XATTR_VERSION);
+ entry = (posix_acl_xattr_entry *) (out + sizeof(posix_acl_xattr_header));
+
+ cp = (const char *) value + sizeof(ext4_acl_header);
+ size -= sizeof(ext4_acl_header);
+
+ while (size > 0) {
+ const ext4_acl_entry *disk_entry = (const ext4_acl_entry *) cp;
+
+ entry->e_tag = disk_entry->e_tag;
+ entry->e_perm = disk_entry->e_perm;
+
+ switch(ext2fs_le16_to_cpu(entry->e_tag)) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ entry->e_id = 0;
+ cp += sizeof(ext4_acl_entry_short);
+ size -= sizeof(ext4_acl_entry_short);
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ entry->e_id = disk_entry->e_id;
+ cp += sizeof(ext4_acl_entry);
+ size -= sizeof(ext4_acl_entry);
+ break;
+ default:
+ ext2fs_free_mem(&out);
+ return EINVAL;
+ }
+ entry++;
+ }
+ *out_buf = out;
+ *size_out = ((char *) entry - out);
+ return 0;
+}
+
+static errcode_t
+write_xattrs_to_buffer(ext2_filsys fs, struct ext2_xattr *attrs, int count,
+ void *entries_start, unsigned int storage_size,
+ unsigned int value_offset_correction, int write_hash)
+{
+ struct ext2_xattr *x;
+ struct ext2_ext_attr_entry *e = entries_start;
+ char *end = (char *) entries_start + storage_size;
+ unsigned int value_size;
+ errcode_t err;
+
+ memset(entries_start, 0, storage_size);
+ for (x = attrs; x < attrs + count; x++) {
+ value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
+ EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
+
+ /* Fill out e appropriately */
+ e->e_name_len = strlen(x->short_name);
+ e->e_name_index = x->name_index;
+
+ e->e_value_size = x->value_len;
+ e->e_value_inum = x->ea_ino;
+
+ /* Store name */
+ memcpy((char *)e + sizeof(*e), x->short_name, e->e_name_len);
+ if (x->ea_ino) {
+ e->e_value_offs = 0;
+ } else {
+ end -= value_size;
+ e->e_value_offs = end - (char *) entries_start +
+ value_offset_correction;
+ memcpy(end, x->value, e->e_value_size);
+ }
+
+ if (write_hash || x->ea_ino) {
+ err = ext2fs_ext_attr_hash_entry2(fs, e,
+ x->ea_ino ? 0 : end,
+ &e->e_hash);
+ if (err)
+ return err;
+ } else
+ e->e_hash = 0;
+
+ e = EXT2_EXT_ATTR_NEXT(e);
+ *(__u32 *)e = 0;
+ }
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
+{
+ ext2_filsys fs = handle->fs;
+ const unsigned int inode_size = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode_large *inode;
+ char *start, *block_buf = NULL;
+ struct ext2_ext_attr_header *header;
+ __u32 ea_inode_magic;
+ blk64_t blk;
+ unsigned int storage_size;
+ unsigned int i;
+ errcode_t err;
+
+ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+ i = inode_size;
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(fs, handle->ino, EXT2_INODE(inode),
+ inode_size);
+ if (err)
+ goto out;
+
+ /* If extra_isize isn't set, we need to set it now */
+ if (inode->i_extra_isize == 0 &&
+ inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
+ char *p = (char *)inode;
+ size_t extra = fs->super->s_want_extra_isize;
+
+ if (extra == 0)
+ extra = sizeof(__u32);
+ memset(p + EXT2_GOOD_OLD_INODE_SIZE, 0, extra);
+ inode->i_extra_isize = extra;
+ }
+ if (inode->i_extra_isize & 3) {
+ err = EXT2_ET_INODE_CORRUPTED;
+ goto out;
+ }
+
+ /* Does the inode have space for EA? */
+ if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
+ inode_size <= EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize +
+ sizeof(__u32))
+ goto write_ea_block;
+
+ /* Write the inode EA */
+ ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
+ memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
+ storage_size = inode_size - EXT2_GOOD_OLD_INODE_SIZE -
+ inode->i_extra_isize - sizeof(__u32);
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+
+ err = write_xattrs_to_buffer(fs, handle->attrs, handle->ibody_count,
+ start, storage_size, 0, 0);
+ if (err)
+ goto out;
+write_ea_block:
+ /* Are we done? */
+ if (handle->ibody_count == handle->count &&
+ !ext2fs_file_acl_block(fs, EXT2_INODE(inode)))
+ goto skip_ea_block;
+
+ /* Write the EA block */
+ err = ext2fs_get_memzero(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ storage_size = fs->blocksize - sizeof(struct ext2_ext_attr_header);
+ start = block_buf + sizeof(struct ext2_ext_attr_header);
+
+ err = write_xattrs_to_buffer(fs, handle->attrs + handle->ibody_count,
+ handle->count - handle->ibody_count, start,
+ storage_size, start - block_buf, 1);
+ if (err)
+ goto out2;
+
+ /* Write a header on the EA block */
+ header = (struct ext2_ext_attr_header *) block_buf;
+ header->h_magic = EXT2_EXT_ATTR_MAGIC;
+ header->h_refcount = 1;
+ header->h_blocks = 1;
+
+ /* Get a new block for writing */
+ err = prep_ea_block_for_write(fs, handle->ino, inode);
+ if (err)
+ goto out2;
+
+ /* Finally, write the new EA block */
+ blk = ext2fs_file_acl_block(fs, EXT2_INODE(inode));
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, handle->ino);
+ if (err)
+ goto out2;
+
+skip_ea_block:
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (!block_buf && blk) {
+ /* xattrs shrunk, free the block */
+ err = ext2fs_free_ext_attr(fs, handle->ino, inode);
+ if (err)
+ goto out;
+ }
+
+ /* Write the inode */
+ err = ext2fs_write_inode_full(fs, handle->ino, EXT2_INODE(inode),
+ inode_size);
+ if (err)
+ goto out2;
+
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ ext2fs_free_mem(&inode);
+ return err;
+}
+
+static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
+ struct ext2_inode_large *inode,
+ struct ext2_ext_attr_entry *entries,
+ unsigned int storage_size,
+ char *value_start)
+{
+ struct ext2_xattr *x;
+ struct ext2_ext_attr_entry *entry, *end;
+ const char *prefix;
+ unsigned int remain, prefix_len;
+ errcode_t err;
+ unsigned int values_size = storage_size +
+ ((char *)entries - value_start);
+
+ /* find the end */
+ end = entries;
+ remain = storage_size;
+ while (remain >= sizeof(struct ext2_ext_attr_entry) &&
+ !EXT2_EXT_IS_LAST_ENTRY(end)) {
+
+ /* header eats this space */
+ remain -= sizeof(struct ext2_ext_attr_entry);
+
+ /* is attribute name valid? */
+ if (EXT2_EXT_ATTR_SIZE(end->e_name_len) > remain)
+ return EXT2_ET_EA_BAD_NAME_LEN;
+
+ /* attribute len eats this space */
+ remain -= EXT2_EXT_ATTR_SIZE(end->e_name_len);
+ end = EXT2_EXT_ATTR_NEXT(end);
+ }
+
+ entry = entries;
+ remain = storage_size;
+ while (remain >= sizeof(struct ext2_ext_attr_entry) &&
+ !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+
+ /* Allocate space for more attrs? */
+ if (handle->count == handle->capacity) {
+ err = ext2fs_xattrs_expand(handle, 4);
+ if (err)
+ return err;
+ }
+
+ x = handle->attrs + handle->count;
+
+ /* header eats this space */
+ remain -= sizeof(struct ext2_ext_attr_entry);
+
+ /* attribute len eats this space */
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+
+ /* Extract name */
+ prefix = find_ea_prefix(entry->e_name_index);
+ prefix_len = (prefix ? strlen(prefix) : 0);
+ err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
+ &x->name);
+ if (err)
+ return err;
+ if (prefix)
+ memcpy(x->name, prefix, prefix_len);
+ if (entry->e_name_len)
+ memcpy(x->name + prefix_len,
+ (char *)entry + sizeof(*entry),
+ entry->e_name_len);
+ x->short_name = x->name + prefix_len;
+ x->name_index = entry->e_name_index;
+
+ /* Check & copy value */
+ if (!ext2fs_has_feature_ea_inode(handle->fs->super) &&
+ entry->e_value_inum != 0)
+ return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+ if (entry->e_value_inum == 0) {
+ if (entry->e_value_size > remain)
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+
+ if (entry->e_value_offs + entry->e_value_size > values_size)
+ return EXT2_ET_EA_BAD_VALUE_OFFSET;
+
+ if (entry->e_value_size > 0 &&
+ value_start + entry->e_value_offs <
+ (char *)end + sizeof(__u32))
+ return EXT2_ET_EA_BAD_VALUE_OFFSET;
+
+ remain -= entry->e_value_size;
+
+ err = ext2fs_get_mem(entry->e_value_size, &x->value);
+ if (err)
+ return err;
+ memcpy(x->value, value_start + entry->e_value_offs,
+ entry->e_value_size);
+ } else {
+ struct ext2_inode *ea_inode;
+ ext2_file_t ea_file;
+
+ if (entry->e_value_offs != 0)
+ return EXT2_ET_EA_BAD_VALUE_OFFSET;
+
+ if (entry->e_value_size > (64 * 1024))
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+
+ err = ext2fs_get_mem(entry->e_value_size, &x->value);
+ if (err)
+ return err;
+
+ err = ext2fs_file_open(handle->fs, entry->e_value_inum,
+ 0, &ea_file);
+ if (err)
+ return err;
+
+ ea_inode = ext2fs_file_get_inode(ea_file);
+ if ((ea_inode->i_flags & EXT4_INLINE_DATA_FL) ||
+ !(ea_inode->i_flags & EXT4_EA_INODE_FL) ||
+ ea_inode->i_links_count == 0)
+ err = EXT2_ET_EA_INODE_CORRUPTED;
+ else if ((__u64) ext2fs_file_get_size(ea_file) !=
+ entry->e_value_size)
+ err = EXT2_ET_EA_BAD_VALUE_SIZE;
+ else
+ err = ext2fs_file_read(ea_file, x->value,
+ entry->e_value_size, 0);
+ ext2fs_file_close(ea_file);
+ if (err)
+ return err;
+ }
+
+ x->ea_ino = entry->e_value_inum;
+ x->value_len = entry->e_value_size;
+
+ /* e_hash may be 0 in older inode's ea */
+ if (entry->e_hash != 0) {
+ __u32 hash, signed_hash;
+
+ void *data = (entry->e_value_inum != 0) ?
+ 0 : value_start + entry->e_value_offs;
+
+ err = ext2fs_ext_attr_hash_entry3(handle->fs, entry,
+ data, &hash,
+ &signed_hash);
+ if (err)
+ return err;
+ if ((entry->e_hash != hash) &&
+ (entry->e_hash != signed_hash)) {
+ struct ext2_inode child;
+
+ /* Check whether this is an old Lustre-style
+ * ea_inode reference.
+ */
+ err = ext2fs_read_inode(handle->fs,
+ entry->e_value_inum,
+ &child);
+ if (err)
+ return err;
+ if (child.i_mtime != handle->ino ||
+ child.i_generation != inode->i_generation)
+ return EXT2_ET_BAD_EA_HASH;
+ }
+ }
+
+ handle->count++;
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+
+ return 0;
+}
+
+static void xattrs_free_keys(struct ext2_xattr_handle *h)
+{
+ struct ext2_xattr *a = h->attrs;
+ int i;
+
+ for (i = 0; i < h->capacity; i++) {
+ if (a[i].name)
+ ext2fs_free_mem(&a[i].name);
+ if (a[i].value)
+ ext2fs_free_mem(&a[i].value);
+ }
+ h->count = 0;
+ h->ibody_count = 0;
+}
+
+/* fetch xattrs from an already-loaded inode */
+errcode_t ext2fs_xattrs_read_inode(struct ext2_xattr_handle *handle,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ __u32 ea_inode_magic;
+ unsigned int storage_size;
+ char *start, *block_buf = NULL;
+ blk64_t blk;
+ errcode_t err = 0;
+
+ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+
+ xattrs_free_keys(handle);
+
+ /* Does the inode have space for EA? */
+ if (inode->i_extra_isize < sizeof(inode->i_extra_isize) ||
+ EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32))
+ goto read_ea_block;
+ if (inode->i_extra_isize & 3) {
+ err = EXT2_ET_INODE_CORRUPTED;
+ goto out;
+ }
+
+ /* Look for EA in the inode */
+ memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, sizeof(__u32));
+ if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
+ storage_size = EXT2_INODE_SIZE(handle->fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+
+ err = read_xattrs_from_buffer(handle, inode,
+ (struct ext2_ext_attr_entry *) start,
+ storage_size, start);
+ if (err)
+ goto out;
+
+ handle->ibody_count = handle->count;
+ }
+
+read_ea_block:
+ /* Look for EA in a separate EA block */
+ blk = ext2fs_file_acl_block(handle->fs, EXT2_INODE(inode));
+ if (blk != 0) {
+ if ((blk < handle->fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(handle->fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
+ handle->ino);
+ if (err)
+ goto out3;
+
+ /* We only know how to deal with v2 EA blocks */
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out3;
+ }
+
+ /* Read EAs */
+ storage_size = handle->fs->blocksize -
+ sizeof(struct ext2_ext_attr_header);
+ start = block_buf + sizeof(struct ext2_ext_attr_header);
+ err = read_xattrs_from_buffer(handle, inode,
+ (struct ext2_ext_attr_entry *) start,
+ storage_size, block_buf);
+ }
+
+out3:
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
+{
+ struct ext2_inode_large *inode;
+ size_t inode_size = EXT2_INODE_SIZE(handle->fs->super);
+ errcode_t err;
+
+ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+
+ if (inode_size < sizeof(*inode))
+ inode_size = sizeof(*inode);
+ err = ext2fs_get_memzero(inode_size, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(handle->fs, handle->ino, EXT2_INODE(inode),
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out;
+
+ err = ext2fs_xattrs_read_inode(handle, inode);
+
+out:
+ ext2fs_free_mem(&inode);
+
+ return err;
+}
+
+errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
+ int (*func)(char *name, char *value,
+ size_t value_len, void *data),
+ void *data)
+{
+ struct ext2_xattr *x;
+ int dirty = 0;
+ int ret;
+
+ EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
+ for (x = h->attrs; x < h->attrs + h->count; x++) {
+ ret = func(x->name, x->value, x->value_len, data);
+ if (ret & XATTR_CHANGED)
+ dirty = 1;
+ if (ret & XATTR_ABORT)
+ break;
+ }
+
+ if (dirty)
+ return ext2fs_xattrs_write(h);
+ return 0;
+}
+
+errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
+ void **value, size_t *value_len)
+{
+ struct ext2_xattr *x;
+ char *val;
+ errcode_t err;
+
+ EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
+ for (x = h->attrs; x < h->attrs + h->count; x++) {
+ if (strcmp(x->name, key))
+ continue;
+
+ if (!(h->flags & XATTR_HANDLE_FLAG_RAW) &&
+ ((strcmp(key, "system.posix_acl_default") == 0) ||
+ (strcmp(key, "system.posix_acl_access") == 0))) {
+ err = convert_disk_buffer_to_posix_acl(x->value, x->value_len,
+ value, value_len);
+ return err;
+ } else {
+ err = ext2fs_get_mem(x->value_len, &val);
+ if (err)
+ return err;
+ memcpy(val, x->value, x->value_len);
+ *value = val;
+ *value_len = x->value_len;
+ return 0;
+ }
+ }
+
+ return EXT2_ET_EA_KEY_NOT_FOUND;
+}
+
+errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size)
+{
+ struct ext2_ext_attr_entry *entry;
+ struct ext2_inode_large *inode;
+ __u32 ea_inode_magic;
+ unsigned int minoff;
+ char *start;
+ size_t i;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(fs->super));
+ if (err)
+ goto out;
+
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32)) {
+ err = EXT2_ET_INLINE_DATA_NO_SPACE;
+ goto out;
+ }
+
+ minoff = EXT2_INODE_SIZE(fs->super) - sizeof(*inode) - sizeof(__u32);
+ memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, sizeof(__u32));
+ if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
+ /* has xattrs. calculate the size */
+ start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+ entry = (struct ext2_ext_attr_entry *) start;
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (!entry->e_value_inum && entry->e_value_size) {
+ unsigned int offs = entry->e_value_offs;
+ if (offs < minoff)
+ minoff = offs;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ *size = minoff - ((char *)entry - (char *)start) - sizeof(__u32);
+ } else {
+ /* no xattr. return a maximum size */
+ *size = EXT2_EXT_ATTR_SIZE(minoff -
+ EXT2_EXT_ATTR_LEN(strlen("data")) -
+ EXT2_EXT_ATTR_ROUND - sizeof(__u32));
+ }
+
+out:
+ ext2fs_free_mem(&inode);
+ return err;
+}
+
+static errcode_t xattr_create_ea_inode(ext2_filsys fs, const void *value,
+ size_t value_len, ext2_ino_t *ea_ino)
+{
+ struct ext2_inode inode;
+ ext2_ino_t ino;
+ ext2_file_t file;
+ __u32 hash;
+ errcode_t ret;
+
+ ret = ext2fs_new_inode(fs, 0, 0, 0, &ino);
+ if (ret)
+ return ret;
+
+ memset(&inode, 0, sizeof(inode));
+ inode.i_flags |= EXT4_EA_INODE_FL;
+ if (ext2fs_has_feature_extents(fs->super))
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ inode.i_size = 0;
+ inode.i_mode = LINUX_S_IFREG | 0600;
+ inode.i_links_count = 1;
+ ret = ext2fs_write_new_inode(fs, ino, &inode);
+ if (ret)
+ return ret;
+ /*
+ * ref_count and hash utilize inode's i_*time fields.
+ * ext2fs_write_new_inode() call above initializes these fields with
+ * current time. That's why ref count and hash updates are done
+ * separately below.
+ */
+ ext2fs_set_ea_inode_ref(&inode, 1);
+ hash = ext2fs_crc32c_le(fs->csum_seed, value, value_len);
+ ext2fs_set_ea_inode_hash(&inode, hash);
+
+ ret = ext2fs_write_inode(fs, ino, &inode);
+ if (ret)
+ return ret;
+
+ ret = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &file);
+ if (ret)
+ return ret;
+ ret = ext2fs_file_write(file, value, value_len, NULL);
+ ext2fs_file_close(file);
+ if (ret)
+ return ret;
+
+ ext2fs_inode_alloc_stats2(fs, ino, 1 /* inuse */, 0 /* isdir */);
+
+ *ea_ino = ino;
+ return 0;
+}
+
+static errcode_t xattr_inode_dec_ref(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode_large inode;
+ __u64 ref_count;
+ errcode_t ret;
+
+ ret = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
+ sizeof(inode));
+ if (ret)
+ goto out;
+
+ ref_count = ext2fs_get_ea_inode_ref(EXT2_INODE(&inode));
+ ref_count--;
+ ext2fs_set_ea_inode_ref(EXT2_INODE(&inode), ref_count);
+
+ if (ref_count)
+ goto write_out;
+
+ inode.i_links_count = 0;
+ inode.i_dtime = fs->now ? fs->now : time(0);
+
+ ret = ext2fs_free_ext_attr(fs, ino, &inode);
+ if (ret)
+ goto write_out;
+
+ if (ext2fs_inode_has_valid_blocks2(fs, (struct ext2_inode *)&inode)) {
+ ret = ext2fs_punch(fs, ino, (struct ext2_inode *)&inode, NULL,
+ 0, ~0ULL);
+ if (ret)
+ goto out;
+ }
+
+ ext2fs_inode_alloc_stats2(fs, ino, -1 /* inuse */, 0 /* is_dir */);
+
+write_out:
+ ret = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
+ sizeof(inode));
+out:
+ return ret;
+}
+
+static errcode_t xattr_update_entry(ext2_filsys fs, struct ext2_xattr *x,
+ const char *name, const char *short_name,
+ int index, const void *value,
+ size_t value_len, int in_inode)
+{
+ ext2_ino_t ea_ino = 0;
+ void *new_value = NULL;
+ char *new_name = NULL;
+ int name_len;
+ errcode_t ret;
+
+ if (!x->name) {
+ name_len = strlen(name);
+ ret = ext2fs_get_mem(name_len + 1, &new_name);
+ if (ret)
+ goto fail;
+ memcpy(new_name, name, name_len + 1);
+ }
+
+ ret = ext2fs_get_mem(value_len, &new_value);
+ if (ret)
+ goto fail;
+ memcpy(new_value, value, value_len);
+
+ if (in_inode) {
+ ret = xattr_create_ea_inode(fs, value, value_len, &ea_ino);
+ if (ret)
+ goto fail;
+ }
+
+ if (x->ea_ino) {
+ ret = xattr_inode_dec_ref(fs, x->ea_ino);
+ if (ret)
+ goto fail;
+ }
+
+ if (!x->name) {
+ x->name = new_name;
+ x->short_name = new_name + (short_name - name);
+ }
+ x->name_index = index;
+
+ if (x->value)
+ ext2fs_free_mem(&x->value);
+ x->value = new_value;
+ x->value_len = value_len;
+ x->ea_ino = ea_ino;
+ return 0;
+fail:
+ if (new_name)
+ ext2fs_free_mem(&new_name);
+ if (new_value)
+ ext2fs_free_mem(&new_value);
+ if (ea_ino)
+ xattr_inode_dec_ref(fs, ea_ino);
+ return ret;
+}
+
+static int xattr_find_position(struct ext2_xattr *attrs, int count,
+ const char *shortname, int name_idx)
+{
+ struct ext2_xattr *x;
+ int i;
+ int shortname_len, x_shortname_len;
+
+ shortname_len = strlen(shortname);
+
+ for (i = 0, x = attrs; i < count; i++, x++) {
+ if (name_idx < x->name_index)
+ break;
+ if (name_idx > x->name_index)
+ continue;
+
+ x_shortname_len = strlen(x->short_name);
+ if (shortname_len < x_shortname_len)
+ break;
+ if (shortname_len > x_shortname_len)
+ continue;
+
+ if (memcmp(shortname, x->short_name, shortname_len) <= 0)
+ break;
+ }
+ return i;
+}
+
+static errcode_t xattr_array_update(struct ext2_xattr_handle *h,
+ const char *name,
+ const void *value, size_t value_len,
+ int ibody_free, int block_free,
+ int old_idx, int in_inode)
+{
+ struct ext2_xattr tmp;
+ int add_to_ibody;
+ int needed;
+ int name_len, name_idx = 0;
+ const char *shortname = name;
+ int new_idx;
+ int ret;
+
+ find_ea_index(name, &shortname, &name_idx);
+ name_len = strlen(shortname);
+
+ needed = EXT2_EXT_ATTR_LEN(name_len);
+ if (!in_inode)
+ needed += EXT2_EXT_ATTR_SIZE(value_len);
+
+ if (old_idx >= 0 && old_idx < h->ibody_count) {
+ ibody_free += EXT2_EXT_ATTR_LEN(name_len);
+ if (!h->attrs[old_idx].ea_ino)
+ ibody_free += EXT2_EXT_ATTR_SIZE(
+ h->attrs[old_idx].value_len);
+ }
+
+ if (needed <= ibody_free) {
+ if (old_idx < 0) {
+ new_idx = h->ibody_count;
+ add_to_ibody = 1;
+ goto add_new;
+ }
+
+ /* Update the existing entry. */
+ ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
+ shortname, name_idx, value,
+ value_len, in_inode);
+ if (ret)
+ return ret;
+ if (h->ibody_count <= old_idx) {
+ /* Move entry from block to the end of ibody. */
+ tmp = h->attrs[old_idx];
+ memmove(h->attrs + h->ibody_count + 1,
+ h->attrs + h->ibody_count,
+ (old_idx - h->ibody_count) * sizeof(*h->attrs));
+ h->attrs[h->ibody_count] = tmp;
+ h->ibody_count++;
+ }
+ return 0;
+ }
+
+ if (h->ibody_count <= old_idx) {
+ block_free += EXT2_EXT_ATTR_LEN(name_len);
+ if (!h->attrs[old_idx].ea_ino)
+ block_free +=
+ EXT2_EXT_ATTR_SIZE(h->attrs[old_idx].value_len);
+ }
+
+ if (needed > block_free)
+ return EXT2_ET_EA_NO_SPACE;
+
+ if (old_idx >= 0) {
+ /* Update the existing entry. */
+ ret = xattr_update_entry(h->fs, &h->attrs[old_idx], name,
+ shortname, name_idx, value,
+ value_len, in_inode);
+ if (ret)
+ return ret;
+ if (old_idx < h->ibody_count) {
+ /*
+ * Move entry from ibody to the block. Note that
+ * entries in the block are sorted.
+ */
+ new_idx = xattr_find_position(h->attrs + h->ibody_count,
+ h->count - h->ibody_count,
+ shortname, name_idx);
+ new_idx += h->ibody_count - 1;
+ tmp = h->attrs[old_idx];
+ memmove(h->attrs + old_idx, h->attrs + old_idx + 1,
+ (new_idx - old_idx) * sizeof(*h->attrs));
+ h->attrs[new_idx] = tmp;
+ h->ibody_count--;
+ }
+ return 0;
+ }
+
+ new_idx = xattr_find_position(h->attrs + h->ibody_count,
+ h->count - h->ibody_count,
+ shortname, name_idx);
+ new_idx += h->ibody_count;
+ add_to_ibody = 0;
+
+add_new:
+ if (h->count == h->capacity) {
+ ret = ext2fs_xattrs_expand(h, 4);
+ if (ret)
+ return ret;
+ }
+
+ ret = xattr_update_entry(h->fs, &h->attrs[h->count], name, shortname,
+ name_idx, value, value_len, in_inode);
+ if (ret)
+ return ret;
+
+ tmp = h->attrs[h->count];
+ memmove(h->attrs + new_idx + 1, h->attrs + new_idx,
+ (h->count - new_idx)*sizeof(*h->attrs));
+ h->attrs[new_idx] = tmp;
+ if (add_to_ibody)
+ h->ibody_count++;
+ h->count++;
+ return 0;
+}
+
+static int space_used(struct ext2_xattr *attrs, int count)
+{
+ int total = 0;
+ struct ext2_xattr *x;
+ int i, len;
+
+ for (i = 0, x = attrs; i < count; i++, x++) {
+ len = strlen(x->short_name);
+ total += EXT2_EXT_ATTR_LEN(len);
+ if (!x->ea_ino)
+ total += EXT2_EXT_ATTR_SIZE(x->value_len);
+ }
+ return total;
+}
+
+/*
+ * The minimum size of EA value when you start storing it in an external inode
+ * size of block - size of header - size of 1 entry - 4 null bytes
+ */
+#define EXT4_XATTR_MIN_LARGE_EA_SIZE(b) \
+ ((b) - EXT2_EXT_ATTR_LEN(3) - sizeof(struct ext2_ext_attr_header) - 4)
+
+errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *h,
+ const char *name,
+ const void *value,
+ size_t value_len)
+{
+ ext2_filsys fs = h->fs;
+ const int inode_size = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode_large *inode = NULL;
+ struct ext2_xattr *x;
+ char *new_value;
+ int ibody_free, block_free;
+ int in_inode = 0;
+ int old_idx = -1;
+ int extra_isize;
+ errcode_t ret;
+
+ EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
+
+ ret = ext2fs_get_mem(value_len, &new_value);
+ if (ret)
+ return ret;
+ if (!(h->flags & XATTR_HANDLE_FLAG_RAW) &&
+ ((strcmp(name, "system.posix_acl_default") == 0) ||
+ (strcmp(name, "system.posix_acl_access") == 0))) {
+ ret = convert_posix_acl_to_disk_buffer(value, value_len,
+ new_value, &value_len);
+ if (ret)
+ goto out;
+ } else if (value_len)
+ memcpy(new_value, value, value_len);
+
+ /* Imitate kernel behavior by skipping update if value is the same. */
+ for (x = h->attrs; x < h->attrs + h->count; x++) {
+ if (!strcmp(x->name, name)) {
+ if (!x->ea_ino && x->value_len == value_len &&
+ (!value_len ||
+ !memcmp(x->value, new_value, value_len))) {
+ ret = 0;
+ goto out;
+ }
+ old_idx = x - h->attrs;
+ break;
+ }
+ }
+
+ ret = ext2fs_get_memzero(inode_size, &inode);
+ if (ret)
+ goto out;
+ ret = ext2fs_read_inode_full(fs, h->ino,
+ (struct ext2_inode *)inode,
+ inode_size);
+ if (ret)
+ goto out;
+ if (inode_size > EXT2_GOOD_OLD_INODE_SIZE) {
+ extra_isize = inode->i_extra_isize;
+ if (extra_isize == 0) {
+ extra_isize = fs->super->s_want_extra_isize;
+ if (extra_isize == 0)
+ extra_isize = sizeof(__u32);
+ }
+ ibody_free = inode_size - EXT2_GOOD_OLD_INODE_SIZE;
+ ibody_free -= extra_isize;
+ /* Extended attribute magic and final null entry. */
+ ibody_free -= sizeof(__u32) * 2;
+ ibody_free -= space_used(h->attrs, h->ibody_count);
+ } else
+ ibody_free = 0;
+
+ /* Inline data can only go to ibody. */
+ if (strcmp(name, "system.data") == 0) {
+ if (h->ibody_count <= old_idx) {
+ ret = EXT2_ET_FILESYSTEM_CORRUPTED;
+ goto out;
+ }
+ ret = xattr_array_update(h, name, new_value, value_len,
+ ibody_free,
+ 0 /* block_free */, old_idx,
+ 0 /* in_inode */);
+ if (ret)
+ goto out;
+ goto write_out;
+ }
+
+ block_free = fs->blocksize;
+ block_free -= sizeof(struct ext2_ext_attr_header);
+ /* Final null entry. */
+ block_free -= sizeof(__u32);
+ block_free -= space_used(h->attrs + h->ibody_count,
+ h->count - h->ibody_count);
+
+ if (ext2fs_has_feature_ea_inode(fs->super) &&
+ value_len > EXT4_XATTR_MIN_LARGE_EA_SIZE(fs->blocksize))
+ in_inode = 1;
+
+ ret = xattr_array_update(h, name, new_value, value_len, ibody_free,
+ block_free, old_idx, in_inode);
+ if (ret == EXT2_ET_EA_NO_SPACE && !in_inode &&
+ ext2fs_has_feature_ea_inode(fs->super))
+ ret = xattr_array_update(h, name, new_value, value_len,
+ ibody_free, block_free, old_idx, 1 /* in_inode */);
+ if (ret)
+ goto out;
+
+write_out:
+ ret = ext2fs_xattrs_write(h);
+out:
+ if (inode)
+ ext2fs_free_mem(&inode);
+ ext2fs_free_mem(&new_value);
+ return ret;
+}
+
+errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
+ const char *key)
+{
+ struct ext2_xattr *x;
+ struct ext2_xattr *end = handle->attrs + handle->count;
+
+ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+ for (x = handle->attrs; x < end; x++) {
+ if (strcmp(x->name, key) == 0) {
+ ext2fs_free_mem(&x->name);
+ ext2fs_free_mem(&x->value);
+ if (x->ea_ino)
+ xattr_inode_dec_ref(handle->fs, x->ea_ino);
+ memmove(x, x + 1, (end - x - 1)*sizeof(*x));
+ memset(end - 1, 0, sizeof(*end));
+ if (x < handle->attrs + handle->ibody_count)
+ handle->ibody_count--;
+ handle->count--;
+ return ext2fs_xattrs_write(handle);
+ }
+ }
+
+ /* no key found, success! */
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle)
+{
+ struct ext2_xattr_handle *h;
+ errcode_t err;
+
+ if (!ext2fs_has_feature_xattr(fs->super) &&
+ !ext2fs_has_feature_inline_data(fs->super))
+ return EXT2_ET_MISSING_EA_FEATURE;
+
+ err = ext2fs_get_memzero(sizeof(*h), &h);
+ if (err)
+ return err;
+
+ h->magic = EXT2_ET_MAGIC_EA_HANDLE;
+ h->capacity = 4;
+ err = ext2fs_get_arrayzero(h->capacity, sizeof(struct ext2_xattr),
+ &h->attrs);
+ if (err) {
+ ext2fs_free_mem(&h);
+ return err;
+ }
+ h->count = 0;
+ h->ino = ino;
+ h->fs = fs;
+ *handle = h;
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
+{
+ struct ext2_xattr_handle *h = *handle;
+
+ EXT2_CHECK_MAGIC(h, EXT2_ET_MAGIC_EA_HANDLE);
+ xattrs_free_keys(h);
+ ext2fs_free_mem(&h->attrs);
+ ext2fs_free_mem(handle);
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle, size_t *count)
+{
+ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+ *count = handle->count;
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
+ unsigned int *new_flags, unsigned int *old_flags)
+{
+ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EA_HANDLE);
+ if (old_flags)
+ *old_flags = handle->flags;
+ if (new_flags)
+ handle->flags = *new_flags;
+ return 0;
+}
diff --git a/lib/ext2fs/extent.c b/lib/ext2fs/extent.c
new file mode 100644
index 0000000..82e75cc
--- /dev/null
+++ b/lib/ext2fs/extent.c
@@ -0,0 +1,1877 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "e2image.h"
+
+#undef DEBUG
+
+/*
+ * 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;
+ blk64_t 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;
+ int max_paths;
+ 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);
+
+}
+
+static void dump_path(const char *tag, struct ext2_extent_handle *handle,
+ struct extent_path *path)
+{
+ struct extent_path *ppp = path;
+ printf("%s: level=%d\n", tag, handle->level);
+
+ do {
+ printf("%s: path=%ld buf=%p entries=%d max_entries=%d left=%d "
+ "visit_num=%d flags=0x%x end_blk=%llu curr=%p(%ld)\n",
+ tag, (ppp - handle->path), ppp->buf, ppp->entries,
+ ppp->max_entries, ppp->left, ppp->visit_num, ppp->flags,
+ ppp->end_blk, ppp->curr, ppp->curr - (void *)ppp->buf);
+ printf(" ");
+ dbg_show_header((struct ext3_extent_header *)ppp->buf);
+ if (ppp->curr) {
+ printf(" ");
+ dbg_show_index(ppp->curr);
+ printf(" ");
+ dbg_show_extent(ppp->curr);
+ }
+ ppp--;
+ } while (ppp >= handle->path);
+ fflush(stdout);
+
+ return;
+}
+
+#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)
+#define dump_path(tag, handle, path) 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
+ */
+void ext2fs_extent_free(ext2_extent_handle_t handle)
+{
+ int i;
+
+ if (!handle)
+ return;
+
+ if (handle->path) {
+ for (i = 1; i < handle->max_paths; i++) {
+ if (handle->path[i].buf)
+ ext2fs_free_mem(&handle->path[i].buf);
+ }
+ ext2fs_free_mem(&handle->path);
+ }
+ ext2fs_free_mem(&handle);
+}
+
+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);
+}
+
+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);
+
+ handle->max_paths = handle->max_depth + 1;
+ retval = ext2fs_get_memzero(handle->max_paths *
+ sizeof(struct extent_path),
+ &handle->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 =
+ (EXT2_I_SIZE(handle->inode) + fs->blocksize - 1) >>
+ EXT2_BLOCK_SIZE_BITS(fs->super);
+ handle->path[0].blk = 0;
+ 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, *tp;
+ 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, l;
+ int failed_csum = 0;
+
+ 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;
+ /* fallthrough */
+ case EXT2_EXTENT_FIRST_SIB:
+ path->left = path->entries;
+ path->curr = 0;
+ /* fallthrough */
+ 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);
+ for (l = handle->level, tp = path; l > 0; l--, tp--) {
+ if (blk == tp->blk)
+ return EXT2_ET_EXTENT_CYCLE;
+ }
+ newpath->blk = blk;
+ 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;
+ }
+
+ if (!(handle->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_extent_block_csum_verify(handle->fs, handle->ino,
+ eh))
+ failed_csum = 1;
+
+ newpath->left = newpath->entries =
+ ext2fs_le16_to_cpu(eh->eh_entries);
+ newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max);
+
+ /* Make sure there is at least one extent present */
+ if (newpath->left <= 0)
+ return EXT2_ET_EXTENT_NO_DOWN;
+
+ 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;
+
+ if (failed_csum)
+ return EXT2_ET_EXTENT_CSUM_INVALID;
+
+ return 0;
+}
+
+static errcode_t update_path(ext2_extent_handle_t handle)
+{
+ blk64_t blk;
+ errcode_t retval;
+ struct ext3_extent_idx *ix;
+ struct ext3_extent_header *eh;
+
+ 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);
+
+ /* then update the checksum */
+ eh = (struct ext3_extent_header *)
+ handle->path[handle->level].buf;
+ retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino,
+ eh);
+ if (retval)
+ return retval;
+
+ 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.
+ */
+errcode_t ext2fs_extent_goto2(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 ext2fs_extent_goto2(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,
+ * it will simply return.
+ *
+ * Note a subtlety of this function -- if there happen to be two extents
+ * mapping the same lblk and someone calls fix_parents on the second of the two
+ * extents, the position of the extent handle after the call will be the second
+ * extent if nothing happened, or the first extent if something did. A caller
+ * in this situation must use ext2fs_extent_goto() after calling this function.
+ * Or simply don't map the same lblk with two extents, ever.
+ */
+errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle)
+{
+ int retval = 0;
+ int orig_height;
+ blk64_t start;
+ struct extent_path *path;
+ struct ext2fs_extent extent;
+ struct ext2_extent_info info;
+
+ 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;
+
+ if ((retval = ext2fs_extent_get_info(handle, &info)))
+ return retval;
+ orig_height = info.max_depth - info.curr_level;
+
+ /* 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_goto2(handle, orig_height, 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;
+}
+
+static int splitting_at_eof(struct ext2_extent_handle *handle,
+ struct extent_path *path)
+{
+ struct extent_path *ppp = path;
+ dump_path(__func__, handle, path);
+
+ if (handle->level == 0)
+ return 0;
+
+ do {
+ if (ppp->left)
+ return 0;
+ ppp--;
+ } while (ppp >= handle->path);
+
+ return 1;
+}
+
+/*
+ * 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,
+ int expand_allowed)
+{
+ 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;
+ int no_balance;
+
+ /* 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;
+
+ /* Try to put the index block before the first extent */
+ path = handle->path + handle->level;
+ eh = (struct ext3_extent_header *) path->buf;
+ if (handle->level == handle->max_depth) {
+ struct ext3_extent *ex;
+
+ ex = EXT_FIRST_EXTENT(eh);
+ goal_blk = ext2fs_le32_to_cpu(ex->ee_start) +
+ ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32);
+ } else {
+ struct ext3_extent_idx *ix;
+
+ ix = EXT_FIRST_INDEX(eh);
+ goal_blk = ext2fs_le32_to_cpu(ix->ei_leaf) +
+ ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32);
+ }
+ goal_blk -= EXT2FS_CLUSTER_RATIO(handle->fs);
+ goal_blk &= ~EXT2FS_CLUSTER_MASK(handle->fs);
+
+ /* 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;
+
+ retval = extent_node_split(handle, expand_allowed);
+ if (retval)
+ goto done;
+
+ /* get handle back to our original split position */
+ retval = ext2fs_extent_goto2(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;
+
+ /*
+ * Normally, we try to split a full node in half. This doesn't turn
+ * out so well if we're tacking extents on the end of the file because
+ * then we're stuck with a tree of half-full extent blocks. This of
+ * course doesn't apply to the root level.
+ */
+ no_balance = expand_allowed ? splitting_at_eof(handle, path) : 0;
+
+ /* 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_memzero((handle->max_paths + 1) *
+ sizeof(struct extent_path),
+ &newpath);
+ if (retval)
+ goto done;
+ } else {
+ if (no_balance)
+ tocopy = 1;
+ 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 && !no_balance) {
+#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)
+ goal_blk = ext2fs_find_inode_goal(handle->fs, handle->ino,
+ handle->inode, 0);
+ 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);
+
+ /* then update the checksum */
+ retval = ext2fs_extent_block_csum_set(handle->fs, handle->ino, neweh);
+ if (retval)
+ goto done;
+
+ /* ...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_paths);
+ handle->path = newpath;
+ newpath = path;
+ path = handle->path;
+ path->entries = 1;
+ path->left = path->max_entries - 1;
+ handle->max_depth++;
+ handle->max_paths++;
+ 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 = ext2fs_extent_goto2(handle, orig_height, orig_lblk);
+ if (retval)
+ goto done;
+
+ /* new node hooked in, so update inode block count (do this here?) */
+ ext2fs_iblk_add_blocks(handle->fs, handle->inode, 1);
+ 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_node_split(ext2_extent_handle_t handle)
+{
+ return extent_node_split(handle, 0);
+}
+
+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, 1);
+ 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->left = -1;
+ }
+
+ 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;
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval)
+ goto done;
+ /*
+ * Now pointing at inserted extent; move back to prev.
+ *
+ * We cannot use EXT2_EXTENT_PREV to go back; note the
+ * subtlety in the comment for fix_parents().
+ */
+ retval = ext2fs_extent_goto(handle, logical);
+ if (retval)
+ goto done;
+ retval = ext2fs_extent_get(handle,
+ EXT2_EXTENT_CURRENT,
+ &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_fix_parents(handle);
+ 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;
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval)
+ goto done;
+ } else {
+ __u32 save_length;
+ blk64_t save_lblk;
+ struct ext2fs_extent save_extent;
+ errcode_t r2;
+
+#ifdef DEBUG
+ printf("(re/un)mapping in middle of extent\n");
+#endif
+ /* need to split this extent; later */
+ save_lblk = extent.e_lblk;
+ save_length = extent.e_len;
+ save_extent = extent;
+
+ /* 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) {
+ r2 = ext2fs_extent_goto(handle, save_lblk);
+ if (r2 == 0)
+ (void)ext2fs_extent_replace(handle, 0,
+ &save_extent);
+ goto done;
+ }
+ }
+ /* add post-split extent */
+ extent.e_pblk += extent.e_len + 1;
+ extent.e_lblk += extent.e_len + 1;
+ extent.e_len = save_length - extent.e_len - 1;
+ retval = ext2fs_extent_insert(handle,
+ EXT2_EXTENT_INSERT_AFTER, &extent);
+ if (retval) {
+ if (physical) {
+ r2 = ext2fs_extent_goto(handle,
+ newextent.e_lblk);
+ if (r2 == 0)
+ (void)ext2fs_extent_delete(handle, 0);
+ }
+ r2 = ext2fs_extent_goto(handle, save_lblk);
+ if (r2 == 0)
+ (void)ext2fs_extent_replace(handle, 0,
+ &save_extent);
+ 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 */
+ ext2fs_extent_goto2(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;
+
+ /* Sanity check before memmove() */
+ if (path->left < 0)
+ return EXT2_ET_EXTENT_LEAF_BAD;
+
+ 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 *
+ EXT2FS_CLUSTER_RATIO(handle->fs)) / 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 = 0;
+ 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 = EXT_MAX_EXTENT_LBLK;
+ info->max_pblk = EXT_MAX_EXTENT_PBLK;
+ info->max_len = EXT_INIT_MAX_LEN;
+ info->max_uninit_len = EXT_UNINIT_MAX_LEN;
+
+ return 0;
+}
+
+static int ul_log2(unsigned long arg)
+{
+ int l = 0;
+
+ arg >>= 1;
+ while (arg) {
+ l++;
+ arg >>= 1;
+ }
+ return l;
+}
+
+size_t ext2fs_max_extent_depth(ext2_extent_handle_t handle)
+{
+ size_t iblock_sz = sizeof(((struct ext2_inode *)NULL)->i_block);
+ size_t iblock_extents = (iblock_sz - sizeof(struct ext3_extent_header)) /
+ sizeof(struct ext3_extent);
+ size_t extents_per_block = (handle->fs->blocksize -
+ sizeof(struct ext3_extent_header)) /
+ sizeof(struct ext3_extent);
+ static unsigned int last_blocksize = 0;
+ static size_t last_result = 0;
+
+ if (last_blocksize && last_blocksize == handle->fs->blocksize)
+ return last_result;
+
+ last_result = 1 + ((ul_log2(EXT_MAX_EXTENT_LBLK) - ul_log2(iblock_extents)) /
+ ul_log2(extents_per_block));
+ last_blocksize = handle->fs->blocksize;
+ return last_result;
+}
+
+errcode_t ext2fs_fix_extents_checksums(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ ext2_extent_handle_t handle;
+ struct ext2fs_extent extent;
+ errcode_t errcode;
+ int save_flags = fs->flags;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super) ||
+ (inode && !(inode->i_flags & EXT4_EXTENTS_FL)))
+ return 0;
+
+ errcode = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (errcode) {
+ if (errcode == EXT2_ET_INODE_NOT_EXTENT)
+ errcode = 0;
+ return errcode;
+ }
+
+ fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
+ errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
+ if (errcode)
+ goto out;
+
+ do {
+ /* Skip to the end of a block of leaf nodes */
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+ errcode = ext2fs_extent_get(handle,
+ EXT2_EXTENT_LAST_SIB,
+ &extent);
+ if (errcode)
+ break;
+ }
+
+ errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
+ if (errcode == EXT2_ET_EXTENT_CSUM_INVALID)
+ errcode = update_path(handle);
+ } while (errcode == 0);
+
+out:
+ /* Ok if we run off the end */
+ if (errcode == EXT2_ET_EXTENT_NO_NEXT)
+ errcode = 0;
+ ext2fs_extent_free(handle);
+ fs->flags = save_flags;
+ return errcode;
+}
+
+errcode_t ext2fs_decode_extent(struct ext2fs_extent *to, void *addr, int len)
+{
+ struct ext3_extent *from = (struct ext3_extent *)addr;
+
+ if (len != sizeof(struct ext3_extent))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ to->e_pblk = ext2fs_le32_to_cpu(from->ee_start) +
+ ((__u64) ext2fs_le16_to_cpu(from->ee_start_hi)
+ << 32);
+ to->e_lblk = ext2fs_le32_to_cpu(from->ee_block);
+ to->e_len = ext2fs_le16_to_cpu(from->ee_len);
+ to->e_flags = EXT2_EXTENT_FLAGS_LEAF;
+ if (to->e_len > EXT_INIT_MAX_LEN) {
+ to->e_len -= EXT_INIT_MAX_LEN;
+ to->e_flags |= EXT2_EXTENT_FLAGS_UNINIT;
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_count_blocks(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t *ret_count)
+{
+ ext2_extent_handle_t handle = NULL;
+ struct ext2fs_extent extent;
+ errcode_t errcode;
+ int i;
+ blk64_t blkcount = 0;
+ blk64_t *intermediate_nodes;
+
+ errcode = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (errcode)
+ goto out;
+
+ errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
+ if (errcode)
+ goto out;
+
+ errcode = ext2fs_get_array(handle->max_depth, sizeof(blk64_t),
+ &intermediate_nodes);
+ if (errcode)
+ goto out;
+
+ blkcount = handle->level;
+ while (!errcode) {
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
+ blkcount += extent.e_len;
+ for (i = 0; i < handle->level; i++) {
+ if (intermediate_nodes[i] !=
+ handle->path[i].end_blk) {
+ blkcount++;
+ intermediate_nodes[i] =
+ handle->path[i].end_blk;
+ }
+ }
+ }
+ errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
+ }
+ ext2fs_free_mem(&intermediate_nodes);
+out:
+ *ret_count = blkcount;
+ ext2fs_extent_free(handle);
+
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Override debugfs's prompt
+ */
+const char *debug_prog_name = "tst_extents";
+
+#endif
+
diff --git a/lib/ext2fs/fallocate.c b/lib/ext2fs/fallocate.c
new file mode 100644
index 0000000..5cde7d5
--- /dev/null
+++ b/lib/ext2fs/fallocate.c
@@ -0,0 +1,873 @@
+/*
+ * fallocate.c -- Allocate large chunks of file.
+ *
+ * Copyright (C) 2014 Oracle.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
+#else
+# define dbg_printf(f, a...)
+#endif
+
+/*
+ * Extent-based fallocate code.
+ *
+ * Find runs of unmapped logical blocks by starting at start and walking the
+ * extents until we reach the end of the range we want.
+ *
+ * For each run of unmapped blocks, try to find the extents on either side of
+ * the range. If there's a left extent that can grow by at least a cluster and
+ * there are lblocks between start and the next lcluster after start, see if
+ * there's an implied cluster allocation; if so, zero the blocks (if the left
+ * extent is initialized) and adjust the extent. Ditto for the blocks between
+ * the end of the last full lcluster and end, if there's a right extent.
+ *
+ * Try to attach as much as we can to the left extent, then try to attach as
+ * much as we can to the right extent. For the remainder, try to allocate the
+ * whole range; map in whatever we get; and repeat until we're done.
+ *
+ * To attach to a left extent, figure out the maximum amount we can add to the
+ * extent and try to allocate that much, and append if successful. To attach
+ * to a right extent, figure out the max we can add to the extent, try to
+ * allocate that much, and prepend if successful.
+ *
+ * We need an alloc_range function that tells us how much we can allocate given
+ * a maximum length and one of a suggested start, a fixed start, or a fixed end
+ * point.
+ *
+ * Every time we modify the extent tree we also need to update the block stats.
+ *
+ * At the end, update i_blocks and i_size appropriately.
+ */
+
+static void dbg_print_extent(const char *desc EXT2FS_ATTR((unused)),
+ const struct ext2fs_extent *extent EXT2FS_ATTR((unused)))
+{
+#ifdef DEBUG
+ 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);
+ fflush(stdout);
+#endif
+}
+
+static errcode_t claim_range(ext2_filsys fs, struct ext2_inode *inode,
+ blk64_t blk, blk64_t len)
+{
+ blk64_t clusters;
+
+ clusters = (len + EXT2FS_CLUSTER_RATIO(fs) - 1) /
+ EXT2FS_CLUSTER_RATIO(fs);
+ ext2fs_block_alloc_stats_range(fs, blk,
+ clusters * EXT2FS_CLUSTER_RATIO(fs), +1);
+ return ext2fs_iblk_add_blocks(fs, inode, clusters);
+}
+
+static errcode_t ext_falloc_helper(ext2_filsys fs,
+ int flags,
+ ext2_ino_t ino,
+ struct ext2_inode *inode,
+ ext2_extent_handle_t handle,
+ struct ext2fs_extent *left_ext,
+ struct ext2fs_extent *right_ext,
+ blk64_t range_start, blk64_t range_len,
+ blk64_t alloc_goal)
+{
+ struct ext2fs_extent newex, ex;
+ int op;
+ blk64_t fillable, pblk, plen, x, y;
+ blk64_t eof_blk = 0, cluster_fill = 0;
+ errcode_t err;
+ blk_t max_extent_len, max_uninit_len, max_init_len;
+
+#ifdef DEBUG
+ printf("%s: ", __func__);
+ if (left_ext)
+ printf("left_ext=%llu--%llu, ", left_ext->e_lblk,
+ left_ext->e_lblk + left_ext->e_len - 1);
+ if (right_ext)
+ printf("right_ext=%llu--%llu, ", right_ext->e_lblk,
+ right_ext->e_lblk + right_ext->e_len - 1);
+ printf("start=%llu len=%llu, goal=%llu\n", range_start, range_len,
+ alloc_goal);
+ fflush(stdout);
+#endif
+ /* Can't create initialized extents past EOF? */
+ if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF))
+ eof_blk = EXT2_I_SIZE(inode) / fs->blocksize;
+
+ /* The allocation goal must be as far into a cluster as range_start. */
+ alloc_goal = (alloc_goal & ~EXT2FS_CLUSTER_MASK(fs)) |
+ (range_start & EXT2FS_CLUSTER_MASK(fs));
+
+ max_uninit_len = EXT_UNINIT_MAX_LEN & ~EXT2FS_CLUSTER_MASK(fs);
+ max_init_len = EXT_INIT_MAX_LEN & ~EXT2FS_CLUSTER_MASK(fs);
+
+ /* We must lengthen the left extent to the end of the cluster */
+ if (left_ext && EXT2FS_CLUSTER_RATIO(fs) > 1) {
+ /* How many more blocks can be attached to left_ext? */
+ if (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
+ fillable = max_uninit_len - left_ext->e_len;
+ else
+ fillable = max_init_len - left_ext->e_len;
+
+ if (fillable > range_len)
+ fillable = range_len;
+ if (fillable == 0)
+ goto expand_right;
+
+ /*
+ * If range_start isn't on a cluster boundary, try an
+ * implied cluster allocation for left_ext.
+ */
+ cluster_fill = EXT2FS_CLUSTER_RATIO(fs) -
+ (range_start & EXT2FS_CLUSTER_MASK(fs));
+ cluster_fill &= EXT2FS_CLUSTER_MASK(fs);
+ if (cluster_fill == 0)
+ goto expand_right;
+
+ if (cluster_fill > fillable)
+ cluster_fill = fillable;
+
+ /* Don't expand an initialized left_ext beyond EOF */
+ if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) {
+ x = left_ext->e_lblk + left_ext->e_len - 1;
+ dbg_printf("%s: lend=%llu newlend=%llu eofblk=%llu\n",
+ __func__, x, x + cluster_fill, eof_blk);
+ if (eof_blk >= x && eof_blk <= x + cluster_fill)
+ cluster_fill = eof_blk - x;
+ if (cluster_fill == 0)
+ goto expand_right;
+ }
+
+ err = ext2fs_extent_goto(handle, left_ext->e_lblk);
+ if (err)
+ goto expand_right;
+ left_ext->e_len += cluster_fill;
+ range_start += cluster_fill;
+ range_len -= cluster_fill;
+ alloc_goal += cluster_fill;
+
+ dbg_print_extent("ext_falloc clus left+", left_ext);
+ err = ext2fs_extent_replace(handle, 0, left_ext);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ /* Zero blocks */
+ if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) {
+ err = ext2fs_zero_blocks2(fs, left_ext->e_pblk +
+ left_ext->e_len -
+ cluster_fill, cluster_fill,
+ NULL, NULL);
+ if (err)
+ goto out;
+ }
+ }
+
+expand_right:
+ /* We must lengthen the right extent to the beginning of the cluster */
+ if (right_ext && EXT2FS_CLUSTER_RATIO(fs) > 1) {
+ /* How much can we attach to right_ext? */
+ if (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
+ fillable = max_uninit_len - right_ext->e_len;
+ else
+ fillable = max_init_len - right_ext->e_len;
+
+ if (fillable > range_len)
+ fillable = range_len;
+ if (fillable == 0)
+ goto try_merge;
+
+ /*
+ * If range_end isn't on a cluster boundary, try an implied
+ * cluster allocation for right_ext.
+ */
+ cluster_fill = right_ext->e_lblk & EXT2FS_CLUSTER_MASK(fs);
+ if (cluster_fill == 0)
+ goto try_merge;
+
+ err = ext2fs_extent_goto(handle, right_ext->e_lblk);
+ if (err)
+ goto out;
+
+ if (cluster_fill > fillable)
+ cluster_fill = fillable;
+ right_ext->e_lblk -= cluster_fill;
+ right_ext->e_pblk -= cluster_fill;
+ right_ext->e_len += cluster_fill;
+ range_len -= cluster_fill;
+
+ dbg_print_extent("ext_falloc clus right+", right_ext);
+ err = ext2fs_extent_replace(handle, 0, right_ext);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ /* Zero blocks if necessary */
+ if (!(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) {
+ err = ext2fs_zero_blocks2(fs, right_ext->e_pblk,
+ cluster_fill, NULL, NULL);
+ if (err)
+ goto out;
+ }
+ }
+
+try_merge:
+ /* Merge both extents together, perhaps? */
+ if (left_ext && right_ext) {
+ /* Are the two extents mergeable? */
+ if ((left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) !=
+ (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT))
+ goto try_left;
+
+ /* User requires init/uninit but extent is uninit/init. */
+ if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
+ (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) ||
+ ((flags & EXT2_FALLOCATE_FORCE_UNINIT) &&
+ !(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)))
+ goto try_left;
+
+ /*
+ * Skip initialized extent unless user wants to zero blocks
+ * or requires init extent.
+ */
+ if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (!(flags & EXT2_FALLOCATE_ZERO_BLOCKS) ||
+ !(flags & EXT2_FALLOCATE_FORCE_INIT)))
+ goto try_left;
+
+ /* Will it even fit? */
+ x = left_ext->e_len + range_len + right_ext->e_len;
+ if (x > (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT ?
+ max_uninit_len : max_init_len))
+ goto try_left;
+
+ err = ext2fs_extent_goto(handle, left_ext->e_lblk);
+ if (err)
+ goto try_left;
+
+ /* Allocate blocks */
+ y = left_ext->e_pblk + left_ext->e_len;
+ err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL |
+ EXT2_NEWRANGE_MIN_LENGTH, y,
+ right_ext->e_pblk - y + 1, NULL,
+ &pblk, &plen);
+ if (err)
+ goto try_left;
+ if (pblk + plen != right_ext->e_pblk)
+ goto try_left;
+ err = claim_range(fs, inode, pblk, plen);
+ if (err)
+ goto out;
+
+ /* Modify extents */
+ left_ext->e_len = x;
+ dbg_print_extent("ext_falloc merge", left_ext);
+ err = ext2fs_extent_replace(handle, 0, left_ext);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &newex);
+ if (err)
+ goto out;
+ err = ext2fs_extent_delete(handle, 0);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+ *right_ext = *left_ext;
+
+ /* Zero blocks */
+ if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
+ err = ext2fs_zero_blocks2(fs, range_start, range_len,
+ NULL, NULL);
+ if (err)
+ goto out;
+ }
+
+ return 0;
+ }
+
+try_left:
+ /* Extend the left extent */
+ if (left_ext) {
+ /* How many more blocks can be attached to left_ext? */
+ if (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
+ fillable = max_uninit_len - left_ext->e_len;
+ else if (flags & EXT2_FALLOCATE_ZERO_BLOCKS)
+ fillable = max_init_len - left_ext->e_len;
+ else
+ fillable = 0;
+
+ /* User requires init/uninit but extent is uninit/init. */
+ if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
+ (left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) ||
+ ((flags & EXT2_FALLOCATE_FORCE_UNINIT) &&
+ !(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)))
+ goto try_right;
+
+ if (fillable > range_len)
+ fillable = range_len;
+
+ /* Don't expand an initialized left_ext beyond EOF */
+ x = left_ext->e_lblk + left_ext->e_len - 1;
+ if (!(flags & EXT2_FALLOCATE_INIT_BEYOND_EOF)) {
+ dbg_printf("%s: lend=%llu newlend=%llu eofblk=%llu\n",
+ __func__, x, x + fillable, eof_blk);
+ if (eof_blk >= x && eof_blk <= x + fillable)
+ fillable = eof_blk - x;
+ }
+
+ if (fillable == 0)
+ goto try_right;
+
+ /* Test if the right edge of the range is already mapped? */
+ if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
+ err = ext2fs_map_cluster_block(fs, ino, inode,
+ x + fillable, &pblk);
+ if (err)
+ goto out;
+ if (pblk)
+ fillable -= 1 + ((x + fillable)
+ & EXT2FS_CLUSTER_MASK(fs));
+ if (fillable == 0)
+ goto try_right;
+ }
+
+ /* Allocate range of blocks */
+ x = left_ext->e_pblk + left_ext->e_len;
+ err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL |
+ EXT2_NEWRANGE_MIN_LENGTH,
+ x, fillable, NULL, &pblk, &plen);
+ if (err)
+ goto try_right;
+ err = claim_range(fs, inode, pblk, plen);
+ if (err)
+ goto out;
+
+ /* Modify left_ext */
+ err = ext2fs_extent_goto(handle, left_ext->e_lblk);
+ if (err)
+ goto out;
+ range_start += plen;
+ range_len -= plen;
+ left_ext->e_len += plen;
+ dbg_print_extent("ext_falloc left+", left_ext);
+ err = ext2fs_extent_replace(handle, 0, left_ext);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ /* Zero blocks if necessary */
+ if (!(left_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
+ err = ext2fs_zero_blocks2(fs, pblk, plen, NULL, NULL);
+ if (err)
+ goto out;
+ }
+ }
+
+try_right:
+ /* Extend the right extent */
+ if (right_ext) {
+ /* How much can we attach to right_ext? */
+ if (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
+ fillable = max_uninit_len - right_ext->e_len;
+ else if (flags & EXT2_FALLOCATE_ZERO_BLOCKS)
+ fillable = max_init_len - right_ext->e_len;
+ else
+ fillable = 0;
+
+ /* User requires init/uninit but extent is uninit/init. */
+ if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
+ (right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)) ||
+ ((flags & EXT2_FALLOCATE_FORCE_UNINIT) &&
+ !(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT)))
+ goto try_anywhere;
+
+ if (fillable > range_len)
+ fillable = range_len;
+ if (fillable == 0)
+ goto try_anywhere;
+
+ /* Test if the left edge of the range is already mapped? */
+ if (EXT2FS_CLUSTER_RATIO(fs) > 1) {
+ err = ext2fs_map_cluster_block(fs, ino, inode,
+ right_ext->e_lblk - fillable, &pblk);
+ if (err)
+ goto out;
+ if (pblk)
+ fillable -= EXT2FS_CLUSTER_RATIO(fs) -
+ ((right_ext->e_lblk - fillable)
+ & EXT2FS_CLUSTER_MASK(fs));
+ if (fillable == 0)
+ goto try_anywhere;
+ }
+
+ /*
+ * FIXME: It would be nice if we could handle allocating a
+ * variable range from a fixed end point instead of just
+ * skipping to the general allocator if the whole range is
+ * unavailable.
+ */
+ err = ext2fs_new_range(fs, EXT2_NEWRANGE_FIXED_GOAL |
+ EXT2_NEWRANGE_MIN_LENGTH,
+ right_ext->e_pblk - fillable,
+ fillable, NULL, &pblk, &plen);
+ if (err)
+ goto try_anywhere;
+ err = claim_range(fs, inode,
+ pblk & ~EXT2FS_CLUSTER_MASK(fs),
+ plen + (pblk & EXT2FS_CLUSTER_MASK(fs)));
+ if (err)
+ goto out;
+
+ /* Modify right_ext */
+ err = ext2fs_extent_goto(handle, right_ext->e_lblk);
+ if (err)
+ goto out;
+ range_len -= plen;
+ right_ext->e_lblk -= plen;
+ right_ext->e_pblk -= plen;
+ right_ext->e_len += plen;
+ dbg_print_extent("ext_falloc right+", right_ext);
+ err = ext2fs_extent_replace(handle, 0, right_ext);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ /* Zero blocks if necessary */
+ if (!(right_ext->e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
+ err = ext2fs_zero_blocks2(fs, pblk,
+ plen + cluster_fill, NULL, NULL);
+ if (err)
+ goto out;
+ }
+ }
+
+try_anywhere:
+ /* Try implied cluster alloc on the left and right ends */
+ if (range_len > 0 && (range_start & EXT2FS_CLUSTER_MASK(fs))) {
+ cluster_fill = EXT2FS_CLUSTER_RATIO(fs) -
+ (range_start & EXT2FS_CLUSTER_MASK(fs));
+ cluster_fill &= EXT2FS_CLUSTER_MASK(fs);
+ if (cluster_fill > range_len)
+ cluster_fill = range_len;
+ newex.e_lblk = range_start;
+ err = ext2fs_map_cluster_block(fs, ino, inode, newex.e_lblk,
+ &pblk);
+ if (err)
+ goto out;
+ if (pblk == 0)
+ goto try_right_implied;
+ newex.e_pblk = pblk;
+ newex.e_len = cluster_fill;
+ newex.e_flags = (flags & EXT2_FALLOCATE_FORCE_INIT ? 0 :
+ EXT2_EXTENT_FLAGS_UNINIT);
+ dbg_print_extent("ext_falloc iclus left+", &newex);
+ ext2fs_extent_goto(handle, newex.e_lblk);
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &ex);
+ if (err == EXT2_ET_NO_CURRENT_NODE)
+ ex.e_lblk = 0;
+ else if (err)
+ goto out;
+
+ if (ex.e_lblk > newex.e_lblk)
+ op = 0; /* insert before */
+ else
+ op = EXT2_EXTENT_INSERT_AFTER;
+ dbg_printf("%s: inserting %s lblk %llu newex=%llu\n",
+ __func__, op ? "after" : "before", ex.e_lblk,
+ newex.e_lblk);
+ err = ext2fs_extent_insert(handle, op, &newex);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
+ err = ext2fs_zero_blocks2(fs, newex.e_pblk,
+ newex.e_len, NULL, NULL);
+ if (err)
+ goto out;
+ }
+
+ range_start += cluster_fill;
+ range_len -= cluster_fill;
+ }
+
+try_right_implied:
+ y = range_start + range_len;
+ if (range_len > 0 && (y & EXT2FS_CLUSTER_MASK(fs))) {
+ cluster_fill = y & EXT2FS_CLUSTER_MASK(fs);
+ if (cluster_fill > range_len)
+ cluster_fill = range_len;
+ newex.e_lblk = y & ~EXT2FS_CLUSTER_MASK(fs);
+ err = ext2fs_map_cluster_block(fs, ino, inode, newex.e_lblk,
+ &pblk);
+ if (err)
+ goto out;
+ if (pblk == 0)
+ goto no_implied;
+ newex.e_pblk = pblk;
+ newex.e_len = cluster_fill;
+ newex.e_flags = (flags & EXT2_FALLOCATE_FORCE_INIT ? 0 :
+ EXT2_EXTENT_FLAGS_UNINIT);
+ dbg_print_extent("ext_falloc iclus right+", &newex);
+ ext2fs_extent_goto(handle, newex.e_lblk);
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &ex);
+ if (err == EXT2_ET_NO_CURRENT_NODE)
+ ex.e_lblk = 0;
+ else if (err)
+ goto out;
+
+ if (ex.e_lblk > newex.e_lblk)
+ op = 0; /* insert before */
+ else
+ op = EXT2_EXTENT_INSERT_AFTER;
+ dbg_printf("%s: inserting %s lblk %llu newex=%llu\n",
+ __func__, op ? "after" : "before", ex.e_lblk,
+ newex.e_lblk);
+ err = ext2fs_extent_insert(handle, op, &newex);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
+ err = ext2fs_zero_blocks2(fs, newex.e_pblk,
+ newex.e_len, NULL, NULL);
+ if (err)
+ goto out;
+ }
+
+ range_len -= cluster_fill;
+ }
+
+no_implied:
+ if (range_len == 0)
+ return 0;
+
+ newex.e_lblk = range_start;
+ if (flags & EXT2_FALLOCATE_FORCE_INIT) {
+ max_extent_len = max_init_len;
+ newex.e_flags = 0;
+ } else {
+ max_extent_len = max_uninit_len;
+ newex.e_flags = EXT2_EXTENT_FLAGS_UNINIT;
+ }
+ pblk = alloc_goal;
+ y = range_len;
+ for (x = 0; x < y;) {
+ cluster_fill = newex.e_lblk & EXT2FS_CLUSTER_MASK(fs);
+ fillable = min(range_len + cluster_fill, max_extent_len);
+ err = ext2fs_new_range(fs, 0, pblk & ~EXT2FS_CLUSTER_MASK(fs),
+ fillable,
+ NULL, &pblk, &plen);
+ if (err)
+ goto out;
+ err = claim_range(fs, inode, pblk, plen);
+ if (err)
+ goto out;
+
+ /* Create extent */
+ newex.e_pblk = pblk + cluster_fill;
+ newex.e_len = plen - cluster_fill;
+ dbg_print_extent("ext_falloc create", &newex);
+ ext2fs_extent_goto(handle, newex.e_lblk);
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &ex);
+ if (err == EXT2_ET_NO_CURRENT_NODE)
+ ex.e_lblk = 0;
+ else if (err)
+ goto out;
+
+ if (ex.e_lblk > newex.e_lblk)
+ op = 0; /* insert before */
+ else
+ op = EXT2_EXTENT_INSERT_AFTER;
+ dbg_printf("%s: inserting %s lblk %llu newex=%llu\n",
+ __func__, op ? "after" : "before", ex.e_lblk,
+ newex.e_lblk);
+ err = ext2fs_extent_insert(handle, op, &newex);
+ if (err)
+ goto out;
+ err = ext2fs_extent_fix_parents(handle);
+ if (err)
+ goto out;
+
+ if (!(newex.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ (flags & EXT2_FALLOCATE_ZERO_BLOCKS)) {
+ err = ext2fs_zero_blocks2(fs, pblk, plen, NULL, NULL);
+ if (err)
+ goto out;
+ }
+
+ /* Update variables at end of loop */
+ x += plen - cluster_fill;
+ range_len -= plen - cluster_fill;
+ newex.e_lblk += plen - cluster_fill;
+ pblk += plen - cluster_fill;
+ if (pblk >= ext2fs_blocks_count(fs->super))
+ pblk = fs->super->s_first_data_block;
+ }
+
+out:
+ return err;
+}
+
+static errcode_t extent_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t goal,
+ blk64_t start, blk64_t len)
+{
+ ext2_extent_handle_t handle;
+ struct ext2fs_extent left_extent, right_extent;
+ struct ext2fs_extent *left_adjacent, *right_adjacent;
+ errcode_t err;
+ blk64_t range_start, range_end = 0, end, next;
+ blk64_t count, goal_distance;
+
+ end = start + len - 1;
+ err = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (err)
+ return err;
+
+ /*
+ * Find the extent closest to the start of the alloc range. We don't
+ * check the return value because _goto() sets the current node to the
+ * next-lowest extent if 'start' is in a hole; or the next-highest
+ * extent if there aren't any lower ones; or doesn't set a current node
+ * if there was a real error reading the extent tree. In that case,
+ * _get() will error out.
+ */
+start_again:
+ ext2fs_extent_goto(handle, start);
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &left_extent);
+ if (err == EXT2_ET_NO_CURRENT_NODE) {
+ blk64_t max_blocks = ext2fs_blocks_count(fs->super);
+
+ if (goal == ~0ULL)
+ goal = ext2fs_find_inode_goal(fs, ino, inode, start);
+ err = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
+ goal, max_blocks - 1, &goal);
+ goal += start;
+ err = ext_falloc_helper(fs, flags, ino, inode, handle, NULL,
+ NULL, start, len, goal);
+ goto errout;
+ } else if (err)
+ goto errout;
+
+ dbg_print_extent("ext_falloc initial", &left_extent);
+ next = left_extent.e_lblk + left_extent.e_len;
+ if (left_extent.e_lblk > start) {
+ /* The nearest extent we found was beyond start??? */
+ goal = left_extent.e_pblk - (left_extent.e_lblk - start);
+ err = ext_falloc_helper(fs, flags, ino, inode, handle, NULL,
+ &left_extent, start,
+ left_extent.e_lblk - start, goal);
+ if (err)
+ goto errout;
+
+ goto start_again;
+ } else if (next >= start) {
+ range_start = next;
+ left_adjacent = &left_extent;
+ } else {
+ range_start = start;
+ left_adjacent = NULL;
+ }
+ goal = left_extent.e_pblk + (range_start - left_extent.e_lblk);
+
+ do {
+ err = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF,
+ &right_extent);
+ dbg_printf("%s: ino=%d get next =%d\n", __func__, ino,
+ (int)err);
+ dbg_print_extent("ext_falloc next", &right_extent);
+ /* Stop if we've seen this extent before */
+ if (!err && right_extent.e_lblk <= left_extent.e_lblk)
+ err = EXT2_ET_EXTENT_NO_NEXT;
+
+ if (err && err != EXT2_ET_EXTENT_NO_NEXT)
+ goto errout;
+ if (err == EXT2_ET_EXTENT_NO_NEXT ||
+ right_extent.e_lblk > end + 1) {
+ range_end = end;
+ right_adjacent = NULL;
+ } else {
+ /* Handle right_extent.e_lblk <= end */
+ range_end = right_extent.e_lblk - 1;
+ right_adjacent = &right_extent;
+ }
+ goal_distance = range_start - next;
+ if (err != EXT2_ET_EXTENT_NO_NEXT &&
+ goal_distance > (range_end - right_extent.e_lblk))
+ goal = right_extent.e_pblk -
+ (right_extent.e_lblk - range_start);
+
+ dbg_printf("%s: ino=%d rstart=%llu rend=%llu\n", __func__, ino,
+ range_start, range_end);
+ err = 0;
+ if (range_start <= range_end) {
+ count = range_end - range_start + 1;
+ err = ext_falloc_helper(fs, flags, ino, inode, handle,
+ left_adjacent, right_adjacent,
+ range_start, count, goal);
+ if (err)
+ goto errout;
+ }
+
+ if (range_end == end)
+ break;
+
+ err = ext2fs_extent_goto(handle, right_extent.e_lblk);
+ if (err)
+ goto errout;
+ next = right_extent.e_lblk + right_extent.e_len;
+ left_extent = right_extent;
+ left_adjacent = &left_extent;
+ range_start = next;
+ goal = left_extent.e_pblk + (range_start - left_extent.e_lblk);
+ } while (range_end < end);
+
+errout:
+ ext2fs_extent_free(handle);
+ return err;
+}
+
+/*
+ * Map physical blocks to a range of logical blocks within a file. The range
+ * of logical blocks are (start, start + len). If there are already extents,
+ * the mappings will try to extend the mappings; otherwise, it will try to map
+ * start as if logical block 0 points to goal. If goal is ~0ULL, then the goal
+ * is calculated based on the inode group.
+ *
+ * Flags:
+ * - EXT2_FALLOCATE_ZERO_BLOCKS: Zero the blocks that are allocated.
+ * - EXT2_FALLOCATE_FORCE_INIT: Create only initialized extents.
+ * - EXT2_FALLOCATE_FORCE_UNINIT: Create only uninitialized extents.
+ * - EXT2_FALLOCATE_INIT_BEYOND_EOF: Create extents beyond EOF.
+ *
+ * If neither FORCE_INIT nor FORCE_UNINIT are specified, this function will
+ * try to expand any extents it finds, zeroing blocks as necessary.
+ */
+errcode_t ext2fs_fallocate(ext2_filsys fs, int flags, ext2_ino_t ino,
+ struct ext2_inode *inode, blk64_t goal,
+ blk64_t start, blk64_t len)
+{
+ struct ext2_inode inode_buf;
+ blk64_t blk, x, zero_blk, last = 0;
+ int zero_len = 0;
+ errcode_t err;
+
+ if (((flags & EXT2_FALLOCATE_FORCE_INIT) &&
+ (flags & EXT2_FALLOCATE_FORCE_UNINIT)) ||
+ (flags & ~EXT2_FALLOCATE_ALL_FLAGS))
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (len > ext2fs_blocks_count(fs->super))
+ return EXT2_ET_BLOCK_ALLOC_FAIL;
+ else if (len == 0)
+ return 0;
+
+ /* Read inode structure if necessary */
+ if (!inode) {
+ err = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (err)
+ return err;
+ inode = &inode_buf;
+ }
+ dbg_printf("%s: ino=%d start=%llu len=%llu goal=%llu\n", __func__, ino,
+ start, len, goal);
+
+ if (inode->i_flags & EXT4_EXTENTS_FL) {
+ err = extent_fallocate(fs, flags, ino, inode, goal, start, len);
+ goto out;
+ }
+
+ /* XXX: Allocate a bunch of blocks the slow way */
+ for (blk = start; blk < start + len; blk++) {
+ err = ext2fs_bmap2(fs, ino, inode, NULL, 0, blk, 0, &x);
+ if (err)
+ return err;
+ if (x)
+ continue;
+
+ err = ext2fs_bmap2(fs, ino, inode, NULL, BMAP_ALLOC,
+ blk, 0, &x);
+ if (err)
+ goto errout;
+ if ((zero_len && (x != last+1)) ||
+ (zero_len >= 65536)) {
+ err = ext2fs_zero_blocks2(fs, zero_blk, zero_len,
+ NULL, NULL);
+ zero_len = 0;
+ if (err)
+ goto errout;
+ }
+ if (zero_len == 0) {
+ zero_blk = x;
+ zero_len = 1;
+ } else {
+ zero_len++;
+ }
+ last = x;
+ }
+
+out:
+ if (inode == &inode_buf)
+ ext2fs_write_inode(fs, ino, inode);
+errout:
+ if (zero_len)
+ ext2fs_zero_blocks2(fs, zero_blk, zero_len, NULL, NULL);
+ return err;
+}
diff --git a/lib/ext2fs/fast_commit.h b/lib/ext2fs/fast_commit.h
new file mode 100644
index 0000000..4ad38f1
--- /dev/null
+++ b/lib/ext2fs/fast_commit.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __FAST_COMMIT_H__
+#define __FAST_COMMIT_H__
+
+#include "jfs_compat.h"
+
+/*
+ * Note this file is present in e2fsprogs/lib/ext2fs/fast_commit.h and
+ * linux/fs/ext4/fast_commit.h. These file should always be byte identical.
+ */
+
+/* Fast commit tags */
+#define EXT4_FC_TAG_ADD_RANGE 0x0001
+#define EXT4_FC_TAG_DEL_RANGE 0x0002
+#define EXT4_FC_TAG_CREAT 0x0003
+#define EXT4_FC_TAG_LINK 0x0004
+#define EXT4_FC_TAG_UNLINK 0x0005
+#define EXT4_FC_TAG_INODE 0x0006
+#define EXT4_FC_TAG_PAD 0x0007
+#define EXT4_FC_TAG_TAIL 0x0008
+#define EXT4_FC_TAG_HEAD 0x0009
+
+#define EXT4_FC_SUPPORTED_FEATURES 0x0
+
+/* On disk fast commit tlv value structures */
+
+/* Fast commit on disk tag length structure */
+struct ext4_fc_tl {
+ __le16 fc_tag;
+ __le16 fc_len;
+};
+
+/* Value structure for tag EXT4_FC_TAG_HEAD. */
+struct ext4_fc_head {
+ __le32 fc_features;
+ __le32 fc_tid;
+};
+
+/* Value structure for EXT4_FC_TAG_ADD_RANGE. */
+struct ext4_fc_add_range {
+ __le32 fc_ino;
+ __u8 fc_ex[12];
+};
+
+/* Value structure for tag EXT4_FC_TAG_DEL_RANGE. */
+struct ext4_fc_del_range {
+ __le32 fc_ino;
+ __le32 fc_lblk;
+ __le32 fc_len;
+};
+
+/*
+ * This is the value structure for tags EXT4_FC_TAG_CREAT, EXT4_FC_TAG_LINK
+ * and EXT4_FC_TAG_UNLINK.
+ */
+struct ext4_fc_dentry_info {
+ __le32 fc_parent_ino;
+ __le32 fc_ino;
+ __u8 fc_dname[0];
+};
+
+/* Value structure for EXT4_FC_TAG_INODE and EXT4_FC_TAG_INODE_PARTIAL. */
+struct ext4_fc_inode {
+ __le32 fc_ino;
+ __u8 fc_raw_inode[0];
+};
+
+/* Value structure for tag EXT4_FC_TAG_TAIL. */
+struct ext4_fc_tail {
+ __le32 fc_tid;
+ __le32 fc_crc;
+};
+
+/*
+ * Fast commit reason codes
+ */
+enum {
+ /*
+ * Commit status codes:
+ */
+ EXT4_FC_REASON_OK = 0,
+ EXT4_FC_REASON_INELIGIBLE,
+ EXT4_FC_REASON_ALREADY_COMMITTED,
+ EXT4_FC_REASON_FC_START_FAILED,
+ EXT4_FC_REASON_FC_FAILED,
+
+ /*
+ * Fast commit ineligiblity reasons:
+ */
+ EXT4_FC_REASON_XATTR = 0,
+ EXT4_FC_REASON_CROSS_RENAME,
+ EXT4_FC_REASON_JOURNAL_FLAG_CHANGE,
+ EXT4_FC_REASON_NOMEM,
+ EXT4_FC_REASON_SWAP_BOOT,
+ EXT4_FC_REASON_RESIZE,
+ EXT4_FC_REASON_RENAME_DIR,
+ EXT4_FC_REASON_FALLOC_RANGE,
+ EXT4_FC_REASON_INODE_JOURNAL_DATA,
+ EXT4_FC_COMMIT_FAILED,
+ EXT4_FC_REASON_MAX
+};
+
+#ifdef __KERNEL__
+/*
+ * In memory list of dentry updates that are performed on the file
+ * system used by fast commit code.
+ */
+struct ext4_fc_dentry_update {
+ int fcd_op; /* Type of update create / unlink / link */
+ int fcd_parent; /* Parent inode number */
+ int fcd_ino; /* Inode number */
+ struct qstr fcd_name; /* Dirent name */
+ unsigned char fcd_iname[DNAME_INLINE_LEN]; /* Dirent name string */
+ struct list_head fcd_list;
+};
+
+struct ext4_fc_stats {
+ unsigned int fc_ineligible_reason_count[EXT4_FC_REASON_MAX];
+ unsigned long fc_num_commits;
+ unsigned long fc_ineligible_commits;
+ unsigned long fc_numblks;
+};
+
+#define EXT4_FC_REPLAY_REALLOC_INCREMENT 4
+
+/*
+ * Physical block regions added to different inodes due to fast commit
+ * recovery. These are set during the SCAN phase. During the replay phase,
+ * our allocator excludes these from its allocation. This ensures that
+ * we don't accidentally allocating a block that is going to be used by
+ * another inode.
+ */
+struct ext4_fc_alloc_region {
+ ext4_lblk_t lblk;
+ ext4_fsblk_t pblk;
+ int ino, len;
+};
+
+/*
+ * Fast commit replay state.
+ */
+struct ext4_fc_replay_state {
+ int fc_replay_num_tags;
+ int fc_replay_expected_off;
+ int fc_current_pass;
+ int fc_cur_tag;
+ int fc_crc;
+ struct ext4_fc_alloc_region *fc_regions;
+ int fc_regions_size, fc_regions_used, fc_regions_valid;
+ int *fc_modified_inodes;
+ int fc_modified_inodes_used, fc_modified_inodes_size;
+};
+
+#define region_last(__region) (((__region)->lblk) + ((__region)->len) - 1)
+#endif
+
+static inline const char *tag2str(__u16 tag)
+{
+ switch (tag) {
+ case EXT4_FC_TAG_LINK:
+ return "ADD_ENTRY";
+ case EXT4_FC_TAG_UNLINK:
+ return "DEL_ENTRY";
+ case EXT4_FC_TAG_ADD_RANGE:
+ return "ADD_RANGE";
+ case EXT4_FC_TAG_CREAT:
+ return "CREAT_DENTRY";
+ case EXT4_FC_TAG_DEL_RANGE:
+ return "DEL_RANGE";
+ case EXT4_FC_TAG_INODE:
+ return "INODE";
+ case EXT4_FC_TAG_PAD:
+ return "PAD";
+ case EXT4_FC_TAG_TAIL:
+ return "TAIL";
+ case EXT4_FC_TAG_HEAD:
+ return "HEAD";
+ default:
+ return "ERROR";
+ }
+}
+
+/* Get length of a particular tlv */
+static inline int ext4_fc_tag_len(struct ext4_fc_tl *tl)
+{
+ return le16_to_cpu(tl->fc_len);
+}
+
+#endif /* __FAST_COMMIT_H__ */
diff --git a/lib/ext2fs/fiemap.h b/lib/ext2fs/fiemap.h
new file mode 100644
index 0000000..33ab8fb
--- /dev/null
+++ b/lib/ext2fs/fiemap.h
@@ -0,0 +1,93 @@
+/*
+ * FS_IOC_FIEMAP ioctl infrastructure.
+ *
+ * Some portions copyright (C) 2007 Cluster File Systems, Inc
+ *
+ * Authors: Mark Fasheh <mfasheh@suse.com>
+ * Kalpak Shah <kalpak.shah@sun.com>
+ * Andreas Dilger <adilger@sun.com>
+ */
+
+#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;
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+ struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic pop
+#endif
+};
+
+#if defined(__linux__) && !defined(FS_IOC_FIEMAP)
+#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap)
+#endif
+
+#if defined(__linux__) && !defined(FS_IOC_GETSTATE)
+#define EXT4_IOC_GETSTATE _IOW('f', 41, __u32)
+#endif
+
+#if defined(__linux__) && !defined(EXT4_IOC_GET_ES_CACHE)
+#define EXT4_IOC_GET_ES_CACHE _IOWR('f', 42, struct fiemap)
+#endif
+
+#if defined(__linux__) && !defined(EXT4_STATE_FLAG_EXT_PRECACHED)
+#define EXT4_STATE_FLAG_EXT_PRECACHED 0x00000001
+#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_FLAG_CACHE 0x00000004 /* request caching of the extents */
+
+#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. */
+#define FIEMAP_EXTENT_SHARED 0x00002000 /* Space shared with other
+ * files. */
+
+#define EXT4_FIEMAP_EXTENT_HOLE 0x08000000 /* Entry in extent status
+ cache for a hole*/
+
+#endif /* _LINUX_FIEMAP_H */
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
new file mode 100644
index 0000000..818f7f0
--- /dev/null
+++ b/lib/ext2fs/fileio.c
@@ -0,0 +1,666 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.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;
+};
+
+struct block_entry {
+ blk64_t physblock;
+ unsigned char sha[EXT2FS_SHA512_LENGTH];
+};
+typedef struct block_entry *block_entry_t;
+
+#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 returns the inode number from the structure */
+ext2_ino_t ext2fs_file_get_inode_num(ext2_file_t file)
+{
+ if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+ return 0;
+ return file->ino;
+}
+
+/*
+ * 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;
+ int ret_flags;
+ blk64_t dontcare;
+
+ 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;
+
+ /* Is this an uninit block? */
+ if (file->physblock && file->inode.i_flags & EXT4_EXTENTS_FL) {
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode, BMAP_BUFFER,
+ 0, file->blockno, &ret_flags, &dontcare);
+ if (retval)
+ return retval;
+ if (ret_flags & BMAP_RET_UNINIT) {
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode,
+ BMAP_BUFFER, BMAP_SET,
+ file->blockno, 0,
+ &file->physblock);
+ if (retval)
+ return retval;
+ }
+ }
+
+ /*
+ * 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_blk64(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)
+{
+ blk64_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;
+ int ret_flags;
+
+ if (!(file->flags & EXT2_FILE_BUF_VALID)) {
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode,
+ BMAP_BUFFER, 0, file->blockno, &ret_flags,
+ &file->physblock);
+ if (retval)
+ return retval;
+ if (!dontfill) {
+ if (file->physblock &&
+ !(ret_flags & BMAP_RET_UNINIT)) {
+ retval = io_channel_read_blk64(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;
+}
+
+
+static errcode_t
+ext2fs_file_read_inline_data(ext2_file_t file, void *buf,
+ unsigned int wanted, unsigned int *got)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned int count = 0;
+ size_t size;
+
+ fs = file->fs;
+ retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
+ file->buf, &size);
+ if (retval)
+ return retval;
+
+ if (file->pos >= size)
+ goto out;
+
+ count = size - file->pos;
+ if (count > wanted)
+ count = wanted;
+ memcpy(buf, file->buf + file->pos, count);
+ file->pos += count;
+ buf = (char *) buf + count;
+
+out:
+ if (got)
+ *got = count;
+ 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;
+
+ /* If an inode has inline data, things get complicated. */
+ if (file->inode.i_flags & EXT4_INLINE_DATA_FL)
+ return ext2fs_file_read_inline_data(file, buf, wanted, got);
+
+ 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;
+}
+
+
+static errcode_t
+ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
+ unsigned int nbytes, unsigned int *written)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned int count = 0;
+ size_t size;
+
+ fs = file->fs;
+ retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
+ file->buf, &size);
+ if (retval)
+ return retval;
+
+ if (file->pos < size) {
+ count = nbytes - file->pos;
+ memcpy(file->buf + file->pos, buf, count);
+
+ retval = ext2fs_inline_data_set(fs, file->ino, &file->inode,
+ file->buf, count);
+ if (retval == EXT2_ET_INLINE_DATA_NO_SPACE)
+ goto expand;
+ if (retval)
+ return retval;
+
+ file->pos += count;
+
+ /* Update inode size */
+ if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
+ errcode_t rc;
+
+ rc = ext2fs_file_set_size2(file, file->pos);
+ if (retval == 0)
+ retval = rc;
+ }
+
+ if (written)
+ *written = count;
+ return 0;
+ }
+
+expand:
+ retval = ext2fs_inline_data_expand(fs, file->ino);
+ if (retval)
+ return retval;
+ /*
+ * reload inode and return no space error
+ *
+ * XXX: file->inode could be copied from the outside
+ * in ext2fs_file_open2(). We have no way to modify
+ * the outside inode.
+ */
+ retval = ext2fs_read_inode(fs, file->ino, &file->inode);
+ if (retval)
+ return retval;
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+}
+
+
+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;
+ block_entry_t new_block = NULL, old_block = NULL;
+ int bmap_flags = 0;
+
+ EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+ fs = file->fs;
+
+ if (!(file->flags & EXT2_FILE_WRITE))
+ return EXT2_ET_FILE_RO;
+
+ /* If an inode has inline data, things get complicated. */
+ if (file->inode.i_flags & EXT4_INLINE_DATA_FL) {
+ retval = ext2fs_file_write_inline_data(file, buf, nbytes,
+ written);
+ if (retval != EXT2_ET_INLINE_DATA_NO_SPACE)
+ return retval;
+ /* fall through to read data from the block */
+ retval = 0;
+ }
+
+ 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);
+
+ /*
+ * OK, the physical block hasn't been allocated yet.
+ * Allocate it.
+ */
+ if (!file->physblock) {
+ bmap_flags = (file->ino ? BMAP_ALLOC : 0);
+ if (fs->flags & EXT2_FLAG_SHARE_DUP) {
+ new_block = calloc(1, sizeof(*new_block));
+ if (!new_block) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto fail;
+ }
+ ext2fs_sha512((const unsigned char*)file->buf,
+ fs->blocksize, new_block->sha);
+ old_block = ext2fs_hashmap_lookup(
+ fs->block_sha_map,
+ new_block->sha,
+ sizeof(new_block->sha));
+ }
+
+ if (old_block) {
+ file->physblock = old_block->physblock;
+ bmap_flags |= BMAP_SET;
+ free(new_block);
+ new_block = NULL;
+ }
+
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode,
+ BMAP_BUFFER,
+ bmap_flags,
+ file->blockno, 0,
+ &file->physblock);
+ if (retval) {
+ free(new_block);
+ new_block = NULL;
+ goto fail;
+ }
+
+ if (new_block) {
+ new_block->physblock = file->physblock;
+ int ret = ext2fs_hashmap_add(fs->block_sha_map,
+ new_block, new_block->sha,
+ sizeof(new_block->sha));
+ if (ret) {
+ retval = EXT2_ET_NO_MEMORY;
+ free(new_block);
+ goto fail;
+ }
+ }
+
+ if (bmap_flags & BMAP_SET) {
+ ext2fs_iblk_add_blocks(fs, &file->inode, 1);
+ ext2fs_write_inode(fs, file->ino, &file->inode);
+ }
+ }
+
+ file->pos += c;
+ ptr += c;
+ count += c;
+ nbytes -= c;
+ }
+
+fail:
+ /* Update inode size */
+ if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
+ errcode_t rc;
+
+ rc = ext2fs_file_set_size2(file, file->pos);
+ if (retval == 0)
+ retval = rc;
+ }
+
+ 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;
+}
+
+/* Zero the parts of the last block that are past EOF. */
+static errcode_t ext2fs_file_zero_past_offset(ext2_file_t file,
+ ext2_off64_t offset)
+{
+ ext2_filsys fs = file->fs;
+ char *b = NULL;
+ ext2_off64_t off = offset % fs->blocksize;
+ blk64_t blk;
+ int ret_flags;
+ errcode_t retval;
+
+ if (off == 0)
+ return 0;
+
+ retval = sync_buffer_position(file);
+ if (retval)
+ return retval;
+
+ /* Is there an initialized block at the end? */
+ retval = ext2fs_bmap2(fs, file->ino, NULL, NULL, 0,
+ offset / fs->blocksize, &ret_flags, &blk);
+ if (retval)
+ return retval;
+ if ((blk == 0) || (ret_flags & BMAP_RET_UNINIT))
+ return 0;
+
+ /* Zero to the end of the block */
+ retval = ext2fs_get_mem(fs->blocksize, &b);
+ if (retval)
+ return retval;
+
+ /* Read/zero/write block */
+ retval = io_channel_read_blk64(fs->io, blk, 1, b);
+ if (retval)
+ goto out;
+
+ memset(b + off, 0, fs->blocksize - off);
+
+ retval = io_channel_write_blk64(fs->io, blk, 1, b);
+ if (retval)
+ goto out;
+
+out:
+ ext2fs_free_mem(&b);
+ return retval;
+}
+
+/*
+ * 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);
+
+ if (size && ext2fs_file_block_offset_too_big(file->fs, &file->inode,
+ (size - 1) / file->fs->blocksize))
+ return EXT2_ET_FILE_TOO_BIG;
+ truncate_block = ((size + file->fs->blocksize - 1) >>
+ EXT2_BLOCK_SIZE_BITS(file->fs->super));
+ old_size = EXT2_I_SIZE(&file->inode);
+ old_truncate = ((old_size + file->fs->blocksize - 1) >>
+ EXT2_BLOCK_SIZE_BITS(file->fs->super));
+
+ retval = ext2fs_inode_size_set(file->fs, &file->inode, size);
+ if (retval)
+ return retval;
+
+ if (file->ino) {
+ retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
+ if (retval)
+ return retval;
+ }
+
+ retval = ext2fs_file_zero_past_offset(file, size);
+ 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/ext2fs/finddev.c b/lib/ext2fs/finddev.c
new file mode 100644
index 0000000..cd85ef5
--- /dev/null
+++ b/lib/ext2fs/finddev.c
@@ -0,0 +1,218 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.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 (ext2fsP_is_disk_device(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;
+ int level = 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;
+ /* Avoid infinite loop */
+ if (++level >= EXT2FS_MAX_NESTED_LINKS)
+ break;
+ }
+ }
+ 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/ext2fs/flushb.c b/lib/ext2fs/flushb.c
new file mode 100644
index 0000000..bb7daf4
--- /dev/null
+++ b/lib/ext2fs/flushb.c
@@ -0,0 +1,88 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/param.h>
+#include <sys/mount.h> /* 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 defined(HAVE_FSYNC)
+ if (fsync (fd) == -1)
+ return errno;
+#endif
+
+ if (flushb) {
+ errcode_t retval = 0;
+
+#ifdef BLKFLSBUF
+ if (ioctl (fd, BLKFLSBUF, 0) == 0)
+ return 0;
+ retval = errno;
+#elif defined(__linux__)
+#warning BLKFLSBUF not defined
+#endif
+#ifdef FDFLUSH
+ /* In case this is a floppy */
+ if (ioctl(fd, FDFLUSH, 0) == 0)
+ return 0;
+ if (retval == 0)
+ retval = errno;
+#elif defined(__linux__)
+#warning FDFLUSH not defined
+#endif
+ return retval;
+ }
+ return 0;
+}
diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c
new file mode 100644
index 0000000..68b8e9a
--- /dev/null
+++ b/lib/ext2fs/freefs.c
@@ -0,0 +1,108 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "hashmap.h"
+
+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->image_header)
+ ext2fs_free_mem(&fs->image_header);
+
+ 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);
+
+ if (fs->mmp_buf)
+ ext2fs_free_mem(&fs->mmp_buf);
+ if (fs->mmp_cmp)
+ ext2fs_free_mem(&fs->mmp_cmp);
+
+ if (fs->block_sha_map)
+ ext2fs_hashmap_free(fs->block_sha_map);
+
+ fs->magic = 0;
+
+ ext2fs_zero_blocks2(NULL, 0, 0, NULL, NULL);
+ ext2fs_free_mem(&fs);
+}
+
+/*
+ * 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/ext2fs/gen_bitmap.c b/lib/ext2fs/gen_bitmap.c
new file mode 100644
index 0000000..1536d4b
--- /dev/null
+++ b/lib/ext2fs/gen_bitmap.c
@@ -0,0 +1,650 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+struct ext2fs_struct_generic_bitmap_32 {
+ errcode_t magic;
+ ext2_filsys fs;
+ __u32 start, end;
+ __u32 real_end;
+ char * description;
+ char * bitmap;
+ errcode_t base_error_code;
+ __u32 reserved[7];
+};
+
+typedef struct ext2fs_struct_generic_bitmap_32 *ext2fs_generic_bitmap_32;
+
+#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 gen_bitmap,
+ int code, unsigned long arg)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+
+#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_32 bitmap;
+ errcode_t retval;
+ size_t size;
+
+ retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap_32),
+ &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 = (ext2fs_generic_bitmap) 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 gen_src,
+ ext2fs_generic_bitmap *dest)
+{
+ ext2fs_generic_bitmap_32 src = (ext2fs_generic_bitmap_32) gen_src;
+
+ 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 gen_bitmap)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+
+ if (check_magic(gen_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)
+{
+ ext2fs_generic_bitmap_32 bitmap32 = (ext2fs_generic_bitmap_32) bitmap;
+
+ 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);
+#endif
+ return 0;
+ }
+
+ if ((bitno < bitmap32->start) || (bitno > bitmap32->end)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno);
+ return 0;
+ }
+ return ext2fs_test_bit(bitno - bitmap32->start, bitmap32->bitmap);
+}
+
+int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ __u32 bitno)
+{
+ ext2fs_generic_bitmap_32 bitmap32 = (ext2fs_generic_bitmap_32) bitmap;
+
+ 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);
+#endif
+ return 0;
+ }
+
+ if ((bitno < bitmap32->start) || (bitno > bitmap32->end)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno);
+ return 0;
+ }
+ return ext2fs_set_bit(bitno - bitmap32->start, bitmap32->bitmap);
+}
+
+int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+ blk_t bitno)
+{
+ ext2fs_generic_bitmap_32 bitmap32 = (ext2fs_generic_bitmap_32) bitmap;
+
+ 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);
+#endif
+ return 0;
+ }
+
+ if ((bitno < bitmap32->start) || (bitno > bitmap32->end)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno);
+ return 0;
+ }
+ return ext2fs_clear_bit(bitno - bitmap32->start, bitmap32->bitmap);
+}
+
+__u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap)
+{
+ ext2fs_generic_bitmap_32 bitmap32 = (ext2fs_generic_bitmap_32) 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");
+#endif
+ return 0;
+ }
+
+ return bitmap32->start;
+}
+
+__u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap)
+{
+ ext2fs_generic_bitmap_32 bitmap32 = (ext2fs_generic_bitmap_32) 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");
+#endif
+ return 0;
+ }
+ return bitmap32->end;
+}
+
+void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap)
+{
+ ext2fs_generic_bitmap_32 bitmap32 = (ext2fs_generic_bitmap_32) 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");
+#endif
+ return;
+ }
+
+ memset(bitmap32->bitmap, 0,
+ (size_t) (((bitmap32->real_end - bitmap32->start) / 8) + 1));
+}
+
+errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap gen_bitmap,
+ errcode_t magic, errcode_t neq,
+ ext2_ino_t end, ext2_ino_t *oend)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+
+ 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 gen_bmap)
+{
+ ext2fs_generic_bitmap_32 bmap = (ext2fs_generic_bitmap_32) gen_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 gen_bm1,
+ ext2fs_generic_bitmap gen_bm2)
+{
+ ext2fs_generic_bitmap_32 bm1 = (ext2fs_generic_bitmap_32) gen_bm1;
+ ext2fs_generic_bitmap_32 bm2 = (ext2fs_generic_bitmap_32) gen_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(gen_bm1, i) !=
+ ext2fs_fast_test_block_bitmap(gen_bm2, i))
+ return neq;
+
+ return 0;
+}
+
+void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap gen_map)
+{
+ ext2fs_generic_bitmap_32 map = (ext2fs_generic_bitmap_32) gen_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 gen_bmap,
+ errcode_t magic,
+ __u32 start, __u32 num,
+ void *out)
+{
+ ext2fs_generic_bitmap_32 bmap = (ext2fs_generic_bitmap_32) gen_bmap;
+
+ 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 - bmap->start) >> 3), (num+7) >> 3);
+ return 0;
+}
+
+errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap gen_bmap,
+ errcode_t magic,
+ __u32 start, __u32 num,
+ void *in)
+{
+ ext2fs_generic_bitmap_32 bmap = (ext2fs_generic_bitmap_32) gen_bmap;
+
+ 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 - bmap->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 gen_bitmap,
+ unsigned int start,
+ unsigned int len)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+ 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);
+}
+
+errcode_t ext2fs_find_first_zero_generic_bitmap(ext2fs_generic_bitmap gen_bitmap,
+ __u32 start, __u32 end,
+ __u32 *out)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+ blk_t b;
+
+ if (start < bitmap->start || end > bitmap->end || start > end) {
+ ext2fs_warn_bitmap2(gen_bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ while (start <= end) {
+ b = ext2fs_test_bit(start - bitmap->start, bitmap->bitmap);
+ if (!b) {
+ *out = start;
+ return 0;
+ }
+ start++;
+ }
+
+ return ENOENT;
+}
+
+errcode_t ext2fs_find_first_set_generic_bitmap(ext2fs_generic_bitmap gen_bitmap,
+ __u32 start, __u32 end,
+ __u32 *out)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+ blk_t b;
+
+ if (start < bitmap->start || end > bitmap->end || start > end) {
+ ext2fs_warn_bitmap2(gen_bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ while (start <= end) {
+ b = ext2fs_test_bit(start - bitmap->start, bitmap->bitmap);
+ if (b) {
+ *out = start;
+ return 0;
+ }
+ start++;
+ }
+
+ return ENOENT;
+}
+
+int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap gen_bitmap,
+ blk_t block, int num)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+
+ EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+ if ((block < bitmap->start) || (block > bitmap->real_end) ||
+ (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 gen_bitmap,
+ ext2_ino_t inode, int num)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+
+ EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP);
+ if ((inode < bitmap->start) || (inode > bitmap->real_end) ||
+ (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 gen_bitmap,
+ blk_t block, int num)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+ int i;
+
+ if ((block < bitmap->start) || (block > bitmap->end) ||
+ (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 gen_bitmap,
+ blk_t block, int num)
+{
+ ext2fs_generic_bitmap_32 bitmap = (ext2fs_generic_bitmap_32) gen_bitmap;
+ int i;
+
+ if ((block < bitmap->start) || (block > bitmap->end) ||
+ (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/ext2fs/gen_bitmap64.c b/lib/ext2fs/gen_bitmap64.c
new file mode 100644
index 0000000..4289e81
--- /dev/null
+++ b/lib/ext2fs/gen_bitmap64.c
@@ -0,0 +1,981 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#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_64 bitmap,
+ int code, __u64 arg)
+{
+#ifndef OMIT_COM_ERR
+ if (bitmap->description)
+ com_err(0, bitmap->base_error_code+code,
+ "#%llu for %s", (unsigned long long) arg,
+ bitmap->description);
+ else
+ com_err(0, bitmap->base_error_code + code, "#%llu",
+ (unsigned long long) arg);
+#endif
+}
+
+#ifdef ENABLE_BMAP_STATS_OPS
+#define INC_STAT(map, name) map->stats.name
+#else
+#define INC_STAT(map, name) ;;
+#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_64 bitmap;
+ struct ext2_bitmap_ops *ops;
+ ext2_ino_t num_dirs;
+ errcode_t retval;
+
+ if (!type)
+ type = EXT2FS_BMAP64_BITARRAY;
+
+ switch (type) {
+ case EXT2FS_BMAP64_BITARRAY:
+ ops = &ext2fs_blkmap64_bitarray;
+ break;
+ case EXT2FS_BMAP64_RBTREE:
+ ops = &ext2fs_blkmap64_rbtree;
+ break;
+ case EXT2FS_BMAP64_AUTODIR:
+ retval = ext2fs_get_num_dirs(fs, &num_dirs);
+ if (retval || num_dirs > (fs->super->s_inodes_count / 320))
+ ops = &ext2fs_blkmap64_bitarray;
+ else
+ ops = &ext2fs_blkmap64_rbtree;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ retval = ext2fs_get_memzero(sizeof(struct ext2fs_struct_generic_bitmap_64),
+ &bitmap);
+ if (retval)
+ return retval;
+
+#ifdef ENABLE_BMAP_STATS
+ if (gettimeofday(&bitmap->stats.created,
+ (struct timezone *) NULL) == -1) {
+ perror("gettimeofday");
+ ext2fs_free_mem(&bitmap);
+ return 1;
+ }
+ bitmap->stats.type = type;
+#endif
+
+ /* 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;
+ bitmap->cluster_bits = 0;
+ 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;
+ bitmap->cluster_bits = fs->cluster_ratio_bits;
+ 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 = (ext2fs_generic_bitmap) bitmap;
+ return 0;
+}
+
+#ifdef ENABLE_BMAP_STATS
+static void ext2fs_print_bmap_statistics(ext2fs_generic_bitmap_64 bitmap)
+{
+ struct ext2_bmap_statistics *stats = &bitmap->stats;
+#ifdef ENABLE_BMAP_STATS_OPS
+ float mark_seq_perc = 0.0, test_seq_perc = 0.0;
+ float mark_back_perc = 0.0, test_back_perc = 0.0;
+ struct timeval now;
+ double inuse;
+
+ if (stats->test_count) {
+ test_seq_perc = ((float)stats->test_seq /
+ stats->test_count) * 100;
+ test_back_perc = ((float)stats->test_back /
+ stats->test_count) * 100;
+ }
+
+ if (stats->mark_count) {
+ mark_seq_perc = ((float)stats->mark_seq /
+ stats->mark_count) * 100;
+ mark_back_perc = ((float)stats->mark_back /
+ stats->mark_count) * 100;
+ }
+
+ if (gettimeofday(&now, (struct timezone *) NULL) == -1) {
+ perror("gettimeofday");
+ return;
+ }
+
+ inuse = (double) now.tv_sec + \
+ (((double) now.tv_usec) * 0.000001);
+ inuse -= (double) stats->created.tv_sec + \
+ (((double) stats->created.tv_usec) * 0.000001);
+#endif /* ENABLE_BMAP_STATS_OPS */
+
+ fprintf(stderr, "\n[+] %s bitmap (type %d)\n", bitmap->description,
+ stats->type);
+ fprintf(stderr, "=================================================\n");
+#ifdef ENABLE_BMAP_STATS_OPS
+ fprintf(stderr, "%16llu bits long\n",
+ bitmap->real_end - bitmap->start);
+ fprintf(stderr, "%16lu copy_bmap\n%16lu resize_bmap\n",
+ stats->copy_count, stats->resize_count);
+ fprintf(stderr, "%16lu mark bmap\n%16lu unmark_bmap\n",
+ stats->mark_count, stats->unmark_count);
+ fprintf(stderr, "%16lu test_bmap\n%16lu mark_bmap_extent\n",
+ stats->test_count, stats->mark_ext_count);
+ fprintf(stderr, "%16lu unmark_bmap_extent\n"
+ "%16lu test_clear_bmap_extent\n",
+ stats->unmark_ext_count, stats->test_ext_count);
+ fprintf(stderr, "%16lu set_bmap_range\n%16lu set_bmap_range\n",
+ stats->set_range_count, stats->get_range_count);
+ fprintf(stderr, "%16lu clear_bmap\n%16lu contiguous bit test (%.2f%%)\n",
+ stats->clear_count, stats->test_seq, test_seq_perc);
+ fprintf(stderr, "%16lu contiguous bit mark (%.2f%%)\n"
+ "%16llu bits tested backwards (%.2f%%)\n",
+ stats->mark_seq, mark_seq_perc,
+ stats->test_back, test_back_perc);
+ fprintf(stderr, "%16llu bits marked backwards (%.2f%%)\n"
+ "%16.2f seconds in use\n",
+ stats->mark_back, mark_back_perc, inuse);
+#endif /* ENABLE_BMAP_STATS_OPS */
+}
+#endif
+
+void ext2fs_free_generic_bmap(ext2fs_generic_bitmap gen_bmap)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+
+ if (!bmap)
+ return;
+
+ if (EXT2FS_IS_32_BITMAP(bmap)) {
+ ext2fs_free_generic_bitmap(gen_bmap);
+ return;
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bmap))
+ return;
+
+#ifdef ENABLE_BMAP_STATS
+ if (getenv("E2FSPROGS_BITMAP_STATS")) {
+ ext2fs_print_bmap_statistics(bmap);
+ bmap->bitmap_ops->print_stats(bmap);
+ }
+#endif
+
+ 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 gen_src,
+ ext2fs_generic_bitmap *dest)
+{
+ ext2fs_generic_bitmap_64 src = (ext2fs_generic_bitmap_64) gen_src;
+ char *descr, *new_descr;
+ ext2fs_generic_bitmap_64 new_bmap;
+ errcode_t retval;
+
+ if (!src)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(src))
+ return ext2fs_copy_generic_bitmap(gen_src, dest);
+
+ if (!EXT2FS_IS_64_BITMAP(src))
+ return EINVAL;
+
+ /* Allocate a new bitmap struct */
+ retval = ext2fs_get_memzero(sizeof(struct ext2fs_struct_generic_bitmap_64),
+ &new_bmap);
+ if (retval)
+ return retval;
+
+
+#ifdef ENABLE_BMAP_STATS_OPS
+ src->stats.copy_count++;
+#endif
+#ifdef ENABLE_BMAP_STATS
+ if (gettimeofday(&new_bmap->stats.created,
+ (struct timezone *) NULL) == -1) {
+ perror("gettimeofday");
+ ext2fs_free_mem(&new_bmap);
+ return 1;
+ }
+ new_bmap->stats.type = src->stats.type;
+#endif
+
+ /* 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;
+ new_bmap->cluster_bits = src->cluster_bits;
+
+ descr = src->description;
+ if (descr) {
+ retval = ext2fs_get_mem(strlen(descr)+10, &new_descr);
+ if (retval) {
+ ext2fs_free_mem(&new_bmap);
+ return retval;
+ }
+ strcpy(new_descr, "copy of ");
+ strcat(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 = (ext2fs_generic_bitmap) new_bmap;
+
+ return 0;
+}
+
+errcode_t ext2fs_resize_generic_bmap(ext2fs_generic_bitmap gen_bmap,
+ __u64 new_end,
+ __u64 new_real_end)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+
+ if (!bmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bmap))
+ return ext2fs_resize_generic_bitmap(gen_bmap->magic, new_end,
+ new_real_end, gen_bmap);
+
+ if (!EXT2FS_IS_64_BITMAP(bmap))
+ return EINVAL;
+
+ INC_STAT(bmap, resize_count);
+
+ return bmap->bitmap_ops->resize_bmap(bmap, new_end, new_real_end);
+}
+
+errcode_t ext2fs_fudge_generic_bmap_end(ext2fs_generic_bitmap gen_bitmap,
+ errcode_t neq,
+ __u64 end, __u64 *oend)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ ext2_ino_t tmp_oend;
+ int retval;
+
+ retval = ext2fs_fudge_generic_bitmap_end(gen_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 gen_bitmap)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap))
+ return ext2fs_get_generic_bitmap_start(gen_bitmap);
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return EINVAL;
+
+ return bitmap->start;
+}
+
+__u64 ext2fs_get_generic_bmap_end(ext2fs_generic_bitmap gen_bitmap)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap))
+ return ext2fs_get_generic_bitmap_end(gen_bitmap);
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return EINVAL;
+
+ return bitmap->end;
+}
+
+void ext2fs_clear_generic_bmap(ext2fs_generic_bitmap gen_bitmap)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap))
+ ext2fs_clear_generic_bitmap(gen_bitmap);
+ else
+ bitmap->bitmap_ops->clear_bmap(bitmap);
+}
+
+int ext2fs_mark_generic_bmap(ext2fs_generic_bitmap gen_bitmap,
+ __u64 arg)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+ if (!bitmap)
+ return 0;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ if (arg & ~0xffffffffULL) {
+ ext2fs_warn_bitmap2(gen_bitmap,
+ EXT2FS_MARK_ERROR, 0xffffffff);
+ return 0;
+ }
+ return ext2fs_mark_generic_bitmap(gen_bitmap, arg);
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return 0;
+
+ arg >>= bitmap->cluster_bits;
+
+#ifdef ENABLE_BMAP_STATS_OPS
+ if (arg == bitmap->stats.last_marked + 1)
+ bitmap->stats.mark_seq++;
+ if (arg < bitmap->stats.last_marked)
+ bitmap->stats.mark_back++;
+ bitmap->stats.last_marked = arg;
+ bitmap->stats.mark_count++;
+#endif
+
+ 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 gen_bitmap,
+ __u64 arg)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+ if (!bitmap)
+ return 0;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ if (arg & ~0xffffffffULL) {
+ ext2fs_warn_bitmap2(gen_bitmap, EXT2FS_UNMARK_ERROR,
+ 0xffffffff);
+ return 0;
+ }
+ return ext2fs_unmark_generic_bitmap(gen_bitmap, arg);
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return 0;
+
+ arg >>= bitmap->cluster_bits;
+
+ INC_STAT(bitmap, unmark_count);
+
+ 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 gen_bitmap,
+ __u64 arg)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+ if (!bitmap)
+ return 0;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ if (arg & ~0xffffffffULL) {
+ ext2fs_warn_bitmap2(gen_bitmap, EXT2FS_TEST_ERROR,
+ 0xffffffff);
+ return 0;
+ }
+ return ext2fs_test_generic_bitmap(gen_bitmap, arg);
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return 0;
+
+ arg >>= bitmap->cluster_bits;
+
+#ifdef ENABLE_BMAP_STATS_OPS
+ bitmap->stats.test_count++;
+ if (arg == bitmap->stats.last_tested + 1)
+ bitmap->stats.test_seq++;
+ if (arg < bitmap->stats.last_tested)
+ bitmap->stats.test_back++;
+ bitmap->stats.last_tested = arg;
+#endif
+
+ 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 gen_bmap,
+ __u64 start, unsigned int num,
+ void *in)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+
+ if (!bmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bmap)) {
+ if ((start+num-1) & ~0xffffffffULL) {
+ ext2fs_warn_bitmap2(gen_bmap, EXT2FS_UNMARK_ERROR,
+ 0xffffffff);
+ return EINVAL;
+ }
+ return ext2fs_set_generic_bitmap_range(gen_bmap, bmap->magic,
+ start, num, in);
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bmap))
+ return EINVAL;
+
+ INC_STAT(bmap, set_range_count);
+
+ return bmap->bitmap_ops->set_bmap_range(bmap, start, num, in);
+}
+
+errcode_t ext2fs_get_generic_bmap_range(ext2fs_generic_bitmap gen_bmap,
+ __u64 start, unsigned int num,
+ void *out)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+
+ if (!bmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bmap)) {
+ if ((start+num-1) & ~0xffffffffULL) {
+ ext2fs_warn_bitmap2(gen_bmap,
+ EXT2FS_UNMARK_ERROR, 0xffffffff);
+ return EINVAL;
+ }
+ return ext2fs_get_generic_bitmap_range(gen_bmap, bmap->magic,
+ start, num, out);
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bmap))
+ return EINVAL;
+
+ INC_STAT(bmap, get_range_count);
+
+ return bmap->bitmap_ops->get_bmap_range(bmap, start, num, out);
+}
+
+errcode_t ext2fs_compare_generic_bmap(errcode_t neq,
+ ext2fs_generic_bitmap gen_bm1,
+ ext2fs_generic_bitmap gen_bm2)
+{
+ ext2fs_generic_bitmap_64 bm1 = (ext2fs_generic_bitmap_64) gen_bm1;
+ ext2fs_generic_bitmap_64 bm2 = (ext2fs_generic_bitmap_64) gen_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,
+ gen_bm1, gen_bm2);
+
+ if (!EXT2FS_IS_64_BITMAP(bm1))
+ return EINVAL;
+
+ if ((bm1->start != bm2->start) ||
+ (bm1->end != bm2->end))
+ return neq;
+
+ for (i = bm1->start; i < bm1->end; i++) {
+ int ret1, ret2;
+ ret1 = !!ext2fs_test_generic_bmap(gen_bm1, i);
+ ret2 = !!ext2fs_test_generic_bmap(gen_bm2, i);
+ if (ret1 != ret2) {
+ return neq;
+ }
+ }
+
+ return 0;
+}
+
+void ext2fs_set_generic_bmap_padding(ext2fs_generic_bitmap gen_bmap)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+ __u64 start, num;
+
+ if (EXT2FS_IS_32_BITMAP(bmap)) {
+ ext2fs_set_generic_bitmap_padding(gen_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 gen_bmap,
+ blk64_t block, unsigned int num)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+ __u64 end = block + 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 & ~0xffffffffULL) ||
+ ((block+num-1) & ~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;
+
+ INC_STAT(bmap, test_ext_count);
+
+ /* convert to clusters if necessary */
+ block >>= bmap->cluster_bits;
+ end += (1ULL << bmap->cluster_bits) - 1;
+ end >>= bmap->cluster_bits;
+ num = end - block;
+
+ if ((block < bmap->start) || (block > bmap->end) ||
+ (block+num-1 > bmap->end)) {
+ ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, block,
+ bmap->description);
+ return EINVAL;
+ }
+
+ return bmap->bitmap_ops->test_clear_bmap_extent(bmap, block, num);
+}
+
+void ext2fs_mark_block_bitmap_range2(ext2fs_block_bitmap gen_bmap,
+ blk64_t block, unsigned int num)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+ __u64 end = block + num;
+
+ if (!bmap)
+ return;
+
+ if (EXT2FS_IS_32_BITMAP(bmap)) {
+ if ((block & ~0xffffffffULL) ||
+ ((block+num-1) & ~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;
+
+ INC_STAT(bmap, mark_ext_count);
+
+ /* convert to clusters if necessary */
+ block >>= bmap->cluster_bits;
+ end += (1ULL << bmap->cluster_bits) - 1;
+ end >>= bmap->cluster_bits;
+ num = end - block;
+
+ if ((block < bmap->start) || (block > bmap->end) ||
+ (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 gen_bmap,
+ blk64_t block, unsigned int num)
+{
+ ext2fs_generic_bitmap_64 bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+ __u64 end = block + num;
+
+ if (!bmap)
+ return;
+
+ if (EXT2FS_IS_32_BITMAP(bmap)) {
+ if ((block & ~0xffffffffULL) ||
+ ((block+num-1) & ~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;
+
+ INC_STAT(bmap, unmark_ext_count);
+
+ /* convert to clusters if necessary */
+ block >>= bmap->cluster_bits;
+ end += (1ULL << bmap->cluster_bits) - 1;
+ end >>= bmap->cluster_bits;
+ num = end - block;
+
+ if ((block < bmap->start) || (block > bmap->end) ||
+ (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);
+}
+
+void ext2fs_warn_bitmap32(ext2fs_generic_bitmap gen_bitmap, const char *func)
+{
+ ext2fs_generic_bitmap_64 bitmap = (ext2fs_generic_bitmap_64) gen_bitmap;
+
+#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
+}
+
+errcode_t ext2fs_convert_subcluster_bitmap(ext2_filsys fs,
+ ext2fs_block_bitmap *bitmap)
+{
+ ext2fs_generic_bitmap_64 bmap, cmap;
+ ext2fs_block_bitmap gen_bmap = *bitmap, gen_cmap;
+ errcode_t retval;
+ blk64_t i, next, b_end, c_end;
+
+ bmap = (ext2fs_generic_bitmap_64) gen_bmap;
+ if (fs->cluster_ratio_bits == ext2fs_get_bitmap_granularity(gen_bmap))
+ return 0; /* Nothing to do */
+
+ retval = ext2fs_allocate_block_bitmap(fs, "converted cluster bitmap",
+ &gen_cmap);
+ if (retval)
+ return retval;
+
+ cmap = (ext2fs_generic_bitmap_64) gen_cmap;
+ i = bmap->start;
+ b_end = bmap->end;
+ bmap->end = bmap->real_end;
+ c_end = cmap->end;
+ cmap->end = cmap->real_end;
+ while (i < bmap->real_end) {
+ retval = ext2fs_find_first_set_block_bitmap2(gen_bmap,
+ i, bmap->real_end, &next);
+ if (retval)
+ break;
+ ext2fs_mark_block_bitmap2(gen_cmap, next);
+ i = EXT2FS_C2B(fs, EXT2FS_B2C(fs, next) + 1);
+ }
+ bmap->end = b_end;
+ cmap->end = c_end;
+ ext2fs_free_block_bitmap(gen_bmap);
+ *bitmap = (ext2fs_block_bitmap) cmap;
+ return 0;
+}
+
+errcode_t ext2fs_find_first_zero_generic_bmap(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ ext2fs_generic_bitmap_64 bmap64 = (ext2fs_generic_bitmap_64) bitmap;
+ __u64 cstart, cend, cout;
+ errcode_t retval;
+
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ blk_t blk = 0;
+
+ if (((start) & ~0xffffffffULL) ||
+ ((end) & ~0xffffffffULL)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ retval = ext2fs_find_first_zero_generic_bitmap(bitmap, start,
+ end, &blk);
+ if (retval == 0)
+ *out = blk;
+ return retval;
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return EINVAL;
+
+ cstart = start >> bmap64->cluster_bits;
+ cend = end >> bmap64->cluster_bits;
+
+ if (cstart < bmap64->start || cend > bmap64->end || start > end) {
+ warn_bitmap(bmap64, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ if (bmap64->bitmap_ops->find_first_zero) {
+ retval = bmap64->bitmap_ops->find_first_zero(bmap64, cstart,
+ cend, &cout);
+ if (retval)
+ return retval;
+ found:
+ cout <<= bmap64->cluster_bits;
+ *out = (cout >= start) ? cout : start;
+ return 0;
+ }
+
+ for (cout = cstart; cout <= cend; cout++)
+ if (!bmap64->bitmap_ops->test_bmap(bmap64, cout))
+ goto found;
+
+ return ENOENT;
+}
+
+errcode_t ext2fs_find_first_set_generic_bmap(ext2fs_generic_bitmap bitmap,
+ __u64 start, __u64 end, __u64 *out)
+{
+ ext2fs_generic_bitmap_64 bmap64 = (ext2fs_generic_bitmap_64) bitmap;
+ __u64 cstart, cend, cout;
+ errcode_t retval;
+
+ if (!bitmap)
+ return EINVAL;
+
+ if (EXT2FS_IS_32_BITMAP(bitmap)) {
+ blk_t blk = 0;
+
+ if (((start) & ~0xffffffffULL) ||
+ ((end) & ~0xffffffffULL)) {
+ ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ retval = ext2fs_find_first_set_generic_bitmap(bitmap, start,
+ end, &blk);
+ if (retval == 0)
+ *out = blk;
+ return retval;
+ }
+
+ if (!EXT2FS_IS_64_BITMAP(bitmap))
+ return EINVAL;
+
+ cstart = start >> bmap64->cluster_bits;
+ cend = end >> bmap64->cluster_bits;
+
+ if (cstart < bmap64->start || cend > bmap64->end || start > end) {
+ warn_bitmap(bmap64, EXT2FS_TEST_ERROR, start);
+ return EINVAL;
+ }
+
+ if (bmap64->bitmap_ops->find_first_set) {
+ retval = bmap64->bitmap_ops->find_first_set(bmap64, cstart,
+ cend, &cout);
+ if (retval)
+ return retval;
+ found:
+ cout <<= bmap64->cluster_bits;
+ *out = (cout >= start) ? cout : start;
+ return 0;
+ }
+
+ for (cout = cstart; cout <= cend; cout++)
+ if (bmap64->bitmap_ops->test_bmap(bmap64, cout))
+ goto found;
+
+ return ENOENT;
+}
+
+errcode_t ext2fs_count_used_clusters(ext2_filsys fs, blk64_t start,
+ blk64_t end, blk64_t *out)
+{
+ blk64_t next;
+ blk64_t tot_set = 0;
+ errcode_t retval = 0;
+
+ while (start < end) {
+ retval = ext2fs_find_first_set_block_bitmap2(fs->block_map,
+ start, end, &next);
+ if (retval) {
+ if (retval == ENOENT)
+ retval = 0;
+ break;
+ }
+ start = next;
+
+ retval = ext2fs_find_first_zero_block_bitmap2(fs->block_map,
+ start, end, &next);
+ if (retval == 0) {
+ tot_set += next - start;
+ start = next + 1;
+ } else if (retval == ENOENT) {
+ retval = 0;
+ tot_set += end - start + 1;
+ break;
+ } else
+ break;
+ }
+
+ if (!retval)
+ *out = EXT2FS_NUM_B2C(fs, tot_set);
+ return retval;
+}
diff --git a/lib/ext2fs/gen_crc32ctable.c b/lib/ext2fs/gen_crc32ctable.c
new file mode 100644
index 0000000..a0742ee
--- /dev/null
+++ b/lib/ext2fs/gen_crc32ctable.c
@@ -0,0 +1,117 @@
+#include <stdio.h>
+#include "crc32c_defs.h"
+#include <inttypes.h>
+
+#define ENTRIES_PER_LINE 4
+
+#if CRC_LE_BITS > 8
+# define LE_TABLE_ROWS (CRC_LE_BITS/8)
+# define LE_TABLE_SIZE 256
+#else
+# define LE_TABLE_ROWS 1
+# define LE_TABLE_SIZE (1 << CRC_LE_BITS)
+#endif
+
+#if CRC_BE_BITS > 8
+# define BE_TABLE_ROWS (CRC_BE_BITS/8)
+# define BE_TABLE_SIZE 256
+#else
+# define BE_TABLE_ROWS 1
+# define BE_TABLE_SIZE (1 << CRC_BE_BITS)
+#endif
+
+static uint32_t crc32table_be[BE_TABLE_ROWS][256];
+static uint32_t crc32ctable_le[LE_TABLE_ROWS][256];
+
+/**
+ * crc32init_le() - allocate and initialize LE table data
+ *
+ * crc is the crc of the byte i; other entries are filled in based on the
+ * fact that crctable[i^j] = crctable[i] ^ crctable[j].
+ *
+ */
+static void crc32cinit_le(void)
+{
+ unsigned i, j;
+ uint32_t crc = 1;
+
+ crc32ctable_le[0][0] = 0;
+
+ for (i = LE_TABLE_SIZE >> 1; i; i >>= 1) {
+ crc = (crc >> 1) ^ ((crc & 1) ? CRC32C_POLY_LE : 0);
+ for (j = 0; j < LE_TABLE_SIZE; j += 2 * i)
+ crc32ctable_le[0][i + j] = crc ^ crc32ctable_le[0][j];
+ }
+ for (i = 0; i < LE_TABLE_SIZE; i++) {
+ crc = crc32ctable_le[0][i];
+ for (j = 1; j < LE_TABLE_ROWS; j++) {
+ crc = crc32ctable_le[0][crc & 0xff] ^ (crc >> 8);
+ crc32ctable_le[j][i] = crc;
+ }
+ }
+}
+
+/**
+ * crc32init_be() - allocate and initialize BE table data
+ */
+static void crc32init_be(void)
+{
+ unsigned i, j;
+ uint32_t crc = 0x80000000;
+
+ crc32table_be[0][0] = 0;
+
+ for (i = 1; i < BE_TABLE_SIZE; i <<= 1) {
+ crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+ for (j = 0; j < i; j++)
+ crc32table_be[0][i + j] = crc ^ crc32table_be[0][j];
+ }
+ for (i = 0; i < BE_TABLE_SIZE; i++) {
+ crc = crc32table_be[0][i];
+ for (j = 1; j < BE_TABLE_ROWS; j++) {
+ crc = crc32table_be[0][(crc >> 24) & 0xff] ^ (crc << 8);
+ crc32table_be[j][i] = crc;
+ }
+ }
+}
+
+static void output_table(uint32_t (*table)[256], int rows, int len, char *trans)
+{
+ int i, j;
+
+ for (j = 0 ; j < rows; j++) {
+ printf("{");
+ for (i = 0; i < len - 1; i++) {
+ if (i % ENTRIES_PER_LINE == 0)
+ printf("\n");
+ printf("%s(0x%8.8xL), ", trans, table[j][i]);
+ }
+ printf("%s(0x%8.8xL)},\n", trans, table[j][len - 1]);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ printf("/* this file is generated - do not edit */\n\n");
+
+ if (CRC_BE_BITS > 1) {
+ crc32init_be();
+ printf("static const uint32_t "
+ "crc32table_be[%d][%d] = {",
+ BE_TABLE_ROWS, BE_TABLE_SIZE);
+ output_table(crc32table_be, LE_TABLE_ROWS,
+ BE_TABLE_SIZE, "tobe");
+ printf("};\n");
+ }
+ if (CRC_LE_BITS > 1) {
+ crc32cinit_le();
+ printf("static const uint32_t "
+ "crc32ctable_le[%d][%d] = {",
+ LE_TABLE_ROWS, LE_TABLE_SIZE);
+ output_table(crc32ctable_le, LE_TABLE_ROWS,
+ LE_TABLE_SIZE, "tole");
+ printf("};\n");
+ }
+
+ return 0;
+}
diff --git a/lib/ext2fs/get_num_dirs.c b/lib/ext2fs/get_num_dirs.c
new file mode 100644
index 0000000..f5644f8
--- /dev/null
+++ b/lib/ext2fs/get_num_dirs.c
@@ -0,0 +1,50 @@
+/*
+ * get_num_dirs.c -- calculate number of directories
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * 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;
+}
+
diff --git a/lib/ext2fs/get_pathname.c b/lib/ext2fs/get_pathname.c
new file mode 100644
index 0000000..8cfaf6e
--- /dev/null
+++ b/lib/ext2fs/get_pathname.c
@@ -0,0 +1,171 @@
+/*
+ * get_pathname.c --- do directory/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 <name>. <dir> is the containing
+ * directory inode, and <ino> is the inode number itself. If
+ * <ino> is zero, then ext2fs_get_pathname will return pathname
+ * of the the directory <dir>.
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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;
+ int name_len = ext2fs_dirent_name_len(dirent);
+
+ gp = (struct get_pathname_struct *) priv_data;
+
+ if ((name_len == 2) && !strncmp(dirent->name, "..", 2))
+ gp->parent = dirent->inode;
+ if (dirent->inode == gp->search_ino) {
+ retval = ext2fs_get_mem(name_len + 1, &gp->name);
+ if (retval) {
+ gp->errcode = retval;
+ return DIRENT_ABORT;
+ }
+ strncpy(gp->name, dirent->name, name_len);
+ gp->name[name_len] = '\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 = 0, *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 == EXT2_ET_NO_DIRECTORY) {
+ char tmp[32];
+
+ if (ino)
+ snprintf(tmp, sizeof(tmp), "<%u>/<%u>", dir, ino);
+ else
+ snprintf(tmp, sizeof(tmp), "<%u>", dir);
+ retval = ext2fs_get_mem(strlen(tmp)+1, name);
+ if (retval)
+ goto cleanup;
+ strcpy(*name, tmp);
+ return 0;
+ } else 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;
+ retval = 0;
+
+cleanup:
+ ext2fs_free_mem(&parent_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/ext2fs/getsectsize.c b/lib/ext2fs/getsectsize.c
new file mode 100644
index 0000000..bd978c5
--- /dev/null
+++ b/lib/ext2fs/getsectsize.c
@@ -0,0 +1,151 @@
+/*
+ * 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%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_DISK_H
+#include <sys/disk.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+#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)
+{
+#ifdef _WIN32
+ *sectsize = 512; // just guessing
+ return 0;
+#else // not _WIN32
+
+ int fd;
+
+ fd = ext2fs_open_file(file, O_RDONLY, 0);
+ if (fd < 0)
+ return errno;
+
+#ifdef BLKSSZGET
+ if (ioctl(fd, BLKSSZGET, sectsize) >= 0) {
+ close(fd);
+ return 0;
+ }
+#endif
+#ifdef DIOCGSECTORSIZE
+ if (ioctl(fd, DIOCGSECTORSIZE, sectsize) >= 0) {
+ close(fd);
+ return 0;
+ }
+#endif
+ *sectsize = 0;
+ close(fd);
+ return 0;
+
+#endif // ifdef _WIN32
+}
+
+/*
+ * Return desired alignment for direct I/O
+ */
+int ext2fs_get_dio_alignment(int fd)
+{
+ int align = 0;
+
+#ifdef BLKSSZGET
+ if (ioctl(fd, BLKSSZGET, &align) < 0)
+ align = 0;
+#endif
+#ifdef DIOCGSECTORSIZE
+ if (align <= 0 &&
+ ioctl(fd, DIOCGSECTORSIZE, &align) < 0)
+ align = 0;
+#endif
+
+#ifdef _SC_PAGESIZE
+ if (align <= 0)
+ align = sysconf(_SC_PAGESIZE);
+#endif
+#ifdef HAVE_GETPAGESIZE
+ if (align <= 0)
+ align = getpagesize();
+#endif
+ if (align <= 0)
+ align = 4096;
+
+ return align;
+}
+
+/*
+ * Returns the physical sector size of a device
+ */
+errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize)
+{
+#ifdef _WIN32
+
+ return ext2fs_get_device_sectsize(file, sectsize);
+
+#else // not _WIN32
+
+ int fd;
+
+ fd = ext2fs_open_file(file, O_RDONLY, 0);
+ if (fd < 0)
+ return errno;
+
+#ifdef BLKPBSZGET
+ if (ioctl(fd, BLKPBSZGET, sectsize) >= 0) {
+ close(fd);
+ return 0;
+ }
+#endif
+#ifdef DIOCGSECTORSIZE
+ /* This isn't really the physical sector size, but FreeBSD
+ * doesn't seem to have this concept. */
+ if (ioctl(fd, DIOCGSECTORSIZE, sectsize) >= 0) {
+ close(fd);
+ return 0;
+ }
+#endif
+ *sectsize = 0;
+ close(fd);
+ return 0;
+
+#endif // ifdef _WIN32
+}
diff --git a/lib/ext2fs/getsize.c b/lib/ext2fs/getsize.c
new file mode 100644
index 0000000..bcf3020
--- /dev/null
+++ b/lib/ext2fs/getsize.c
@@ -0,0 +1,324 @@
+/*
+ * 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%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <ctype.h>
+
+#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
+
+HANDLE windows_get_handle(io_channel channel);
+
+errcode_t ext2fs_get_device_size2(const char *file, int blocksize,
+ blk64_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 */
+
+ io_channel data_io = 0;
+ int retval;
+
+ retval = windows_io_manager->open(file, 0, &data_io);
+ if (retval)
+ return retval;
+
+ dev = windows_get_handle(data_io);
+ 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 */
+
+ windows_io_manager->close(data_io);
+
+ 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;
+ unsigned long long size64;
+ ext2_loff_t high, low;
+
+ fd = ext2fs_open_file(file, O_RDONLY, 0);
+ if (fd < 0)
+ return errno;
+
+#if defined DKIOCGETBLOCKCOUNT && defined DKIOCGETBLOCKSIZE /* For Apple Darwin */
+ unsigned int size;
+
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0 &&
+ ioctl(fd, DKIOCGETBLOCKSIZE, &size) >= 0) {
+ *retblocks = size64 * size / blocksize;
+ goto out;
+ }
+#endif
+
+#ifdef BLKGETSIZE64
+ {
+ int valid_blkgetsize64 = 1;
+#ifdef __linux__
+ struct utsname ut;
+
+ 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 /* BLKGETSIZE64 */
+
+#ifdef BLKGETSIZE
+ {
+ unsigned long size;
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ *retblocks = size / (blocksize / 512);
+ goto out;
+ }
+ }
+#endif
+
+#ifdef FDGETPRM
+ {
+ struct floppy_struct this_floppy;
+
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+ *retblocks = this_floppy.size / (blocksize / 512);
+ goto out;
+ }
+ }
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+ {
+ int part;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+
+#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 */
+
+ {
+ ext2fs_struct_stat st;
+
+ if (ext2fs_fstat(fd, &st) == 0)
+ 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;
+}
+
+#endif /* WIN32 */
+
+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;
+}
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+ blk64_t blocks;
+ int retval;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n", argv[0]);
+ exit(1);
+ }
+
+ retval = ext2fs_get_device_size2(argv[1], 1024, &blocks);
+ if (retval) {
+ com_err(argv[0], retval,
+ "while calling ext2fs_get_device_size");
+ exit(1);
+ }
+ printf("Device %s has %llu 1k blocks.\n", argv[1],
+ (unsigned long long) locks);
+ exit(0);
+}
+#endif
diff --git a/lib/ext2fs/hashmap.c b/lib/ext2fs/hashmap.c
new file mode 100644
index 0000000..697b2bc
--- /dev/null
+++ b/lib/ext2fs/hashmap.c
@@ -0,0 +1,109 @@
+#include "hashmap.h"
+#include <string.h>
+
+struct ext2fs_hashmap {
+ uint32_t size;
+ uint32_t(*hash)(const void *key, size_t len);
+ void(*free)(void*);
+ struct ext2fs_hashmap_entry *first;
+ struct ext2fs_hashmap_entry *last;
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+ struct ext2fs_hashmap_entry *entries[0];
+#if __GNUC_PREREQ (4, 8)
+#pragma GCC diagnostic pop
+#endif
+};
+
+uint32_t ext2fs_djb2_hash(const void *str, size_t size)
+{
+ int c;
+ const char *s = str;
+ uint32_t hash = 5381;
+
+ while (size-- > 0) {
+ c = *s++;
+ hash = ((hash << 5) + hash) + c;
+ }
+ return hash;
+}
+
+struct ext2fs_hashmap *ext2fs_hashmap_create(
+ uint32_t(*hash_fct)(const void*, size_t),
+ void(*free_fct)(void*), size_t size)
+{
+ struct ext2fs_hashmap *h = calloc(sizeof(struct ext2fs_hashmap) +
+ sizeof(struct ext2fs_hashmap_entry) * size, 1);
+ if (!h)
+ return NULL;
+
+ h->size = size;
+ h->free = free_fct;
+ h->hash = hash_fct;
+ h->first = h->last = NULL;
+ return h;
+}
+
+int ext2fs_hashmap_add(struct ext2fs_hashmap *h,
+ void *data, const void *key, size_t key_len)
+{
+ uint32_t hash = h->hash(key, key_len) % h->size;
+ struct ext2fs_hashmap_entry *e = malloc(sizeof(*e));
+
+ if (!e)
+ return -1;
+
+ e->data = data;
+ e->key = key;
+ e->key_len = key_len;
+ e->next = h->entries[hash];
+ h->entries[hash] = e;
+
+ e->list_prev = NULL;
+ e->list_next = h->first;
+ if (h->first)
+ h->first->list_prev = e;
+ h->first = e;
+ if (!h->last)
+ h->last = e;
+
+ return 0;
+}
+
+void *ext2fs_hashmap_lookup(struct ext2fs_hashmap *h, const void *key,
+ size_t key_len)
+{
+ struct ext2fs_hashmap_entry *iter;
+ uint32_t hash = h->hash(key, key_len) % h->size;
+
+ for (iter = h->entries[hash]; iter; iter = iter->next)
+ if (iter->key_len == key_len && !memcmp(iter->key, key, key_len))
+ return iter->data;
+ return NULL;
+}
+
+void *ext2fs_hashmap_iter_in_order(struct ext2fs_hashmap *h,
+ struct ext2fs_hashmap_entry **it)
+{
+ *it = *it ? (*it)->list_next : h->first;
+ return *it ? (*it)->data : NULL;
+}
+
+void ext2fs_hashmap_free(struct ext2fs_hashmap *h)
+{
+ size_t i;
+
+ for (i = 0; i < h->size; ++i) {
+ struct ext2fs_hashmap_entry *it = h->entries[i];
+ while (it) {
+ struct ext2fs_hashmap_entry *tmp = it->next;
+ if (h->free)
+ h->free(it->data);
+ free(it);
+ it = tmp;
+ }
+ }
+ free(h);
+}
diff --git a/lib/ext2fs/hashmap.h b/lib/ext2fs/hashmap.h
new file mode 100644
index 0000000..0c09d2b
--- /dev/null
+++ b/lib/ext2fs/hashmap.h
@@ -0,0 +1,42 @@
+#ifndef HASHMAP_H
+# define HASHMAP_H
+
+# include <stdlib.h>
+# include <stdint.h>
+
+#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
+
+struct ext2fs_hashmap;
+
+struct ext2fs_hashmap_entry {
+ void *data;
+ const void *key;
+ size_t key_len;
+ struct ext2fs_hashmap_entry *next;
+ struct ext2fs_hashmap_entry *list_next;
+ struct ext2fs_hashmap_entry *list_prev;
+};
+
+struct ext2fs_hashmap *ext2fs_hashmap_create(
+ uint32_t(*hash_fct)(const void*, size_t),
+ void(*free_fct)(void*), size_t size);
+int ext2fs_hashmap_add(struct ext2fs_hashmap *h,
+ void *data, const void *key,size_t key_len);
+void *ext2fs_hashmap_lookup(struct ext2fs_hashmap *h, const void *key,
+ size_t key_len);
+void *ext2fs_hashmap_iter_in_order(struct ext2fs_hashmap *h,
+ struct ext2fs_hashmap_entry **it);
+void ext2fs_hashmap_del(struct ext2fs_hashmap *h,
+ struct ext2fs_hashmap_entry *e);
+void ext2fs_hashmap_free(struct ext2fs_hashmap *h);
+
+uint32_t ext2fs_djb2_hash(const void *str, size_t size);
+
+#endif /* !HASHMAP_H */
diff --git a/lib/ext2fs/i_block.c b/lib/ext2fs/i_block.c
new file mode 100644
index 0000000..2eecf02
--- /dev/null
+++ b/lib/ext2fs/i_block.c
@@ -0,0 +1,90 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <errno.h>
+
+#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 (ext2fs_has_feature_huge_file(fs->super))
+ b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
+
+ if (!ext2fs_has_feature_huge_file(fs->super) ||
+ !(inode->i_flags & EXT4_HUGE_FILE_FL))
+ num_blocks *= fs->blocksize / 512;
+ num_blocks *= EXT2FS_CLUSTER_RATIO(fs);
+
+ b += num_blocks;
+
+ if (ext2fs_has_feature_huge_file(fs->super))
+ 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 (ext2fs_has_feature_huge_file(fs->super))
+ b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32;
+
+ if (!ext2fs_has_feature_huge_file(fs->super) ||
+ !(inode->i_flags & EXT4_HUGE_FILE_FL))
+ num_blocks *= fs->blocksize / 512;
+ num_blocks *= EXT2FS_CLUSTER_RATIO(fs);
+
+ if (num_blocks > b)
+ return EOVERFLOW;
+
+ b -= num_blocks;
+
+ if (ext2fs_has_feature_huge_file(fs->super))
+ 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 (!ext2fs_has_feature_huge_file(fs->super) ||
+ !(inode->i_flags & EXT4_HUGE_FILE_FL))
+ b *= fs->blocksize / 512;
+ b *= EXT2FS_CLUSTER_RATIO(fs);
+
+ inode->i_blocks = b & 0xFFFFFFFF;
+ if (ext2fs_has_feature_huge_file(fs->super))
+ inode->osd2.linux2.l_i_blocks_hi = b >> 32;
+ else if (b >> 32)
+ return EOVERFLOW;
+ return 0;
+}
diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
new file mode 100644
index 0000000..888a90b
--- /dev/null
+++ b/lib/ext2fs/icount.c
@@ -0,0 +1,921 @@
+/*
+ * 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%
+ */
+
+#include "config.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#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;
+#ifdef CONFIG_TDB
+ char *tdb_fn;
+ TDB_CONTEXT *tdb;
+#endif
+ __u16 *fullmap;
+};
+
+/*
+ * 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);
+#ifdef CONFIG_TDB
+ if (icount->tdb)
+ tdb_close(icount->tdb);
+ if (icount->tdb_fn) {
+ (void) unlink(icount->tdb_fn);
+ free(icount->tdb_fn);
+ }
+#endif
+
+ if (icount->fullmap)
+ ext2fs_free_mem(&icount->fullmap);
+
+ 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));
+ icount->magic = EXT2_ET_MAGIC_ICOUNT;
+ icount->num_inodes = fs->super->s_inodes_count;
+
+ if ((flags & EXT2_ICOUNT_OPT_FULLMAP) &&
+ (flags & EXT2_ICOUNT_OPT_INCREMENT)) {
+ unsigned sz = sizeof(*icount->fullmap) * icount->num_inodes;
+
+ retval = ext2fs_get_mem(sz, &icount->fullmap);
+ /* If we can't allocate, fall back */
+ if (!retval) {
+ memset(icount->fullmap, 0, sz);
+ *ret = icount;
+ return 0;
+ }
+ }
+
+ retval = ext2fs_allocate_inode_bitmap(fs, "icount", &icount->single);
+ if (retval)
+ goto errout;
+
+ if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
+ retval = ext2fs_allocate_inode_bitmap(fs, "icount_inc",
+ &icount->multiple);
+ if (retval)
+ goto errout;
+ } else
+ icount->multiple = 0;
+
+ *ret = icount;
+ return 0;
+
+errout:
+ ext2fs_free_icount(icount);
+ return(retval);
+}
+
+#ifdef CONFIG_TDB
+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]);
+}
+#endif
+
+errcode_t ext2fs_create_icount_tdb(ext2_filsys fs EXT2FS_NO_TDB_UNUSED,
+ char *tdb_dir EXT2FS_NO_TDB_UNUSED,
+ int flags EXT2FS_NO_TDB_UNUSED,
+ ext2_icount_t *ret EXT2FS_NO_TDB_UNUSED)
+{
+#ifdef CONFIG_TDB
+ ext2_icount_t icount;
+ errcode_t retval;
+ char *fn, uuid[40];
+ ext2_ino_t num_inodes;
+ mode_t save_umask;
+ 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);
+ save_umask = umask(077);
+ fd = mkstemp(fn);
+ if (fd < 0) {
+ retval = errno;
+ ext2fs_free_mem(&fn);
+ goto errout;
+ }
+ icount->tdb_fn = fn;
+ umask(save_umask);
+ /*
+ * This is an overestimate of the size that we will need; the
+ * ideal value is the number of used inodes with a count
+ * greater than 1. OTOH the times when we really need this is
+ * with the backup programs that use lots of hard links, in
+ * which case the number of inodes in use approaches the ideal
+ * value.
+ */
+ num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
+
+ icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ close(fd);
+ if (icount->tdb == NULL) {
+ retval = errno;
+ goto errout;
+ }
+ *ret = icount;
+ return 0;
+errout:
+ ext2fs_free_icount(icount);
+ return(retval);
+#else
+ return EXT2_ET_UNIMPLEMENTED;
+#endif
+}
+
+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 (icount->fullmap)
+ goto successout;
+
+ 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;
+ }
+
+successout:
+ *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)
+{
+ int low, high, mid;
+
+ 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) {
+ mid = ((unsigned)low + (unsigned)high) >> 1;
+ 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;
+#ifdef CONFIG_TDB
+ 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;
+ }
+#endif
+ if (icount->fullmap) {
+ icount->fullmap[ino] = icount_16_xlate(count);
+ 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;
+#ifdef CONFIG_TDB
+ 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;
+ }
+#endif
+ if (icount->fullmap) {
+ *count = icount->fullmap[ino];
+ 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 (!icount->fullmap) {
+ 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 (icount->fullmap) {
+ curr_value = icount_16_xlate(icount->fullmap[ino] + 1);
+ icount->fullmap[ino] = curr_value;
+ } else 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 (icount->fullmap) {
+ if (!icount->fullmap[ino])
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ curr_value = --icount->fullmap[ino];
+ if (ret)
+ *ret = icount_16_xlate(curr_value);
+ return 0;
+ }
+
+ 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 (icount->fullmap)
+ return set_inode_count(icount, ino, count);
+
+ 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(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, 12000);
+
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ 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) {
+#ifdef CONFIG_TDB
+ 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
+ printf("Skipped\n");
+ return 0;
+#endif
+ } 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/ext2fs/imager.c b/lib/ext2fs/imager.c
new file mode 100644
index 0000000..23290a6
--- /dev/null
+++ b/lib/ext2fs/imager.c
@@ -0,0 +1,470 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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)
+{
+ dgrp_t group;
+ ssize_t left, c, d;
+ char *buf, *cp;
+ blk64_t blk;
+ ssize_t actual;
+ errcode_t retval;
+ ext2_loff_t r;
+
+ 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, group);
+ if (!blk) {
+ retval = EXT2_ET_MISSING_INODE_TABLE;
+ goto errout;
+ }
+ left = fs->inode_blocks_per_group;
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk + left - 1 >= ext2fs_blocks_count(fs->super))) {
+ retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
+ goto errout;
+ }
+ 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;
+ r = ext2fs_llseek(fd, fs->blocksize,
+ SEEK_CUR);
+ if (r < 0) {
+ retval = errno;
+ goto errout;
+ }
+ 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, d * fs->blocksize);
+ if (actual == -1) {
+ retval = errno;
+ goto errout;
+ }
+ if (actual != d * fs->blocksize) {
+ retval = EXT2_ET_SHORT_WRITE;
+ goto errout;
+ }
+ blk += d;
+ left -= d;
+ cp += d * fs->blocksize;
+ 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)))
+{
+ dgrp_t group;
+ ssize_t 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, 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 != 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;
+#ifdef WORDS_BIGENDIAN
+ unsigned int groups_per_block;
+ struct ext2_group_desc *gdp;
+ int j;
+#endif
+
+ if (fs->group_desc == NULL)
+ return EXT2_ET_NO_GDESC;
+
+ buf = malloc(fs->blocksize);
+ if (!buf)
+ return ENOMEM;
+
+ /*
+ * Write out the superblock
+ */
+ memset(buf, 0, fs->blocksize);
+#ifdef WORDS_BIGENDIAN
+ /*
+ * We're writing out superblock so let's convert
+ * it to little endian and then back if needed
+ */
+ ext2fs_swap_super(fs->super);
+ memcpy(buf, fs->super, SUPERBLOCK_SIZE);
+ ext2fs_swap_super(fs->super);
+#else
+ memcpy(buf, fs->super, SUPERBLOCK_SIZE);
+#endif
+ 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;
+
+#ifdef WORDS_BIGENDIAN
+ /*
+ * Convert group descriptors to little endian and back
+ * if needed
+ */
+ groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
+ for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
+ gdp = ext2fs_group_desc(fs, fs->group_desc, j);
+ if (gdp)
+ ext2fs_swap_group_desc2(fs, gdp);
+ }
+#endif
+
+ actual = write(fd, cp, (ssize_t)fs->blocksize * fs->desc_blocks);
+
+
+#ifdef WORDS_BIGENDIAN
+ groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
+ for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
+ gdp = ext2fs_group_desc(fs, fs->group_desc, j);
+ if (gdp)
+ ext2fs_swap_group_desc2(fs, gdp);
+ }
+#endif
+
+ 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 = (ssize_t)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,
+ (ssize_t)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 retval;
+ ssize_t actual;
+ size_t c;
+ __u64 itr, cnt, size, 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;
+ itr = 1;
+ cnt = (__u64)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;
+ itr = fs->super->s_first_data_block;
+ cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
+ size = EXT2_CLUSTERS_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);
+ if (size == 0)
+ break;
+
+ 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 < 0)
+ return errno;
+ if ((size_t) 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 retval;
+ __u64 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;
+ itr = 1;
+ cnt = (__u64)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;
+ itr = fs->super->s_first_data_block;
+ cnt = EXT2_GROUPS_TO_BLOCKS(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);
+ if (size == 0)
+ break;
+
+ 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/ext2fs/ind_block.c b/lib/ext2fs/ind_block.c
new file mode 100644
index 0000000..aa82ae6
--- /dev/null
+++ b/lib/ext2fs/ind_block.c
@@ -0,0 +1,67 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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/ext2fs/initialize.c b/lib/ext2fs/initialize.c
new file mode 100644
index 0000000..edd692b
--- /dev/null
+++ b/lib/ext2fs/initialize.c
@@ -0,0 +1,671 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#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;
+ blk64_t free_blocks;
+ blk_t numblocks;
+ int rsv_gdt;
+ int csum_flag;
+ int bigalloc_flag;
+ int io_flags;
+ int has_bg;
+ unsigned reserved_inos;
+ char *buf = 0;
+ char c;
+ double reserved_ratio;
+ char *time_env;
+
+ 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;
+ fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
+#ifdef WORDS_BIGENDIAN
+ fs->flags |= EXT2_FLAG_SWAP_BYTES;
+#endif
+
+ time_env = getenv("E2FSPROGS_FAKE_TIME");
+ if (time_env)
+ fs->now = strtoul(time_env, NULL, 0);
+
+ 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;
+ io_flags |= O_BINARY;
+ 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))
+#define assign_field(field) (super->field = param->field)
+
+ super->s_magic = EXT2_SUPER_MAGIC;
+ super->s_state = EXT2_VALID_FS;
+
+ bigalloc_flag = ext2fs_has_feature_bigalloc(param);
+
+ assign_field(s_log_block_size);
+
+ if (bigalloc_flag) {
+ set_field(s_log_cluster_size, super->s_log_block_size+4);
+ if (super->s_log_block_size > super->s_log_cluster_size) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+ } else
+ super->s_log_cluster_size = super->s_log_block_size;
+
+ set_field(s_first_data_block, super->s_log_cluster_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);
+ assign_field(s_backup_bgs[0]);
+ assign_field(s_backup_bgs[1]);
+
+ assign_field(s_encoding);
+ assign_field(s_encoding_flags);
+
+ if (ext2fs_has_feature_casefold(param))
+ fs->encoding = ext2fs_load_nls_table(param->s_encoding);
+
+ 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->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(super);
+ fs->cluster_ratio_bits = super->s_log_cluster_size -
+ super->s_log_block_size;
+
+ if (bigalloc_flag) {
+ unsigned long long bpg;
+
+ if (param->s_blocks_per_group &&
+ param->s_clusters_per_group &&
+ ((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) !=
+ param->s_blocks_per_group)) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+ if (param->s_clusters_per_group)
+ assign_field(s_clusters_per_group);
+ else if (param->s_blocks_per_group)
+ super->s_clusters_per_group =
+ param->s_blocks_per_group /
+ EXT2FS_CLUSTER_RATIO(fs);
+ else if (super->s_log_cluster_size + 15 < 32)
+ super->s_clusters_per_group = fs->blocksize * 8;
+ else
+ super->s_clusters_per_group = (fs->blocksize - 1) * 8;
+ if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super))
+ super->s_clusters_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super);
+ bpg = EXT2FS_C2B(fs,
+ (unsigned long long) super->s_clusters_per_group);
+ if (bpg >= (((unsigned long long) 1) << 32)) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+ super->s_blocks_per_group = bpg;
+ } else {
+ 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) &
+ ~((blk64_t) EXT2FS_CLUSTER_MASK(fs)));
+ 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;
+ }
+
+ set_field(s_mmp_update_interval, 0);
+
+ /*
+ * If we're creating an external journal device, we don't need
+ * to bother with the rest.
+ */
+ if (ext2fs_has_feature_journal_dev(super)) {
+ fs->group_desc_count = 0;
+ ext2fs_mark_super_dirty(fs);
+ *ret_fs = fs;
+ return 0;
+ }
+
+retry:
+ fs->group_desc_count = (dgrp_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;
+ }
+
+ set_field(s_desc_size,
+ ext2fs_has_feature_64bit(super) ?
+ EXT2_MIN_DESC_SIZE_64BIT : 0);
+
+ fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count,
+ EXT2_DESC_PER_BLOCK(super));
+
+ i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize;
+
+ if (ext2fs_has_feature_64bit(super) &&
+ (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 (!bigalloc_flag && 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 (ext2fs_has_feature_resize_inode(super))
+ 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;
+ }
+ /* Enable meta_bg if we'd lose more than 3/4 of a BG to GDT blocks. */
+ if (super->s_reserved_gdt_blocks + fs->desc_blocks >
+ super->s_blocks_per_group * 3 / 4) {
+ ext2fs_set_feature_meta_bg(fs->super);
+ ext2fs_clear_feature_resize_inode(fs->super);
+ set_field(s_reserved_gdt_blocks, 0);
+ }
+
+ /*
+ * 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 +
+ super->s_reserved_gdt_blocks);
+
+ if (ext2fs_has_feature_meta_bg(fs->super))
+ overhead++;
+ else
+ overhead += fs->desc_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);
+ has_bg = 0;
+ if (ext2fs_has_feature_sparse_super2(super)) {
+ /*
+ * We have to do this manually since
+ * super->s_backup_bgs hasn't been set up yet.
+ */
+ if (fs->group_desc_count == 2)
+ has_bg = param->s_backup_bgs[0] != 0;
+ else
+ has_bg = param->s_backup_bgs[1] != 0;
+ } else
+ has_bg = ext2fs_bg_has_super(fs, fs->group_desc_count - 1);
+ if (has_bg)
+ 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);
+ /*
+ * If blocks count is changed, we need to recalculate
+ * reserved blocks count not to exceed 50%.
+ */
+ reserved_ratio = 100.0 * ext2fs_r_blocks_count(param) /
+ ext2fs_blocks_count(param);
+ ext2fs_r_blocks_count_set(super, reserved_ratio *
+ ext2fs_blocks_count(super) / 100.0);
+
+ 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.
+ */
+
+ /* Set up the locations of the backup superblocks */
+ if (ext2fs_has_feature_sparse_super2(super)) {
+ if (super->s_backup_bgs[0] >= fs->group_desc_count)
+ super->s_backup_bgs[0] = fs->group_desc_count - 1;
+ if (super->s_backup_bgs[1] >= fs->group_desc_count)
+ super->s_backup_bgs[1] = fs->group_desc_count - 1;
+ if (super->s_backup_bgs[0] == super->s_backup_bgs[1])
+ super->s_backup_bgs[1] = 0;
+ if (super->s_backup_bgs[0] > super->s_backup_bgs[1]) {
+ __u32 t = super->s_backup_bgs[0];
+ super->s_backup_bgs[0] = super->s_backup_bgs[1];
+ super->s_backup_bgs[1] = t;
+ }
+ }
+
+ 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_subcluster_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).
+ */
+ free_blocks = 0;
+ csum_flag = ext2fs_has_group_desc_csum(fs);
+ reserved_inos = super->s_first_ino;
+ 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 (reserved_inos) {
+ if (numblocks > reserved_inos) {
+ numblocks -= reserved_inos;
+ reserved_inos = 0;
+ } else {
+ reserved_inos -= numblocks;
+ numblocks = 0;
+ }
+ }
+ 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;
+
+ free_blocks += 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);
+ }
+ free_blocks &= ~EXT2FS_CLUSTER_MASK(fs);
+ ext2fs_free_blocks_count_set(super, free_blocks);
+
+ 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;
+}
+
+errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs, int super_only)
+{
+ blk64_t blk;
+ ext2_ino_t ino;
+ unsigned int group = 0;
+ unsigned int count = 0;
+ int total_free = 0;
+ int group_free = 0;
+ int last_allocated = 0;
+ int uninit;
+
+ /*
+ * First calculate the block statistics
+ */
+ uninit = 1;
+ for (blk = fs->super->s_first_data_block;
+ blk < ext2fs_blocks_count(fs->super); blk++) {
+ if (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk)) {
+ group_free++;
+ total_free++;
+ } else {
+ uninit = 0;
+ }
+ count++;
+ if ((count == fs->super->s_blocks_per_group) ||
+ (blk == ext2fs_blocks_count(fs->super)-1)) {
+ ext2fs_bg_free_blocks_count_set(fs, group,
+ group_free);
+ if (!super_only) {
+ if (uninit && blk !=
+ ext2fs_blocks_count(fs->super) - 1)
+ ext2fs_bg_flags_set(fs, group,
+ EXT2_BG_BLOCK_UNINIT);
+ else
+ ext2fs_bg_flags_clear(fs, group,
+ EXT2_BG_BLOCK_UNINIT);
+ }
+ count = 0;
+ group_free = 0;
+ uninit = 1;
+ group++;
+ }
+ }
+ total_free = EXT2FS_C2B(fs, total_free);
+ ext2fs_free_blocks_count_set(fs->super, total_free);
+
+ /*
+ * Next, calculate the inode statistics
+ */
+ group_free = 0;
+ total_free = 0;
+ last_allocated = 0;
+ count = 0;
+ group = 0;
+
+ /* Protect loop from wrap-around if s_inodes_count maxed */
+ for (ino = 1; ino <= fs->super->s_inodes_count && ino > 0; ino++) {
+ if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
+ group_free++;
+ total_free++;
+ } else {
+ last_allocated = ino;
+ }
+ count++;
+ if ((count == fs->super->s_inodes_per_group) ||
+ (ino == fs->super->s_inodes_count)) {
+ if (!super_only) {
+ if (last_allocated) {
+ ext2fs_bg_flags_clear(fs, group,
+ EXT2_BG_INODE_UNINIT);
+ ext2fs_bg_itable_unused_set(fs, group,
+ fs->super->s_inodes_per_group -
+ (last_allocated %
+ fs->super->s_inodes_per_group));
+ } else {
+ ext2fs_bg_flags_set(fs, group,
+ EXT2_BG_INODE_UNINIT);
+ ext2fs_bg_itable_unused_set(fs, group,
+ 0);
+ }
+ ext2fs_bg_free_inodes_count_set(fs, group,
+ group_free);
+ }
+ group++;
+ count = 0;
+ group_free = 0;
+ last_allocated = 0;
+ }
+ }
+ fs->super->s_free_inodes_count = total_free;
+ ext2fs_mark_super_dirty(fs);
+ return 0;
+}
diff --git a/lib/ext2fs/inline.c b/lib/ext2fs/inline.c
new file mode 100644
index 0000000..ae2ae6e
--- /dev/null
+++ b/lib/ext2fs/inline.c
@@ -0,0 +1,118 @@
+/*
+ * 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%
+ */
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 600 /* for posix_memalign() */
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#include "ext2_fs.h"
+#define INCLUDE_INLINE_FUNCS
+#include "ext2fs.h"
+
+/*
+ * We used to define this as an inline, but since we are now using
+ * autoconf-defined #ifdef's, we need to export this as a
+ * library-provided function exclusively.
+ */
+errcode_t ext2fs_get_memalign(unsigned long size,
+ unsigned long align, void *ptr)
+{
+ errcode_t retval = 0;
+ void **p = ptr;
+
+ if (align < 8)
+ align = 8;
+#ifdef HAVE_POSIX_MEMALIGN
+ retval = posix_memalign(p, align, size);
+ if (retval == ENOMEM)
+ return EXT2_ET_NO_MEMORY;
+#else /* !HAVE_POSIX_MEMALIGN */
+#ifdef HAVE_MEMALIGN
+ *p = memalign(align, size);
+ if (*p == NULL) {
+ if (errno)
+ return errno;
+ else
+ return EXT2_ET_NO_MEMORY;
+ }
+#else /* !HAVE_MEMALIGN */
+#ifdef HAVE_VALLOC
+ if (align > sizeof(long long))
+ *p = valloc(size);
+ else
+#endif
+ *p = malloc(size);
+ if ((uintptr_t) *p & (align - 1)) {
+ free(*p);
+ *p = 0;
+ }
+ if (*p == 0)
+ return EXT2_ET_NO_MEMORY;
+#endif /* HAVE_MEMALIGN */
+#endif /* HAVE_POSIX_MEMALIGN */
+ return retval;
+}
+
+#ifdef DEBUG
+static int isaligned(void *ptr, unsigned long align)
+{
+ return (((unsigned long) ptr & (align - 1)) == 0);
+}
+
+static errcode_t test_memalign(unsigned long align)
+{
+ void *ptr = 0;
+ errcode_t retval;
+
+ retval = ext2fs_get_memalign(32, align, &ptr);
+ if (!retval && !isaligned(ptr, align))
+ retval = EINVAL;
+ free(ptr);
+ printf("tst_memalign(%lu) is %s\n", align,
+ retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+int main(int argc, char **argv)
+{
+ int err = 0;
+
+ if (test_memalign(4))
+ err++;
+ if (test_memalign(32))
+ err++;
+ if (test_memalign(1024))
+ err++;
+ if (test_memalign(4096))
+ err++;
+ return err;
+}
+#endif
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
new file mode 100644
index 0000000..bd52e37
--- /dev/null
+++ b/lib/ext2fs/inline_data.c
@@ -0,0 +1,842 @@
+/*
+ * inline_data.c --- data in inode
+ *
+ * Copyright (C) 2012 Zheng Liu <wenqing.lz@taobao.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <time.h>
+#include <limits.h> /* for PATH_MAX */
+
+#include "ext2_fs.h"
+#include "ext2_ext_attr.h"
+
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+struct ext2_inline_data {
+ ext2_filsys fs;
+ ext2_ino_t ino;
+ size_t ea_size; /* the size of inline data in ea area */
+ void *ea_data;
+};
+
+static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_set(handle, "system.data",
+ data->ea_data, data->ea_size);
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ data->ea_size = 0;
+ data->ea_data = 0;
+
+ retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_get(handle, "system.data",
+ (void **)&data->ea_data, &data->ea_size);
+ if (retval == EXT2_ET_EA_KEY_NOT_FOUND) {
+ data->ea_size = 0;
+ data->ea_data = NULL;
+ retval = 0;
+ } else if (retval)
+ goto err;
+
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inline_data data;
+ char empty[1] = { '\0' };
+
+ data.fs = fs;
+ data.ino = ino;
+ data.ea_size = 0;
+ data.ea_data = empty;
+ return ext2fs_inline_data_ea_set(&data);
+}
+
+errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size)
+{
+ struct ext2_inode inode;
+ struct ext2_inline_data data;
+ errcode_t retval;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+
+ *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
+ return ext2fs_free_mem(&data.ea_data);
+}
+
+int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
+ void *priv_data)
+{
+ struct dir_context *ctx;
+ struct ext2_inode inode;
+ struct ext2_dir_entry dirent;
+ struct ext2_inline_data data;
+ int ret = BLOCK_ABORT;
+ e2_blkcnt_t blockcnt = 0;
+ char *old_buf;
+ unsigned int old_buflen;
+ int old_flags;
+
+ ctx = (struct dir_context *)priv_data;
+ old_buf = ctx->buf;
+ old_buflen = ctx->buflen;
+ old_flags = ctx->flags;
+ ctx->flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA;
+
+ ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
+ if (ctx->errcode)
+ goto out;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
+ ctx->errcode = EXT2_ET_NO_INLINE_DATA;
+ goto out;
+ }
+
+ if (!LINUX_S_ISDIR(inode.i_mode)) {
+ ctx->errcode = EXT2_ET_NO_DIRECTORY;
+ goto out;
+ }
+ ret = 0;
+
+ /* we first check '.' and '..' dir */
+ dirent.inode = ino;
+ dirent.name_len = 1;
+ ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent);
+ dirent.name[0] = '.';
+ dirent.name[1] = '\0';
+ ctx->buf = (char *)&dirent;
+ ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_ABORT)
+ goto out;
+
+ dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
+ dirent.name_len = 2;
+ ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent);
+ dirent.name[0] = '.';
+ dirent.name[1] = '.';
+ dirent.name[2] = '\0';
+ ctx->buf = (char *)&dirent;
+ ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
+ errcode_t err;
+
+ inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
+ err = ext2fs_write_inode(fs, ino, &inode);
+ if (err)
+ goto out;
+ ret &= ~BLOCK_INLINE_DATA_CHANGED;
+ }
+ if (ret & BLOCK_ABORT)
+ goto out;
+
+ ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
+ ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+#endif
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
+ ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+#endif
+ ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
+ if (ctx->errcode)
+ ret |= BLOCK_ABORT;
+ ret &= ~BLOCK_INLINE_DATA_CHANGED;
+ }
+ if (ret & BLOCK_ABORT)
+ goto out;
+
+ data.fs = fs;
+ data.ino = ino;
+ ctx->errcode = ext2fs_inline_data_ea_get(&data);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+ if (data.ea_size <= 0)
+ goto out1;
+
+ ctx->buf = data.ea_data;
+ ctx->buflen = data.ea_size;
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out1;
+ }
+#endif
+
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
+ ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out1;
+ }
+#endif
+ ctx->errcode = ext2fs_inline_data_ea_set(&data);
+ if (ctx->errcode)
+ ret |= BLOCK_ABORT;
+ }
+
+out1:
+ ext2fs_free_mem(&data.ea_data);
+out:
+ ctx->buf = old_buf;
+ ctx->buflen = old_buflen;
+ ctx->flags = old_flags;
+ ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
+ return ret;
+}
+
+errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ retval = ext2fs_xattrs_open(fs, ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_remove(handle, "system.data");
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
+ char *bbuf, char *ibuf, int size)
+{
+ struct ext2_dir_entry *dir, *dir2;
+ struct ext2_dir_entry_tail *t;
+ errcode_t retval;
+ int offset;
+ unsigned int rec_len;
+ int csum_size = 0;
+ int filetype = 0;
+
+ if (ext2fs_has_feature_metadata_csum(fs->super))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ /* Create '.' and '..' */
+ if (ext2fs_has_feature_filetype(fs->super))
+ filetype = EXT2_FT_DIR;
+
+ /*
+ * Set up entry for '.'
+ */
+ dir = (struct ext2_dir_entry *) bbuf;
+ dir->inode = ino;
+ ext2fs_dirent_set_name_len(dir, 1);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
+ dir->rec_len = EXT2_DIR_REC_LEN(1);
+
+ /*
+ * Set up entry for '..'
+ */
+ dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
+ dir->rec_len = EXT2_DIR_REC_LEN(2);
+ dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
+ ext2fs_dirent_set_name_len(dir, 2);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ dir->name[1] = '.';
+
+ /*
+ * Adjust the last rec_len
+ */
+ offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
+ dir = (struct ext2_dir_entry *) (bbuf + offset);
+ memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
+ size - EXT4_INLINE_DATA_DOTDOT_SIZE);
+ size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) -
+ EXT4_INLINE_DATA_DOTDOT_SIZE;
+
+ do {
+ dir2 = dir;
+ retval = ext2fs_get_rec_len(fs, dir, &rec_len);
+ if (retval)
+ goto err;
+ offset += rec_len;
+ dir = (struct ext2_dir_entry *) (bbuf + offset);
+ } while (offset < size);
+ rec_len += fs->blocksize - csum_size - offset;
+ retval = ext2fs_set_rec_len(fs, rec_len, dir2);
+ if (retval)
+ goto err;
+
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
+
+err:
+ return retval;
+}
+
+static errcode_t
+ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, char *buf, size_t size)
+{
+ errcode_t retval;
+ blk64_t blk;
+ char *blk_buf;
+
+ retval = ext2fs_get_memzero(fs->blocksize, &blk_buf);
+ if (retval)
+ return retval;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in2(fs, buf + EXT4_INLINE_DATA_DOTDOT_SIZE,
+ size, 0);
+ if (retval)
+ goto errout;
+#endif
+
+ /* Adjust the rec_len */
+ retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size);
+ if (retval)
+ goto errout;
+ /* Allocate a new block */
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+ if (retval)
+ goto errout;
+
+ /* Update inode */
+ if (ext2fs_has_feature_extents(fs->super))
+ inode->i_flags |= EXT4_EXTENTS_FL;
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ retval = ext2fs_iblk_add_blocks(fs, inode, 1);
+ if (retval)
+ goto errout;
+ inode->i_size = fs->blocksize;
+ retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ goto errout;
+ ext2fs_block_alloc_stats(fs, blk, +1);
+
+errout:
+ ext2fs_free_mem(&blk_buf);
+ return retval;
+}
+
+static errcode_t
+ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, char *buf, size_t size)
+{
+ ext2_file_t e2_file;
+ errcode_t retval;
+
+ /* Update inode */
+ memset(inode->i_block, 0, sizeof(inode->i_block));
+ if (ext2fs_has_feature_extents(fs->super)) {
+ ext2_extent_handle_t handle;
+
+ inode->i_flags &= ~EXT4_EXTENTS_FL;
+ retval = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (retval)
+ return retval;
+ ext2fs_extent_free(handle);
+ }
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ /* Write out the block buffer */
+ retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
+ if (retval)
+ return retval;
+ retval = ext2fs_file_write(e2_file, buf, size, 0);
+ ext2fs_file_close(e2_file);
+ return retval;
+}
+
+errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ struct ext2_inline_data data;
+ errcode_t retval;
+ size_t inline_size;
+ char *inline_buf = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+ inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
+ retval = ext2fs_get_mem(inline_size, &inline_buf);
+ if (retval)
+ goto errout;
+
+ memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ if (data.ea_size > 0) {
+ memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE,
+ data.ea_data, data.ea_size);
+ }
+
+ memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ /*
+ * NOTE: We must do this write -> ea_remove -> read cycle here because
+ * removing the inline data EA can free the EA block, which is a change
+ * that our stack copy of the inode will never see. If that happens,
+ * we can end up with the EA block and lblk 0 pointing to the same
+ * pblk, which is bad news.
+ */
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ if (retval)
+ goto errout;
+ retval = ext2fs_inline_data_ea_remove(fs, ino);
+ if (retval)
+ goto errout;
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ goto errout;
+
+ if (LINUX_S_ISDIR(inode.i_mode)) {
+ retval = ext2fs_inline_data_dir_expand(fs, ino, &inode,
+ inline_buf, inline_size);
+ } else {
+ retval = ext2fs_inline_data_file_expand(fs, ino, &inode,
+ inline_buf, inline_size);
+ }
+
+errout:
+ if (inline_buf)
+ ext2fs_free_mem(&inline_buf);
+ ext2fs_free_mem(&data.ea_data);
+ return retval;
+}
+
+/*
+ * When caller uses this function to retrieve the inline data, it must
+ * allocate a buffer which has the size of inline data. The size of
+ * inline data can be know by ext2fs_inline_data_get_size().
+ */
+errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t *size)
+{
+ struct ext2_inode inode_buf;
+ struct ext2_inline_data data;
+ errcode_t retval;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+
+ memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ if (data.ea_size > 0)
+ memcpy((char *) buf + EXT4_MIN_INLINE_DATA_SIZE,
+ data.ea_data, data.ea_size);
+
+ if (size)
+ *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
+ ext2fs_free_mem(&data.ea_data);
+ return 0;
+}
+
+errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t size)
+{
+ struct ext2_inode inode_buf;
+ struct ext2_inline_data data = {
+ .fs = fs,
+ .ino = ino,
+ };
+ errcode_t retval;
+ size_t free_ea_size, existing_size, free_inode_size;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
+ memcpy((void *)inode->i_block, buf, size);
+ } else {
+ retval = ext2fs_xattr_inode_max_size(fs, ino, &free_ea_size);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_inline_data_size(fs, ino, &existing_size);
+ if (retval)
+ return retval;
+
+ if (existing_size < EXT4_MIN_INLINE_DATA_SIZE) {
+ free_inode_size = EXT4_MIN_INLINE_DATA_SIZE -
+ existing_size;
+ } else {
+ free_inode_size = 0;
+ }
+
+ if (size != existing_size &&
+ size > existing_size + free_ea_size + free_inode_size)
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+
+ memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE);
+ if (size > EXT4_MIN_INLINE_DATA_SIZE)
+ data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE;
+ data.ea_data = (char *) buf + EXT4_MIN_INLINE_DATA_SIZE;
+ }
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+ return ext2fs_inline_data_ea_set(&data);
+}
+
+#ifdef DEBUG
+#include "e2p/e2p.h"
+
+/*
+ * The length of buffer is set to 64 because in inode's i_block member it only
+ * can save 60 bytes. Thus this value can let the data being saved in extra
+ * space.
+ */
+#define BUFF_SIZE (64)
+
+static errcode_t file_test(ext2_filsys fs)
+{
+ struct ext2_inode inode;
+ ext2_ino_t newfile;
+ errcode_t retval;
+ size_t size;
+ char *buf = 0, *cmpbuf = 0;
+
+ /* create a new file */
+ retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile);
+ if (retval) {
+ com_err("file_test", retval, "while allocating a new inode");
+ return 1;
+ }
+
+ memset(&inode, 0, sizeof(inode));
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
+ inode.i_mode = LINUX_S_IFREG;
+ retval = ext2fs_write_new_inode(fs, newfile, &inode);
+ if (retval) {
+ com_err("file_test", retval, "while writing a new inode");
+ return 1;
+ }
+
+ retval = ext2fs_inline_data_init(fs, newfile);
+ if (retval) {
+ com_err("file_test", retval, "while init 'system.data'");
+ return 1;
+ }
+
+ retval = ext2fs_inline_data_size(fs, newfile, &size);
+ if (retval) {
+ com_err("file_test", retval, "while getting size");
+ return 1;
+ }
+
+ if (size != EXT4_MIN_INLINE_DATA_SIZE) {
+ fprintf(stderr,
+ "tst_inline_data: size of inline data is wrong\n");
+ return 1;
+ }
+
+ ext2fs_get_mem(BUFF_SIZE, &buf);
+ memset(buf, 'a', BUFF_SIZE);
+ retval = ext2fs_inline_data_set(fs, newfile, 0, buf, BUFF_SIZE);
+ if (retval) {
+ com_err("file_test", retval,
+ "while setting inline data %s", buf);
+ goto err;
+ }
+
+ ext2fs_get_mem(BUFF_SIZE, &cmpbuf);
+ retval = ext2fs_inline_data_get(fs, newfile, 0, cmpbuf, &size);
+ if (retval) {
+ com_err("file_test", retval, "while getting inline data");
+ goto err;
+ }
+
+ if (size != BUFF_SIZE) {
+ fprintf(stderr,
+ "tst_inline_data: size %zu != buflen %u\n",
+ size, BUFF_SIZE);
+ retval = 1;
+ goto err;
+ }
+
+ if (memcmp(buf, cmpbuf, BUFF_SIZE)) {
+ fprintf(stderr, "tst_inline_data: buf != cmpbuf\n");
+ retval = 1;
+ goto err;
+ }
+
+ retval = ext2fs_punch(fs, newfile, 0, 0, 0, ~0ULL);
+ if (retval) {
+ com_err("file_test", retval, "while truncating inode");
+ goto err;
+ }
+
+ /* reload inode and check isize */
+ ext2fs_read_inode(fs, newfile, &inode);
+ if (inode.i_size != 0) {
+ fprintf(stderr, "tst_inline_data: i_size should be 0\n");
+ retval = 1;
+ }
+
+err:
+ if (cmpbuf)
+ ext2fs_free_mem(&cmpbuf);
+ if (buf)
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+static errcode_t dir_test(ext2_filsys fs)
+{
+ const char *dot_name = ".";
+ const char *stub_name = "stub";
+ const char *parent_name = "test";
+ ext2_ino_t parent, dir, tmp;
+ errcode_t retval;
+ char dirname[32];
+ int i;
+
+ retval = ext2fs_mkdir(fs, 11, 11, stub_name);
+ if (retval) {
+ com_err("dir_test", retval, "while creating %s dir", stub_name);
+ return retval;
+ }
+
+ retval = ext2fs_mkdir(fs, 11, 0, parent_name);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while creating %s dir", parent_name);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, 11, parent_name, strlen(parent_name),
+ 0, &parent);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, parent, dot_name, strlen(dot_name),
+ 0, &tmp);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ if (parent != tmp) {
+ fprintf(stderr, "tst_inline_data: parent (%u) != tmp (%u)\n",
+ parent, tmp);
+ return 1;
+ }
+
+ for (i = 0, dir = 13; i < 4; i++, dir++) {
+ tmp = 0;
+ snprintf(dirname, sizeof(dirname), "%d", i);
+ retval = ext2fs_mkdir(fs, parent, 0, dirname);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while creating %s dir", dirname);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, parent, dirname, strlen(dirname),
+ 0, &tmp);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ if (dir != tmp) {
+ fprintf(stderr,
+ "tst_inline_data: dir (%u) != tmp (%u)\n",
+ dir, tmp);
+ return 1;
+ }
+ }
+
+ snprintf(dirname, sizeof(dirname), "%d", i);
+ retval = ext2fs_mkdir(fs, parent, 0, dirname);
+ if (retval && retval != EXT2_ET_DIR_NO_SPACE) {
+ com_err("dir_test", retval, "while creating %s dir", dirname);
+ return retval;
+ }
+
+ retval = ext2fs_expand_dir(fs, parent);
+ if (retval) {
+ com_err("dir_test", retval, "while expanding %s dir", parent_name);
+ return retval;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ ext2_filsys fs;
+ struct ext2_super_block param;
+ errcode_t retval;
+
+ /* setup */
+ initialize_ext2_error_table();
+
+ memset(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, 32768);
+ param.s_inodes_count = 100;
+
+ param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA;
+ param.s_rev_level = EXT2_DYNAMIC_REV;
+ param.s_inode_size = 256;
+
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ test_io_manager, &fs);
+ if (retval) {
+ com_err("setup", retval,
+ "while initializing filesystem");
+ exit(1);
+ }
+
+ retval = ext2fs_allocate_tables(fs);
+ if (retval) {
+ com_err("setup", retval,
+ "while allocating tables for test filesystem");
+ exit(1);
+ }
+
+ /* initialize inode cache */
+ if (!fs->icache) {
+ ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super);
+ int i;
+
+ /* we just want to init inode cache. So ignore error */
+ ext2fs_create_inode_cache(fs, 16);
+ if (!fs->icache) {
+ fprintf(stderr,
+ "tst_inline_data: init inode cache failed\n");
+ exit(1);
+ }
+
+ /* setup inode cache */
+ for (i = 0; i < fs->icache->cache_size; i++)
+ fs->icache->cache[i].ino = first_ino++;
+ }
+
+ /* test */
+ if (file_test(fs)) {
+ fprintf(stderr, "tst_inline_data(FILE): FAILED\n");
+ return 1;
+ }
+ printf("tst_inline_data(FILE): OK\n");
+
+ if (dir_test(fs)) {
+ fprintf(stderr, "tst_inline_data(DIR): FAILED\n");
+ return 1;
+ }
+ printf("tst_inline_data(DIR): OK\n");
+ ext2fs_free(fs);
+
+ return 0;
+}
+#endif
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
new file mode 100644
index 0000000..957d5aa
--- /dev/null
+++ b/lib/ext2fs/inode.c
@@ -0,0 +1,1122 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "e2image.h"
+
+#define IBLOCK_STATUS_CSUMS_OK 1
+#define IBLOCK_STATUS_INSANE 2
+#define SCAN_BLOCK_STATUS(scan) ((scan)->temp_buffer + (scan)->inode_size)
+
+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)
+{
+ unsigned 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;
+}
+
+/*
+ * Free the inode cache structure
+ */
+void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
+{
+ unsigned i;
+
+ if (--icache->refcount)
+ return;
+ if (icache->buffer)
+ ext2fs_free_mem(&icache->buffer);
+ for (i = 0; i < icache->cache_size; i++)
+ ext2fs_free_mem(&icache->cache[i].inode);
+ if (icache->cache)
+ ext2fs_free_mem(&icache->cache);
+ icache->buffer_blk = 0;
+ ext2fs_free_mem(&icache);
+}
+
+errcode_t ext2fs_create_inode_cache(ext2_filsys fs, unsigned int cache_size)
+{
+ unsigned i;
+ 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)
+ goto errout;
+
+ fs->icache->buffer_blk = 0;
+ fs->icache->cache_last = -1;
+ fs->icache->cache_size = cache_size;
+ fs->icache->refcount = 1;
+ retval = ext2fs_get_array(fs->icache->cache_size,
+ sizeof(struct ext2_inode_cache_ent),
+ &fs->icache->cache);
+ if (retval)
+ goto errout;
+
+ for (i = 0; i < fs->icache->cache_size; i++) {
+ retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super),
+ &fs->icache->cache[i].inode);
+ if (retval)
+ goto errout;
+ }
+
+ ext2fs_flush_icache(fs);
+ return 0;
+errout:
+ ext2fs_free_inode_cache(fs->icache);
+ fs->icache = 0;
+ return retval;
+}
+
+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 (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ if (fs->blocksize < 1024)
+ return EXT2_FILSYS_CORRUPTED; /* Should never happen */
+
+ /*
+ * If fs->badblocks isn't set, then set it --- since the inode
+ * scanning functions require it.
+ */
+ if (fs->badblocks == 0) {
+ /*
+ * Temporarily 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 :
+ EXT2_INODE_SCAN_DEFAULT_BUFFER_BLOCKS;
+ scan->current_block = ext2fs_inode_table_loc(scan->fs,
+ scan->current_group);
+ if (scan->current_block &&
+ ((scan->current_block < fs->super->s_first_data_block) ||
+ (scan->current_block + fs->inode_blocks_per_group - 1 >=
+ ext2fs_blocks_count(fs->super)))) {
+ ext2fs_free_mem(&scan);
+ return EXT2_ET_GDESC_BAD_INODE_TABLE;
+ }
+
+ scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+ scan->blocks_left = scan->fs->inode_blocks_per_group;
+ if (ext2fs_has_group_desc_csum(fs)) {
+ __u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group);
+ if (scan->inodes_left > unused)
+ scan->inodes_left -= unused;
+ else
+ scan->inodes_left = 0;
+ scan->blocks_left =
+ (scan->inodes_left +
+ (fs->blocksize / scan->inode_size - 1)) *
+ scan->inode_size / fs->blocksize;
+ }
+ retval = io_channel_alloc_buf(fs->io, scan->inode_buffer_blocks,
+ &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->inode_buffer_blocks,
+ &scan->temp_buffer);
+ if (retval) {
+ ext2fs_free_mem(&scan->inode_buffer);
+ ext2fs_free_mem(&scan);
+ return retval;
+ }
+ memset(SCAN_BLOCK_STATUS(scan), 0, scan->inode_buffer_blocks);
+ if (scan->fs->badblocks && scan->fs->badblocks->num)
+ scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
+ if (ext2fs_has_group_desc_csum(fs))
+ 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 (ext2fs_has_group_desc_csum(fs)) {
+ __u32 unused = ext2fs_bg_itable_unused(fs, scan->current_group);
+ if (scan->inodes_left > unused)
+ scan->inodes_left -= unused;
+ else
+ scan->inodes_left = 0;
+ scan->blocks_left =
+ (scan->inodes_left +
+ (fs->blocksize / scan->inode_size - 1)) *
+ scan->inode_size / fs->blocksize;
+ }
+ if (scan->current_block &&
+ ((scan->current_block < fs->super->s_first_data_block) ||
+ (scan->current_block + fs->inode_blocks_per_group - 1 >=
+ ext2fs_blocks_count(fs->super))))
+ return EXT2_ET_GDESC_BAD_INODE_TABLE;
+ 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;
+ scan->bad_block_ptr = 0;
+ 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,
+ blk64_t *num_blocks)
+{
+ blk64_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;
+
+ /* Make sure bad_block_ptr is still valid */
+ if (scan->bad_block_ptr >= bb->num) {
+ scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+ 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;
+}
+
+static int block_map_looks_insane(ext2_filsys fs,
+ struct ext2_inode_large *inode)
+{
+ unsigned int i, bad;
+
+ /* We're only interested in block mapped files, dirs, and symlinks */
+ if ((inode->i_flags & EXT4_INLINE_DATA_FL) ||
+ (inode->i_flags & EXT4_EXTENTS_FL))
+ return 0;
+ if (!LINUX_S_ISREG(inode->i_mode) &&
+ !LINUX_S_ISLNK(inode->i_mode) &&
+ !LINUX_S_ISDIR(inode->i_mode))
+ return 0;
+ if (LINUX_S_ISLNK(inode->i_mode) &&
+ EXT2_I_SIZE(inode) <= sizeof(inode->i_block))
+ return 0;
+
+ /* Unused inodes probably aren't insane */
+ if (inode->i_links_count == 0)
+ return 0;
+
+ /* See if more than half the block maps are insane */
+ for (i = 0, bad = 0; i < EXT2_N_BLOCKS; i++)
+ if (inode->i_block[i] != 0 &&
+ (inode->i_block[i] < fs->super->s_first_data_block ||
+ inode->i_block[i] >= ext2fs_blocks_count(fs->super)))
+ bad++;
+ return bad > EXT2_N_BLOCKS / 2;
+}
+
+static int extent_head_looks_insane(struct ext2_inode_large *inode)
+{
+ if (!(inode->i_flags & EXT4_EXTENTS_FL) ||
+ ext2fs_extent_header_verify(inode->i_block,
+ sizeof(inode->i_block)) == 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Check all the inodes that we just read into the buffer. Record what we
+ * find here -- currently, we can observe that all checksums are ok; more
+ * than half the inodes are insane; or no conclusions at all.
+ */
+static void check_inode_block_sanity(ext2_inode_scan scan, blk64_t num_blocks)
+{
+ ext2_ino_t ino, inodes_to_scan;
+ unsigned int badness, checksum_failures;
+ unsigned int inodes_in_buf, inodes_per_block;
+ char *p;
+ struct ext2_inode_large *inode;
+ char *block_status;
+ unsigned int blk, bad_csum;
+
+ if (!(scan->scan_flags & EXT2_SF_WARN_GARBAGE_INODES))
+ return;
+
+ inodes_to_scan = scan->inodes_left;
+ inodes_in_buf = num_blocks * scan->fs->blocksize / scan->inode_size;
+ if (inodes_to_scan > inodes_in_buf)
+ inodes_to_scan = inodes_in_buf;
+
+ p = (char *) scan->inode_buffer;
+ ino = scan->current_inode + 1;
+ checksum_failures = badness = 0;
+ block_status = SCAN_BLOCK_STATUS(scan);
+ memset(block_status, 0, scan->inode_buffer_blocks);
+ inodes_per_block = EXT2_INODES_PER_BLOCK(scan->fs->super);
+
+ if (inodes_per_block < 2)
+ return;
+
+#ifdef WORDS_BIGENDIAN
+ if (ext2fs_get_mem(EXT2_INODE_SIZE(scan->fs->super), &inode))
+ return;
+#endif
+
+ while (inodes_to_scan > 0) {
+ blk = (p - (char *)scan->inode_buffer) / scan->fs->blocksize;
+ bad_csum = ext2fs_inode_csum_verify(scan->fs, ino,
+ (struct ext2_inode_large *) p) == 0;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_inode_full(scan->fs,
+ (struct ext2_inode_large *) inode,
+ (struct ext2_inode_large *) p,
+ 0, EXT2_INODE_SIZE(scan->fs->super));
+#else
+ inode = (struct ext2_inode_large *) p;
+#endif
+
+ /* Is this inode insane? */
+ if (bad_csum) {
+ checksum_failures++;
+ badness++;
+ } else if (extent_head_looks_insane(inode) ||
+ block_map_looks_insane(scan->fs, inode))
+ badness++;
+
+ /* If more than half are insane, declare the whole block bad */
+ if (badness > inodes_per_block / 2) {
+ unsigned int ino_adj;
+
+ block_status[blk] |= IBLOCK_STATUS_INSANE;
+ ino_adj = inodes_per_block -
+ ((ino - 1) % inodes_per_block);
+ if (ino_adj > inodes_to_scan)
+ ino_adj = inodes_to_scan;
+ inodes_to_scan -= ino_adj;
+ p += scan->inode_size * ino_adj;
+ ino += ino_adj;
+ checksum_failures = badness = 0;
+ continue;
+ }
+
+ if ((ino % inodes_per_block) == 0) {
+ if (checksum_failures == 0)
+ block_status[blk] |= IBLOCK_STATUS_CSUMS_OK;
+ checksum_failures = badness = 0;
+ }
+ inodes_to_scan--;
+ p += scan->inode_size;
+ ino++;
+ };
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_free_mem(&inode);
+#endif
+}
+
+/*
+ * 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)
+{
+ blk64_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;
+ }
+ check_inode_block_sanity(scan, num_blocks);
+
+ 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;
+ int length;
+ struct ext2_inode_large *iptr = (struct ext2_inode_large *)inode;
+ char *iblock_status;
+ unsigned int iblk;
+
+ EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
+ length = EXT2_INODE_SIZE(scan->fs->super);
+ iblock_status = SCAN_BLOCK_STATUS(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) {
+ if (scan->bytes_left)
+ 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
+ }
+
+ if (bufsize < length) {
+ retval = ext2fs_get_mem(length, &iptr);
+ if (retval)
+ return retval;
+ }
+
+ retval = 0;
+ iblk = scan->current_inode % EXT2_INODES_PER_GROUP(scan->fs->super) /
+ EXT2_INODES_PER_BLOCK(scan->fs->super) %
+ scan->inode_buffer_blocks;
+ 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;
+
+ /* Verify the inode checksum. */
+ if (!(iblock_status[iblk] & IBLOCK_STATUS_CSUMS_OK) &&
+ !(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+ (struct ext2_inode_large *)scan->temp_buffer))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
+#ifdef WORDS_BIGENDIAN
+ memset(iptr, 0, length);
+ ext2fs_swap_inode_full(scan->fs,
+ (struct ext2_inode_large *) iptr,
+ (struct ext2_inode_large *) scan->temp_buffer,
+ 0, length);
+#else
+ memcpy(iptr, scan->temp_buffer, length);
+#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 {
+ /* Verify the inode checksum. */
+ if (!(iblock_status[iblk] & IBLOCK_STATUS_CSUMS_OK) &&
+ !(scan->fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_csum_verify(scan->fs, scan->current_inode + 1,
+ (struct ext2_inode_large *)scan->ptr))
+ retval = EXT2_ET_INODE_CSUM_INVALID;
+
+#ifdef WORDS_BIGENDIAN
+ memset(iptr, 0, length);
+ ext2fs_swap_inode_full(scan->fs,
+ (struct ext2_inode_large *) iptr,
+ (struct ext2_inode_large *) scan->ptr,
+ 0, length);
+#else
+ memcpy(iptr, scan->ptr, length);
+#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;
+ }
+ if ((iblock_status[iblk] & IBLOCK_STATUS_INSANE) &&
+ (retval == 0 || retval == EXT2_ET_INODE_CSUM_INVALID))
+ retval = EXT2_ET_INODE_IS_GARBAGE;
+
+ scan->inodes_left--;
+ scan->current_inode++;
+ *ino = scan->current_inode;
+ if (iptr != (struct ext2_inode_large *)inode) {
+ memcpy(inode, iptr, bufsize);
+ ext2fs_free_mem(&iptr);
+ }
+ 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_inode2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode, int bufsize,
+ int flags)
+{
+ blk64_t block_nr;
+ dgrp_t group;
+ unsigned long block, offset;
+ char *ptr;
+ errcode_t retval;
+ unsigned i;
+ int clen, inodes_per_block;
+ io_channel io;
+ int length = EXT2_INODE_SIZE(fs->super);
+ struct ext2_inode_large *iptr;
+ int cache_slot, fail_csum;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ if (fs->blocksize < 1024)
+ return EXT2_FILSYS_CORRUPTED; /* Should never happen */
+
+ /* Check to see if user has an override function */
+ if (fs->read_inode &&
+ ((bufsize == sizeof(struct ext2_inode)) ||
+ (EXT2_INODE_SIZE(fs->super) == sizeof(struct ext2_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 = ext2fs_create_inode_cache(fs, 4);
+ if (retval)
+ return retval;
+ }
+ /* Check to see if it's in the inode cache */
+ for (i = 0; i < fs->icache->cache_size; i++) {
+ if (fs->icache->cache[i].ino == ino) {
+ memcpy(inode, fs->icache->cache[i].inode,
+ (bufsize > length) ? length : bufsize);
+ return 0;
+ }
+ }
+ if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+ inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
+ block_nr = ext2fs_le32_to_cpu(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);
+ block_nr = ext2fs_inode_table_loc(fs, group);
+ if (!block_nr)
+ return EXT2_ET_MISSING_INODE_TABLE;
+ if ((block_nr < fs->super->s_first_data_block) ||
+ (block_nr + fs->inode_blocks_per_group - 1 >=
+ ext2fs_blocks_count(fs->super)))
+ return EXT2_ET_GDESC_BAD_INODE_TABLE;
+ block_nr += block;
+ io = fs->io;
+ }
+ offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+ cache_slot = (fs->icache->cache_last + 1) % fs->icache->cache_size;
+ iptr = (struct ext2_inode_large *)fs->icache->cache[cache_slot].inode;
+
+ ptr = (char *) iptr;
+ 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++;
+ }
+ length = EXT2_INODE_SIZE(fs->super);
+
+ /* Verify the inode checksum. */
+ fail_csum = !ext2fs_inode_csum_verify(fs, ino, iptr);
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) iptr,
+ (struct ext2_inode_large *) iptr,
+ 0, length);
+#endif
+
+ /* Update the inode cache bookkeeping */
+ if (!fail_csum) {
+ fs->icache->cache_last = cache_slot;
+ fs->icache->cache[cache_slot].ino = ino;
+ }
+ memcpy(inode, iptr, (bufsize > length) ? length : bufsize);
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !(flags & READ_INODE_NOCSUM) && fail_csum)
+ return EXT2_ET_INODE_CSUM_INVALID;
+
+ return 0;
+}
+
+errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode, int bufsize)
+{
+ return ext2fs_read_inode2(fs, ino, inode, bufsize, 0);
+}
+
+errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode)
+{
+ return ext2fs_read_inode2(fs, ino, inode,
+ sizeof(struct ext2_inode), 0);
+}
+
+errcode_t ext2fs_write_inode2(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode, int bufsize,
+ int flags)
+{
+ blk64_t block_nr;
+ dgrp_t group;
+ unsigned long block, offset;
+ errcode_t retval = 0;
+ struct ext2_inode_large *w_inode;
+ char *ptr;
+ unsigned i;
+ int clen;
+ int length = EXT2_INODE_SIZE(fs->super);
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ /* 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;
+ }
+
+ if ((ino == 0) || (ino > fs->super->s_inodes_count))
+ return EXT2_ET_BAD_INODE_NUM;
+
+ /* Prepare our shadow buffer for read/modify/byteswap/write */
+ retval = ext2fs_get_mem(length, &w_inode);
+ if (retval)
+ return retval;
+
+ if (bufsize < length) {
+ retval = ext2fs_read_inode2(fs, ino,
+ (struct ext2_inode *)w_inode,
+ length, READ_INODE_NOCSUM);
+ if (retval)
+ goto errout;
+ }
+
+ /* 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) {
+ memcpy(fs->icache->cache[i].inode, inode,
+ (bufsize > length) ? length : bufsize);
+ break;
+ }
+ }
+ } else {
+ retval = ext2fs_create_inode_cache(fs, 4);
+ if (retval)
+ goto errout;
+ }
+ memcpy(w_inode, inode, (bufsize > length) ? length : bufsize);
+
+ if (!(fs->flags & EXT2_FLAG_RW)) {
+ retval = EXT2_ET_RO_FILSYS;
+ goto errout;
+ }
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_inode_full(fs, w_inode, w_inode, 1, length);
+#endif
+
+ if ((flags & WRITE_INODE_NOCSUM) == 0) {
+ retval = ext2fs_inode_csum_set(fs, ino, w_inode);
+ if (retval)
+ goto errout;
+ }
+
+ 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);
+ block_nr = ext2fs_inode_table_loc(fs, (unsigned) group);
+ if (!block_nr) {
+ retval = EXT2_ET_MISSING_INODE_TABLE;
+ goto errout;
+ }
+ if ((block_nr < fs->super->s_first_data_block) ||
+ (block_nr + fs->inode_blocks_per_group - 1 >=
+ ext2fs_blocks_count(fs->super))) {
+ retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
+ goto errout;
+ }
+ block_nr += block;
+
+ offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+ 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:
+ ext2fs_free_mem(&w_inode);
+ return retval;
+}
+
+errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode * inode, int bufsize)
+{
+ return ext2fs_write_inode2(fs, ino, inode, bufsize, 0);
+}
+
+errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode)
+{
+ return ext2fs_write_inode2(fs, ino, inode,
+ sizeof(struct ext2_inode), 0);
+}
+
+/*
+ * 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/ext2fs/inode_io.c b/lib/ext2fs/inode_io.c
new file mode 100644
index 0000000..d7474a6
--- /dev/null
+++ b/lib/ext2fs/inode_io.c
@@ -0,0 +1,290 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+
+#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 = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Inode I/O Manager",
+ .open = inode_open,
+ .close = inode_close,
+ .set_blksize = inode_set_blksize,
+ .read_blk = inode_read_blk,
+ .write_blk = inode_write_blk,
+ .flush = inode_flush,
+ .write_byte = inode_write_byte,
+ .read_blk64 = inode_read_blk64,
+ .write_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 && 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_llseek(data->file,
+ (ext2_off64_t)(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_llseek(data->file,
+ (ext2_off64_t) (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/ext2fs/io_manager.c b/lib/ext2fs/io_manager.c
new file mode 100644
index 0000000..dca6af0
--- /dev/null
+++ b/lib/ext2fs/io_manager.c
@@ -0,0 +1,152 @@
+/*
+ * io_manager.c --- the I/O manager abstraction
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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;
+}
+
+errcode_t io_channel_zeroout(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+ if (channel->manager->zeroout)
+ return (channel->manager->zeroout)(channel, block, count);
+
+ return EXT2_ET_UNIMPLEMENTED;
+}
+
+errcode_t io_channel_alloc_buf(io_channel io, int count, void *ptr)
+{
+ size_t size;
+
+ if (count == 0)
+ size = io->block_size;
+ else if (count > 0)
+ size = io->block_size * count;
+ else
+ size = -count;
+
+ if (io->align > 0) {
+ if ((unsigned) io->align > size)
+ size = io->align;
+ return ext2fs_get_memalign(size, io->align, ptr);
+ } else
+ return ext2fs_get_mem(size, ptr);
+}
+
+errcode_t io_channel_cache_readahead(io_channel io, unsigned long long block,
+ unsigned long long count)
+{
+ if (!io->manager->cache_readahead)
+ return EXT2_ET_OP_NOT_SUPPORTED;
+
+ return io->manager->cache_readahead(io, block, count);
+}
diff --git a/lib/ext2fs/irel.h b/lib/ext2fs/irel.h
new file mode 100644
index 0000000..23741ba
--- /dev/null
+++ b/lib/ext2fs/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 {
+ blk64_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 iterator
+ * 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/ext2fs/irel_ma.c b/lib/ext2fs/irel_ma.c
new file mode 100644
index 0000000..c64b7e7
--- /dev/null
+++ b/lib/ext2fs/irel_ma.c
@@ -0,0 +1,377 @@
+/*
+ * 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 "config.h"
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#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(max_inode+1,
+ sizeof(struct inode_reference_entry), &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/ext2fs/ismounted.c b/lib/ext2fs/ismounted.c
new file mode 100644
index 0000000..a7db1a5
--- /dev/null
+++ b/lib/ext2fs/ismounted.c
@@ -0,0 +1,478 @@
+/*
+ * 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%
+ */
+
+/* define BSD_SOURCE to make sure we get the major() macro */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_LINUX_LOOP_H
+#include <linux/loop.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_LINUX_MAJOR_H
+#include <linux/major.h>
+#endif /* HAVE_LINUX_MAJOR_H */
+#endif /* HAVE_LINUX_LOOP_H */
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#ifdef HAVE_GETMNTINFO
+#include <paths.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif /* HAVE_GETMNTINFO */
+#include <string.h>
+#include <sys/stat.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+#ifdef HAVE_SETMNTENT
+/*
+ * Check to see if a regular file is mounted.
+ * If /etc/mtab/ is a symlink of /proc/mounts, you will need the following check
+ * because the name in /proc/mounts is a loopback device not a regular file.
+ */
+static int check_loop_mounted(const char *mnt_fsname, dev_t mnt_rdev,
+ dev_t file_dev, ino_t file_ino)
+{
+#if defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H)
+ struct loop_info64 loopinfo = {0, };
+ int loop_fd, ret;
+
+ if (major(mnt_rdev) == LOOP_MAJOR) {
+ loop_fd = open(mnt_fsname, O_RDONLY);
+ if (loop_fd < 0)
+ return -1;
+
+ ret = ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo);
+ close(loop_fd);
+ if (ret < 0)
+ return -1;
+
+ if (file_dev == loopinfo.lo_device &&
+ file_ino == loopinfo.lo_inode)
+ return 1;
+ }
+#endif /* defined(HAVE_LINUX_LOOP_H) && defined(HAVE_LINUX_MAJOR_H) */
+ return 0;
+}
+
+/*
+ * 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, dir_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) {
+ if (errno == ENOENT) {
+ if (getenv("EXT2FS_NO_MTAB_OK"))
+ return 0;
+ else
+ return EXT2_ET_NO_MTAB_FILE;
+ }
+ return errno;
+ }
+ if (stat(file, &st_buf) == 0) {
+ if (ext2fsP_is_disk_device(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) {
+ if (stat(mnt->mnt_dir, &st_buf) != 0)
+ continue;
+ if (file_rdev && (file_rdev != st_buf.st_dev)) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! "
+ "(%s is not mounted on %s)\n",
+ mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+ continue;
+ }
+ break;
+ }
+ if (stat(mnt->mnt_fsname, &st_buf) == 0) {
+ if (ext2fsP_is_disk_device(st_buf.st_mode)) {
+#ifndef __GNU__
+ if (file_rdev &&
+ (file_rdev == st_buf.st_rdev)) {
+ if (stat(mnt->mnt_dir,
+ &dir_st_buf) != 0)
+ continue;
+ if (file_rdev == dir_st_buf.st_dev)
+ break;
+ }
+ if (check_loop_mounted(mnt->mnt_fsname,
+ st_buf.st_rdev, file_dev,
+ file_ino) == 1)
+ 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;
+ }
+ *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);
+ }
+
+ if (mnt && mnt->mnt_type &&
+ (!strcmp(mnt->mnt_type, "ext4") ||
+ !strcmp(mnt->mnt_type, "ext3") ||
+ !strcmp(mnt->mnt_type, "ext2")))
+ *mount_flags |= EXT2_MF_EXTFS;
+ 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)
+ 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_SETMNTENT */
+
+/*
+ * 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) &&
+ ext2fsP_is_disk_device(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) &&
+ ext2fsP_is_disk_device(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;
+ int busy = 0;
+
+ if (getenv("EXT2FS_PRETEND_RO_MOUNT")) {
+ *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_READONLY;
+ if (getenv("EXT2FS_PRETEND_ROOTFS"))
+ *mount_flags = EXT2_MF_ISROOT;
+ return 0;
+ }
+ if (getenv("EXT2FS_PRETEND_RW_MOUNT")) {
+ *mount_flags = EXT2_MF_MOUNTED;
+ if (getenv("EXT2FS_PRETEND_ROOTFS"))
+ *mount_flags = EXT2_MF_ISROOT;
+ return 0;
+ }
+
+#ifdef __linux__ /* This only works on Linux 2.6+ systems */
+ {
+ struct stat st_buf;
+
+ if (stat(device, &st_buf) == 0 &&
+ ext2fsP_is_disk_device(st_buf.st_mode)) {
+ int fd = open(device, O_RDONLY | O_EXCL);
+
+ if (fd >= 0) {
+ /*
+ * The device is not busy so it's
+ * definitelly not mounted. No need to
+ * to perform any more checks.
+ */
+ close(fd);
+ *mount_flags = 0;
+ return 0;
+ } else if (errno == EBUSY) {
+ busy = 1;
+ }
+ }
+ }
+#endif
+
+ if (is_swap_device(device)) {
+ *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP;
+ if (mtpt)
+ strncpy(mtpt, "<swap>", mtlen);
+ } else {
+#ifdef HAVE_SETMNTENT
+ retval = check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+ retval = check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#if defined(__GNUC__) && !defined(_WIN32)
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+ *mount_flags = 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_SETMNTENT */
+ }
+ if (retval)
+ return retval;
+
+ if (busy)
+ *mount_flags |= EXT2_MF_BUSY;
+
+ 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/ext2fs/jfs_compat.h b/lib/ext2fs/jfs_compat.h
new file mode 100644
index 0000000..0e96b56
--- /dev/null
+++ b/lib/ext2fs/jfs_compat.h
@@ -0,0 +1,113 @@
+
+#ifndef _JFS_COMPAT_H
+#define _JFS_COMPAT_H
+
+#include "kernel-list.h"
+#include <errno.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+#include <stdbool.h>
+
+#define printk printf
+#define KERN_ERR ""
+#define KERN_DEBUG ""
+
+#define REQ_OP_READ 0
+#define REQ_OP_WRITE 1
+
+#define cpu_to_le16(x) ext2fs_cpu_to_le16(x)
+#define cpu_to_be16(x) ext2fs_cpu_to_be16(x)
+#define cpu_to_le32(x) ext2fs_cpu_to_le32(x)
+#define cpu_to_be32(x) ext2fs_cpu_to_be32(x)
+#define cpu_to_le64(x) ext2fs_cpu_to_le64(x)
+#define cpu_to_be64(x) ext2fs_cpu_to_be64(x)
+
+#define le16_to_cpu(x) ext2fs_le16_to_cpu(x)
+#define be16_to_cpu(x) ext2fs_be16_to_cpu(x)
+#define le32_to_cpu(x) ext2fs_le32_to_cpu(x)
+#define be32_to_cpu(x) ext2fs_be32_to_cpu(x)
+#define le64_to_cpu(x) ext2fs_le64_to_cpu(x)
+#define be64_to_cpu(x) ext2fs_be64_to_cpu(x)
+
+typedef unsigned int tid_t;
+typedef struct journal_s journal_t;
+typedef struct kdev_s *kdev_t;
+
+struct buffer_head;
+struct inode;
+
+typedef unsigned int gfp_t;
+#define GFP_KERNEL 0
+#define GFP_NOFS 0
+#define __GFP_NOFAIL 0
+#define JBD2_TAG_SIZE32 JBD_TAG_SIZE32
+#define JBD2_BARRIER 0
+typedef __u64 u64;
+#define put_bh(x) brelse(x)
+
+#define crc32_be(x, y, z) ext2fs_crc32_be((x), (y), (z))
+#define spin_lock_init(x)
+#define spin_lock(x)
+#define spin_unlock(x)
+#define SLAB_HWCACHE_ALIGN 0
+#define SLAB_TEMPORARY 0
+#define KMEM_CACHE(__struct, __flags) kmem_cache_create(#__struct,\
+ sizeof(struct __struct), __alignof__(struct __struct),\
+ (__flags), NULL)
+
+#define blkdev_issue_flush(kdev) sync_blockdev(kdev)
+#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
+#define pr_emerg(fmt)
+#define pr_err(...)
+
+enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
+
+#define JBD2_FC_REPLAY_STOP 0
+#define JBD2_FC_REPLAY_CONTINUE 1
+
+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_fc_first;
+ unsigned long j_fc_off;
+ unsigned long j_fc_last;
+ 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_total_len;
+ struct inode * j_inode;
+ tid_t j_tail_sequence;
+ tid_t j_transaction_sequence;
+ __u8 j_uuid[16];
+ struct jbd2_revoke_table_s *j_revoke;
+ struct jbd2_revoke_table_s *j_revoke_table[2];
+ tid_t j_failed_commit;
+ __u32 j_csum_seed;
+ int (*j_fc_replay_callback)(struct journal_s *journal,
+ struct buffer_head *bh,
+ enum passtype pass, int off,
+ tid_t expected_tid);
+
+};
+
+#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/ext2fs/kernel-jbd.h b/lib/ext2fs/kernel-jbd.h
new file mode 100644
index 0000000..e569500
--- /dev/null
+++ b/lib/ext2fs/kernel-jbd.h
@@ -0,0 +1,456 @@
+/*
+ * linux/include/linux/jbd.h
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>
+ *
+ * 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
+
+#include "jfs_compat.h"
+#define JFS_DEBUG
+#define jfs_debug jbd_debug
+
+#ifndef __GNUC__
+#define __FUNCTION__ ""
+#endif
+
+#define journal_oom_retry 0
+
+#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;
+#else
+#define journal_enable_debug (-1)
+#endif /* !CONFIG_JBD_DEBUG */
+
+#ifdef __STDC__
+#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
+#define jbd_debug(x) /* AIX doesn't do STDC */
+#endif /* !__STDC__ */
+
+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 JBD2_MIN_JOURNAL_BLOCKS 1024
+#define JBD2_DEFAULT_FAST_COMMIT_BLOCKS 256
+
+/*
+ * Internal structures used by the logging mechanism:
+ */
+
+#define JBD2_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * On-disk structures
+ */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JBD2_DESCRIPTOR_BLOCK 1
+#define JBD2_COMMIT_BLOCK 2
+#define JBD2_SUPERBLOCK_V1 3
+#define JBD2_SUPERBLOCK_V2 4
+#define JBD2_REVOKE_BLOCK 5
+#define JBD2_FC_BLOCK 6
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+ __be32 h_magic;
+ __be32 h_blocktype;
+ __be32 h_sequence;
+} journal_header_t;
+
+/*
+ * Checksum types.
+ */
+#define JBD2_CRC32_CHKSUM 1
+#define JBD2_MD5_CHKSUM 2
+#define JBD2_SHA1_CHKSUM 3
+#define JBD2_CRC32C_CHKSUM 4
+
+#define JBD2_CRC32_CHKSUM_SIZE 4
+
+#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32))
+/*
+ * Commit block header for storing transactional checksums:
+ *
+ * NOTE: If FEATURE_COMPAT_CHECKSUM (checksum v1) is set, the h_chksum*
+ * fields are used to store a checksum of the descriptor and data blocks.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V2 (checksum v2) is set, then the h_chksum
+ * field is used to store crc32c(uuid+commit_block). Each journal metadata
+ * block gets its own checksum, and data block checksums are stored in
+ * journal_block_tag (in the descriptor). The other h_chksum* fields are
+ * not used.
+ *
+ * If FEATURE_INCOMPAT_CSUM_V3 is set, the descriptor block uses
+ * journal_block_tag3_t to store a full 32-bit checksum. Everything else
+ * is the same as v2.
+ *
+ * Checksum v1, v2, and v3 are mutually exclusive features.
+ */
+struct commit_header {
+ __be32 h_magic;
+ __be32 h_blocktype;
+ __be32 h_sequence;
+ unsigned char h_chksum_type;
+ unsigned char h_chksum_size;
+ unsigned char h_padding[2];
+ __be32 h_chksum[JBD2_CHECKSUM_BYTES];
+ __be64 h_commit_sec;
+ __be32 h_commit_nsec;
+};
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+typedef struct journal_block_tag3_s
+{
+ __be32 t_blocknr; /* The on-disk block number */
+ __be32 t_flags; /* See below */
+ __be32 t_blocknr_high; /* most-significant high 32bits. */
+ __be32 t_checksum; /* crc32c(uuid+seq+block) */
+} journal_block_tag3_t;
+
+typedef struct journal_block_tag_s
+{
+ __be32 t_blocknr; /* The on-disk block number */
+ __be16 t_checksum; /* truncated crc32c(uuid+seq+block) */
+ __be16 t_flags; /* See below */
+ __be32 t_blocknr_high; /* most-significant high 32bits. */
+} journal_block_tag_t;
+
+/* Tail of descriptor or revoke block, for checksumming */
+struct jbd2_journal_block_tail {
+ __be32 t_checksum;
+};
+
+/*
+ * 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;
+ __be32 r_count; /* Count of bytes used in the block */
+} jbd2_journal_revoke_header_t;
+
+/* Definitions for the journal tag flags word: */
+#define JBD2_FLAG_ESCAPE 1 /* on-disk block is escaped */
+#define JBD2_FLAG_SAME_UUID 2 /* block has same uuid as previous */
+#define JBD2_FLAG_DELETED 4 /* block deleted by this transaction */
+#define JBD2_FLAG_LAST_TAG 8 /* last tag in this descriptor block */
+
+
+#define UUID_SIZE 16
+#define JBD2_USERS_MAX 48
+#define JBD2_USERS_SIZE (UUID_SIZE * JBD2_USERS_MAX)
+/*
+ * 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 */
+ __be32 s_blocksize; /* journal device blocksize */
+ __be32 s_maxlen; /* total blocks in journal file */
+ __be32 s_first; /* first block of log information */
+
+/* 0x0018 */
+ /* Dynamic information describing the current state of the log */
+ __be32 s_sequence; /* first commit ID expected in log */
+ __be32 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 */
+ __be32 s_feature_compat; /* compatible feature set */
+ __be32 s_feature_incompat; /* incompatible feature set */
+ __be32 s_feature_ro_compat; /* readonly-compatible feature set */
+/* 0x0030 */
+ __u8 s_uuid[16]; /* 128-bit uuid for journal */
+
+/* 0x0040 */
+ __be32 s_nr_users; /* Nr of filesystems sharing log */
+
+ __be32 s_dynsuper; /* Blocknr of dynamic superblock copy*/
+
+/* 0x0048 */
+ __be32 s_max_transaction; /* Limit of journal blocks per trans.*/
+ __be32 s_max_trans_data; /* Limit of data blocks per trans. */
+
+/* 0x0050 */
+ __u8 s_checksum_type; /* checksum type */
+ __u8 s_padding2[3];
+/* 0x0054 */
+ __be32 s_num_fc_blks; /* Number of fast commit blocks */
+/* 0x0058 */
+ __be32 s_padding[41];
+ __be32 s_checksum; /* crc32c(superblock) */
+
+/* 0x0100 */
+ __u8 s_users[JBD2_USERS_SIZE]; /* ids of all fs'es sharing the log */
+
+/* 0x0400 */
+} journal_superblock_t;
+
+#define JBD2_HAS_COMPAT_FEATURE(j,mask) \
+ ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_compat & ext2fs_cpu_to_be32((mask))))
+#define JBD2_HAS_RO_COMPAT_FEATURE(j,mask) \
+ ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_ro_compat & ext2fs_cpu_to_be32((mask))))
+#define JBD2_HAS_INCOMPAT_FEATURE(j,mask) \
+ ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_incompat & ext2fs_cpu_to_be32((mask))))
+
+#define JBD2_FEATURE_COMPAT_CHECKSUM 0x00000001
+
+#define JBD2_FEATURE_INCOMPAT_REVOKE 0x00000001
+#define JBD2_FEATURE_INCOMPAT_64BIT 0x00000002
+#define JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004
+#define JBD2_FEATURE_INCOMPAT_CSUM_V2 0x00000008
+#define JBD2_FEATURE_INCOMPAT_CSUM_V3 0x00000010
+#define JBD2_FEATURE_INCOMPAT_FAST_COMMIT 0x00000020
+
+/* Features known to this kernel version: */
+#define JBD2_KNOWN_COMPAT_FEATURES 0
+#define JBD2_KNOWN_ROCOMPAT_FEATURES 0
+#define JBD2_KNOWN_INCOMPAT_FEATURES (JBD2_FEATURE_INCOMPAT_REVOKE|\
+ JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT| \
+ JBD2_FEATURE_INCOMPAT_64BIT|\
+ JBD2_FEATURE_INCOMPAT_CSUM_V2| \
+ JBD2_FEATURE_INCOMPAT_CSUM_V3 | \
+ JBD2_FEATURE_INCOMPAT_FAST_COMMIT)
+
+#ifdef NO_INLINE_FUNCS
+extern size_t journal_tag_bytes(journal_t *journal);
+extern int jbd2_journal_has_csum_v2or3(journal_t *journal);
+extern int jbd2_journal_get_num_fc_blks(journal_superblock_t *jsb);
+extern int tid_gt(tid_t x, tid_t y) EXT2FS_ATTR((unused));
+extern int tid_geq(tid_t x, tid_t y) EXT2FS_ATTR((unused));
+#endif
+
+#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+#ifdef E2FSCK_INCLUDE_INLINE_FUNCS
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ extern inline
+#else
+#define _INLINE_ inline
+#endif
+#else /* !E2FSCK_INCLUDE_INLINE FUNCS */
+#if (__STDC_VERSION__ >= 199901L)
+#define _INLINE_ inline
+#else /* not C99 */
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else /* For Watcom C */
+#define _INLINE_ extern inline
+#endif /* __GNUC__ */
+#endif /* __STDC_VERSION__ >= 199901L */
+#endif /* INCLUDE_INLINE_FUNCS */
+
+/* journal feature predicate functions */
+#define JBD2_FEATURE_COMPAT_FUNCS(name, flagname) \
+_INLINE_ int jbd2_has_feature_##name(journal_t *j); \
+_INLINE_ int jbd2_has_feature_##name(journal_t *j) \
+{ \
+ return ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_compat & \
+ ext2fs_cpu_to_be32(JBD2_FEATURE_COMPAT_##flagname)) != 0); \
+} \
+_INLINE_ void jbd2_set_feature_##name(journal_t *j); \
+_INLINE_ void jbd2_set_feature_##name(journal_t *j) \
+{ \
+ (j)->j_superblock->s_feature_compat |= \
+ ext2fs_cpu_to_be32(JBD2_FEATURE_COMPAT_##flagname); \
+} \
+_INLINE_ void jbd2_clear_feature_##name(journal_t *j); \
+_INLINE_ void jbd2_clear_feature_##name(journal_t *j) \
+{ \
+ (j)->j_superblock->s_feature_compat &= \
+ ~ext2fs_cpu_to_be32(JBD2_FEATURE_COMPAT_##flagname); \
+}
+
+#define JBD2_FEATURE_RO_COMPAT_FUNCS(name, flagname) \
+_INLINE_ int jbd2_has_feature_##name(journal_t *j); \
+_INLINE_ int jbd2_has_feature_##name(journal_t *j) \
+{ \
+ return ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_ro_compat & \
+ ext2fs_cpu_to_be32(JBD2_FEATURE_RO_COMPAT_##flagname)) != 0); \
+} \
+_INLINE_ void jbd2_set_feature_##name(journal_t *j); \
+_INLINE_ void jbd2_set_feature_##name(journal_t *j) \
+{ \
+ (j)->j_superblock->s_feature_ro_compat |= \
+ ext2fs_cpu_to_be32(JBD2_FEATURE_RO_COMPAT_##flagname); \
+} \
+_INLINE_ void jbd2_clear_feature_##name(journal_t *j); \
+_INLINE_ void jbd2_clear_feature_##name(journal_t *j) \
+{ \
+ (j)->j_superblock->s_feature_ro_compat &= \
+ ~ext2fs_cpu_to_be32(JBD2_FEATURE_RO_COMPAT_##flagname); \
+}
+
+#define JBD2_FEATURE_INCOMPAT_FUNCS(name, flagname) \
+_INLINE_ int jbd2_has_feature_##name(journal_t *j); \
+_INLINE_ int jbd2_has_feature_##name(journal_t *j) \
+{ \
+ return ((j)->j_format_version >= 2 && \
+ ((j)->j_superblock->s_feature_incompat & \
+ ext2fs_cpu_to_be32(JBD2_FEATURE_INCOMPAT_##flagname)) != 0); \
+} \
+_INLINE_ void jbd2_set_feature_##name(journal_t *j); \
+_INLINE_ void jbd2_set_feature_##name(journal_t *j) \
+{ \
+ (j)->j_superblock->s_feature_incompat |= \
+ ext2fs_cpu_to_be32(JBD2_FEATURE_INCOMPAT_##flagname); \
+} \
+_INLINE_ void jbd2_clear_feature_##name(journal_t *j); \
+_INLINE_ void jbd2_clear_feature_##name(journal_t *j) \
+{ \
+ (j)->j_superblock->s_feature_incompat &= \
+ ~ext2fs_cpu_to_be32(JBD2_FEATURE_INCOMPAT_##flagname); \
+}
+
+#else
+#define JBD2_FEATURE_COMPAT_FUNCS(name, flagname) \
+extern int jbd2_has_feature_##name(journal_t *j); \
+extern void jbd2_set_feature_##name(journal_t *j); \
+extern void jbd2_clear_feature_##name(journal_t *j);
+
+#define JBD2_FEATURE_RO_COMPAT_FUNCS(name, flagname) \
+extern int jbd2_has_feature_##name(journal_t *j); \
+extern void jbd2_set_feature_##name(journal_t *j); \
+extern void jbd2_clear_feature_##name(journal_t *j);
+
+#define JBD2_FEATURE_INCOMPAT_FUNCS(name, flagname) \
+extern int jbd2_has_feature_##name(journal_t *j); \
+extern void jbd2_set_feature_##name(journal_t *j); \
+extern void jbd2_clear_feature_##name(journal_t *j);
+
+#endif /* (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) */
+
+JBD2_FEATURE_COMPAT_FUNCS(checksum, CHECKSUM)
+
+JBD2_FEATURE_INCOMPAT_FUNCS(revoke, REVOKE)
+JBD2_FEATURE_INCOMPAT_FUNCS(64bit, 64BIT)
+JBD2_FEATURE_INCOMPAT_FUNCS(async_commit, ASYNC_COMMIT)
+JBD2_FEATURE_INCOMPAT_FUNCS(csum2, CSUM_V2)
+JBD2_FEATURE_INCOMPAT_FUNCS(csum3, CSUM_V3)
+JBD2_FEATURE_INCOMPAT_FUNCS(fast_commit, FAST_COMMIT)
+
+#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+/*
+ * helper functions to deal with 32 or 64bit block numbers.
+ */
+_INLINE_ size_t journal_tag_bytes(journal_t *journal)
+{
+ size_t sz;
+
+ if (jbd2_has_feature_csum3(journal))
+ return sizeof(journal_block_tag3_t);
+
+ sz = sizeof(journal_block_tag_t);
+
+ if (jbd2_has_feature_csum2(journal))
+ sz += sizeof(__u16);
+
+ if (jbd2_has_feature_64bit(journal))
+ return sz;
+
+ return sz - sizeof(__u32);
+}
+
+_INLINE_ int jbd2_journal_has_csum_v2or3(journal_t *journal)
+{
+ if (jbd2_has_feature_csum2(journal) || jbd2_has_feature_csum3(journal))
+ return 1;
+
+ return 0;
+}
+
+_INLINE_ int jbd2_journal_get_num_fc_blks(journal_superblock_t *jsb)
+{
+ int num_fc_blocks = be32_to_cpu(jsb->s_num_fc_blks);
+
+ return num_fc_blocks ? num_fc_blocks : JBD2_DEFAULT_FAST_COMMIT_BLOCKS;
+}
+
+/* Comparison functions for transaction IDs: perform comparisons using
+ * modulo arithmetic so that they work over sequence number wraps. */
+
+_INLINE_ int tid_gt(tid_t x, tid_t y)
+{
+ int difference = (x - y);
+ return (difference > 0);
+}
+
+_INLINE_ int tid_geq(tid_t x, tid_t y)
+{
+ int difference = (x - y);
+ return (difference >= 0);
+}
+#endif /* (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) */
+
+#undef _INLINE_
+
+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 superseded 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);
+
+#endif /* _LINUX_JBD_H */
diff --git a/lib/ext2fs/kernel-list.h b/lib/ext2fs/kernel-list.h
new file mode 100644
index 0000000..dd7b8e0
--- /dev/null
+++ b/lib/ext2fs/kernel-list.h
@@ -0,0 +1,111 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include "compiler.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 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) \
+ container_of(ptr, type, member)
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#endif
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
new file mode 100644
index 0000000..d69b1e3
--- /dev/null
+++ b/lib/ext2fs/link.c
@@ -0,0 +1,646 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+#define EXT2_DX_ROOT_OFF 24
+
+struct dx_frame {
+ void *buf;
+ blk64_t pblock;
+ struct ext2_dx_countlimit *head;
+ struct ext2_dx_entry *entries;
+ struct ext2_dx_entry *at;
+};
+
+struct dx_lookup_info {
+ const char *name;
+ int namelen;
+ int hash_alg;
+ __u32 hash;
+ unsigned levels;
+ struct dx_frame frames[EXT4_HTREE_LEVEL];
+};
+
+static errcode_t alloc_dx_frame(ext2_filsys fs, struct dx_frame *frame)
+{
+ return ext2fs_get_mem(fs->blocksize, &frame->buf);
+}
+
+static void dx_release(struct dx_lookup_info *info)
+{
+ unsigned level;
+
+ for (level = 0; level < info->levels; level++) {
+ if (info->frames[level].buf == NULL)
+ break;
+ ext2fs_free_mem(&(info->frames[level].buf));
+ }
+ info->levels = 0;
+}
+
+static void dx_search_entry(struct dx_frame *frame, int count, __u32 hash)
+{
+ struct ext2_dx_entry *p, *q, *m;
+
+ p = frame->entries + 1;
+ q = frame->entries + count - 1;
+ while (p <= q) {
+ m = p + (q - p) / 2;
+ if (ext2fs_le32_to_cpu(m->hash) > hash)
+ q = m - 1;
+ else
+ p = m + 1;
+ }
+ frame->at = p - 1;
+}
+
+static errcode_t load_logical_dir_block(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *diri, blk64_t block,
+ blk64_t *pblk, void *buf)
+{
+ errcode_t errcode;
+ int ret_flags;
+
+ errcode = ext2fs_bmap2(fs, dir, diri, NULL, 0, block, &ret_flags,
+ pblk);
+ if (errcode)
+ return errcode;
+ if (ret_flags & BMAP_RET_UNINIT)
+ return EXT2_ET_DIR_CORRUPTED;
+ return ext2fs_read_dir_block4(fs, *pblk, buf, 0, dir);
+}
+
+static errcode_t dx_lookup(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *diri, struct dx_lookup_info *info)
+{
+ struct ext2_dx_root_info *root;
+ errcode_t errcode;
+ int level = 0;
+ int count, limit;
+ int hash_alg;
+ int hash_flags = diri->i_flags & EXT4_CASEFOLD_FL;
+ __u32 minor_hash;
+ struct dx_frame *frame;
+
+ errcode = alloc_dx_frame(fs, &(info->frames[0]));
+ if (errcode)
+ return errcode;
+ info->levels = 1;
+
+ errcode = load_logical_dir_block(fs, dir, diri, 0,
+ &(info->frames[0].pblock),
+ info->frames[0].buf);
+ if (errcode)
+ goto out_err;
+ root = (struct ext2_dx_root_info *) ((char *)info->frames[0].buf +
+ EXT2_DX_ROOT_OFF);
+ hash_alg = root->hash_version;
+ if (hash_alg != EXT2_HASH_TEA && hash_alg != EXT2_HASH_HALF_MD4 &&
+ hash_alg != EXT2_HASH_LEGACY) {
+ errcode = EXT2_ET_DIRHASH_UNSUPP;
+ goto out_err;
+ }
+ if (hash_alg <= EXT2_HASH_TEA &&
+ fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH)
+ hash_alg += 3;
+ if (root->indirect_levels >= ext2_dir_htree_level(fs)) {
+ errcode = EXT2_ET_DIR_CORRUPTED;
+ goto out_err;
+ }
+ info->hash_alg = hash_alg;
+
+ errcode = ext2fs_dirhash2(hash_alg, info->name, info->namelen,
+ fs->encoding, hash_flags,
+ fs->super->s_hash_seed, &info->hash,
+ &minor_hash);
+ if (errcode)
+ goto out_err;
+
+ for (level = 0; level <= root->indirect_levels; level++) {
+ frame = &(info->frames[level]);
+ if (level > 0) {
+ errcode = alloc_dx_frame(fs, frame);
+ if (errcode)
+ goto out_err;
+ info->levels++;
+
+ errcode = load_logical_dir_block(fs, dir, diri,
+ ext2fs_le32_to_cpu(info->frames[level-1].at->block) & 0x0fffffff,
+ &(frame->pblock), frame->buf);
+ if (errcode)
+ goto out_err;
+ }
+ errcode = ext2fs_get_dx_countlimit(fs, frame->buf,
+ &(frame->head), NULL);
+ if (errcode)
+ goto out_err;
+ count = ext2fs_le16_to_cpu(frame->head->count);
+ limit = ext2fs_le16_to_cpu(frame->head->limit);
+ frame->entries = (struct ext2_dx_entry *)(frame->head);
+ if (!count || count > limit) {
+ errcode = EXT2_ET_DIR_CORRUPTED;
+ goto out_err;
+ }
+
+ dx_search_entry(frame, count, info->hash);
+ }
+ return 0;
+out_err:
+ dx_release(info);
+ return errcode;
+}
+
+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(ext2_ino_t dir EXT2FS_ATTR((unused)),
+ int entru EXT2FS_ATTR((unused)),
+ 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;
+ int csum_size = 0;
+
+ if (ls->done)
+ return DIRENT_ABORT;
+
+ 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;
+
+ if (ext2fs_has_feature_metadata_csum(ls->fs->super))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+ /*
+ * 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 + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
+ (next->inode == 0) &&
+ (offset + (int) curr_rec_len + (int) 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(ext2fs_dirent_name_len(dirent));
+ 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;
+ ext2fs_dirent_set_name_len(next, 0);
+ ext2fs_dirent_set_file_type(next, 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;
+ ext2fs_dirent_set_name_len(dirent, ls->namelen);
+ strncpy(dirent->name, ls->name, ls->namelen);
+ if (ext2fs_has_feature_filetype(ls->sb))
+ ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7);
+
+ ls->done++;
+ return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+static errcode_t add_dirent_to_buf(ext2_filsys fs, e2_blkcnt_t blockcnt,
+ char *buf, ext2_ino_t dir,
+ struct ext2_inode *diri, const char *name,
+ ext2_ino_t ino, int flags, blk64_t *pblkp)
+{
+ struct dir_context ctx;
+ struct link_struct ls;
+ errcode_t retval;
+
+ retval = load_logical_dir_block(fs, dir, diri, blockcnt, pblkp, buf);
+ if (retval)
+ return retval;
+ ctx.errcode = 0;
+ ctx.func = link_proc;
+ ctx.dir = dir;
+ ctx.flags = DIRENT_FLAG_INCLUDE_EMPTY;
+ ctx.buf = buf;
+ ctx.priv_data = &ls;
+
+ ls.fs = fs;
+ ls.name = name;
+ ls.namelen = strlen(name);
+ ls.inode = ino;
+ ls.flags = flags;
+ ls.done = 0;
+ ls.sb = fs->super;
+ ls.blocksize = fs->blocksize;
+ ls.err = 0;
+
+ ext2fs_process_dir_block(fs, pblkp, blockcnt, 0, 0, &ctx);
+ if (ctx.errcode)
+ return ctx.errcode;
+ if (ls.err)
+ return ls.err;
+ if (!ls.done)
+ return EXT2_ET_DIR_NO_SPACE;
+ return 0;
+}
+
+struct dx_hash_map {
+ __u32 hash;
+ int size;
+ int off;
+};
+
+static EXT2_QSORT_TYPE dx_hash_map_cmp(const void *ap, const void *bp)
+{
+ const struct dx_hash_map *a = ap, *b = bp;
+
+ if (a->hash < b->hash)
+ return -1;
+ if (a->hash > b->hash)
+ return 1;
+ return 0;
+}
+
+static errcode_t dx_move_dirents(ext2_filsys fs, struct dx_hash_map *map,
+ int count, void *from, void *to)
+{
+ struct ext2_dir_entry *de;
+ int i;
+ int rec_len = 0;
+ errcode_t retval;
+ int csum_size = 0;
+ void *base = to;
+
+ if (ext2fs_has_feature_metadata_csum(fs->super))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ for (i = 0; i < count; i++) {
+ de = (struct ext2_dir_entry *) ((char *)from + map[i].off);
+ rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(de));
+ memcpy(to, de, rec_len);
+ retval = ext2fs_set_rec_len(fs, rec_len, to);
+ if (retval)
+ return retval;
+ to = (char *)to + rec_len;
+ }
+ /*
+ * Update rec_len of the last dir entry to stretch to the end of block
+ */
+ to = (char *)to - rec_len;
+ rec_len = fs->blocksize - ((char *)to - (char *)base) - csum_size;
+ retval = ext2fs_set_rec_len(fs, rec_len, to);
+ if (retval)
+ return retval;
+ if (csum_size)
+ ext2fs_initialize_dirent_tail(fs,
+ EXT2_DIRENT_TAIL(base, fs->blocksize));
+ return 0;
+}
+
+static errcode_t dx_insert_entry(ext2_filsys fs, ext2_ino_t dir,
+ struct dx_lookup_info *info, int level,
+ __u32 hash, blk64_t lblk)
+{
+ int pcount;
+ struct ext2_dx_entry *top, *new;
+
+ pcount = ext2fs_le16_to_cpu(info->frames[level].head->count);
+ top = info->frames[level].entries + pcount;
+ new = info->frames[level].at + 1;
+ memmove(new + 1, new, (char *)top - (char *)new);
+ new->hash = ext2fs_cpu_to_le32(hash);
+ new->block = ext2fs_cpu_to_le32(lblk);
+ info->frames[level].head->count = ext2fs_cpu_to_le16(pcount + 1);
+ return ext2fs_write_dir_block4(fs, info->frames[level].pblock,
+ info->frames[level].buf, 0, dir);
+}
+
+static errcode_t dx_split_leaf(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *diri,
+ struct dx_lookup_info *info, void *buf,
+ blk64_t leaf_pblk, blk64_t new_lblk,
+ blk64_t new_pblk)
+{
+ int hash_flags = diri->i_flags & EXT4_CASEFOLD_FL;
+ struct ext2_dir_entry *de;
+ void *buf2;
+ errcode_t retval = 0;
+ unsigned int rec_len;
+ unsigned int offset, move_size;
+ int i, count = 0;
+ struct dx_hash_map *map;
+ int continued;
+ __u32 minor_hash;
+
+ retval = ext2fs_get_mem(fs->blocksize, &buf2);
+ if (retval)
+ return retval;
+ retval = ext2fs_get_array(fs->blocksize / 12,
+ sizeof(struct dx_hash_map), &map);
+ if (retval) {
+ ext2fs_free_mem(&buf2);
+ return retval;
+ }
+ for (offset = 0; offset < fs->blocksize; offset += rec_len) {
+ de = (struct ext2_dir_entry *) ((char *)buf + offset);
+ retval = ext2fs_get_rec_len(fs, de, &rec_len);
+ if (retval)
+ goto out;
+ if (ext2fs_dirent_name_len(de) > 0 && de->inode) {
+ map[count].off = offset;
+ map[count].size = rec_len;
+ retval = ext2fs_dirhash2(info->hash_alg, de->name,
+ ext2fs_dirent_name_len(de),
+ fs->encoding, hash_flags,
+ fs->super->s_hash_seed,
+ &(map[count].hash),
+ &minor_hash);
+ if (retval)
+ goto out;
+ count++;
+ }
+ }
+ qsort(map, count, sizeof(struct dx_hash_map), dx_hash_map_cmp);
+ move_size = 0;
+ /* Find place to split block */
+ for (i = count - 1; i >= 0; i--) {
+ if (move_size + map[i].size / 2 > fs->blocksize / 2)
+ break;
+ move_size += map[i].size;
+ }
+ /* Let i be the first entry to move */
+ i++;
+ /* Move selected directory entries to new block */
+ retval = dx_move_dirents(fs, map + i, count - i, buf, buf2);
+ if (retval)
+ goto out;
+ retval = ext2fs_write_dir_block4(fs, new_pblk, buf2, 0, dir);
+ if (retval)
+ goto out;
+ /* Repack remaining entries in the old block */
+ retval = dx_move_dirents(fs, map, i, buf, buf2);
+ if (retval)
+ goto out;
+ retval = ext2fs_write_dir_block4(fs, leaf_pblk, buf2, 0, dir);
+ if (retval)
+ goto out;
+ /* Update parent node */
+ continued = map[i].hash == map[i-1].hash;
+ retval = dx_insert_entry(fs, dir, info, info->levels - 1,
+ map[i].hash + continued, new_lblk);
+out:
+ ext2fs_free_mem(&buf2);
+ ext2fs_free_mem(&map);
+ return retval;
+}
+
+static errcode_t dx_grow_tree(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *diri,
+ struct dx_lookup_info *info, void *buf,
+ blk64_t leaf_pblk)
+{
+ int i;
+ errcode_t retval;
+ ext2_off64_t size = EXT2_I_SIZE(diri);
+ blk64_t lblk, pblk;
+ struct ext2_dir_entry *de;
+ struct ext2_dx_countlimit *head;
+ int csum_size = 0;
+ int count;
+
+ if (ext2fs_has_feature_metadata_csum(fs->super))
+ csum_size = sizeof(struct ext2_dx_tail);
+
+ /* Find level which can accommodate new child */
+ for (i = info->levels - 1; i >= 0; i--)
+ if (ext2fs_le16_to_cpu(info->frames[i].head->count) <
+ ext2fs_le16_to_cpu(info->frames[i].head->limit))
+ break;
+ /* Need to grow tree depth? */
+ if (i < 0 && info->levels >= ext2_dir_htree_level(fs))
+ return EXT2_ET_DIR_NO_SPACE;
+ lblk = size / fs->blocksize;
+ size += fs->blocksize;
+ retval = ext2fs_inode_size_set(fs, diri, size);
+ if (retval)
+ return retval;
+ retval = ext2fs_fallocate(fs,
+ EXT2_FALLOCATE_FORCE_INIT | EXT2_FALLOCATE_ZERO_BLOCKS,
+ dir, diri, 0, lblk, 1);
+ if (retval)
+ return retval;
+ retval = ext2fs_write_inode(fs, dir, diri);
+ if (retval)
+ return retval;
+ retval = ext2fs_bmap2(fs, dir, diri, NULL, 0, lblk, NULL, &pblk);
+ if (retval)
+ return retval;
+ /* Only leaf addition needed? */
+ if (i == (int)info->levels - 1)
+ return dx_split_leaf(fs, dir, diri, info, buf, leaf_pblk,
+ lblk, pblk);
+
+ de = buf;
+ de->inode = 0;
+ ext2fs_dirent_set_name_len(de, 0);
+ ext2fs_dirent_set_file_type(de, 0);
+ retval = ext2fs_set_rec_len(fs, fs->blocksize, de);
+ if (retval)
+ return retval;
+ head = (struct ext2_dx_countlimit *) ((char *)buf + 8);
+ count = ext2fs_le16_to_cpu(info->frames[i+1].head->count);
+ /* Growing tree depth? */
+ if (i < 0) {
+ struct ext2_dx_root_info *root;
+
+ memcpy(head, info->frames[0].entries,
+ count * sizeof(struct ext2_dx_entry));
+ head->limit = ext2fs_cpu_to_le16(
+ (fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry));
+ /* head->count gets set by memcpy above to correct value */
+
+ /* Now update tree root */
+ info->frames[0].head->count = ext2fs_cpu_to_le16(1);
+ info->frames[0].entries[0].block = ext2fs_cpu_to_le32(lblk);
+ root = (struct ext2_dx_root_info *)
+ ((char *)info->frames[0].buf + EXT2_DX_ROOT_OFF);
+ root->indirect_levels++;
+ } else {
+ /* Splitting internal node in two */
+ int count1 = count / 2;
+ int count2 = count - count1;
+ __u32 split_hash = ext2fs_le32_to_cpu(info->frames[i+1].entries[count1].hash);
+
+ memcpy(head, info->frames[i+1].entries + count1,
+ count2 * sizeof(struct ext2_dx_entry));
+ head->count = ext2fs_cpu_to_le16(count2);
+ head->limit = ext2fs_cpu_to_le16(
+ (fs->blocksize - (8 + csum_size)) /
+ sizeof(struct ext2_dx_entry));
+ info->frames[i+1].head->count = ext2fs_cpu_to_le16(count1);
+
+ /* Update parent node */
+ retval = dx_insert_entry(fs, dir, info, i, split_hash, lblk);
+ if (retval)
+ return retval;
+
+ }
+ /* Writeout split block / updated root */
+ retval = ext2fs_write_dir_block4(fs, info->frames[i+1].pblock,
+ info->frames[i+1].buf, 0, dir);
+ if (retval)
+ return retval;
+ /* Writeout new tree block */
+ retval = ext2fs_write_dir_block4(fs, pblk, buf, 0, dir);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+static errcode_t dx_link(ext2_filsys fs, ext2_ino_t dir,
+ struct ext2_inode *diri, const char *name,
+ ext2_ino_t ino, int flags)
+{
+ struct dx_lookup_info dx_info;
+ errcode_t retval;
+ void *blockbuf;
+ unsigned restart = 0;
+ blk64_t leaf_pblk;
+
+ retval = ext2fs_get_mem(fs->blocksize, &blockbuf);
+ if (retval)
+ return retval;
+
+ dx_info.name = name;
+ dx_info.namelen = strlen(name);
+again:
+ retval = dx_lookup(fs, dir, diri, &dx_info);
+ if (retval)
+ goto free_buf;
+
+ retval = add_dirent_to_buf(fs,
+ ext2fs_le32_to_cpu(dx_info.frames[dx_info.levels-1].at->block) & 0x0fffffff,
+ blockbuf, dir, diri, name, ino, flags, &leaf_pblk);
+ /*
+ * Success or error other than ENOSPC...? We are done. We may need upto
+ * two tries to add entry. One to split htree node and another to add
+ * new leaf block.
+ */
+ if (restart >= dx_info.levels || retval != EXT2_ET_DIR_NO_SPACE)
+ goto free_frames;
+ retval = dx_grow_tree(fs, dir, diri, &dx_info, blockbuf, leaf_pblk);
+ if (retval)
+ goto free_frames;
+ /* Restart everything now that the tree is larger */
+ restart++;
+ dx_release(&dx_info);
+ goto again;
+free_frames:
+ dx_release(&dx_info);
+free_buf:
+ ext2fs_free_mem(&blockbuf);
+ return retval;
+}
+
+/*
+ * 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;
+
+ if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
+ return retval;
+
+ if (inode.i_flags & EXT2_INDEX_FL)
+ return dx_link(fs, dir, &inode, name, ino, flags);
+
+ 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_iterate2(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
+ NULL, link_proc, &ls);
+ if (retval)
+ return retval;
+ if (ls.err)
+ return ls.err;
+
+ if (!ls.done)
+ return EXT2_ET_DIR_NO_SPACE;
+ return 0;
+}
diff --git a/lib/ext2fs/llseek.c b/lib/ext2fs/llseek.c
new file mode 100644
index 0000000..45f21d0
--- /dev/null
+++ b/lib/ext2fs/llseek.c
@@ -0,0 +1,145 @@
+/*
+ * 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%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+#include "et/com_err.h"
+#include "ext2fs/ext2_io.h"
+
+#ifdef __linux__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#else
+#if defined(HAVE_LLSEEK)
+#include <sys/syscall.h>
+
+#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 || _FILE_OFFSET_BITS+0 == 64
+
+#define my_llseek lseek
+
+#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */
+
+#include <linux/unistd.h>
+
+#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 /* SIZE_LONG == SIZEOF_LONG_LONG */
+
+#endif /* HAVE_LLSEEK */
+#endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */
+
+ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
+{
+#if SIZEOF_OFF_T >= SIZEOF_LONG_LONG
+ return my_llseek (fd, offset, origin);
+#else
+ ext2_loff_t result;
+ static int do_compat = 0;
+
+ if (do_compat)
+ goto fallback;
+
+ 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++;
+ fallback:
+ if (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))
+ return lseek(fd, (off_t) offset, origin);
+ errno = EINVAL;
+ return -1;
+ }
+ return result;
+#endif
+}
+
+#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/ext2fs/lookup.c b/lib/ext2fs/lookup.c
new file mode 100644
index 0000000..c1d802c
--- /dev/null
+++ b/lib/ext2fs/lookup.c
@@ -0,0 +1,70 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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 != ext2fs_dirent_name_len(dirent))
+ return 0;
+ if (strncmp(ls->name, dirent->name, ext2fs_dirent_name_len(dirent)))
+ 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/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
new file mode 100644
index 0000000..437c8ff
--- /dev/null
+++ b/lib/ext2fs/mkdir.c
@@ -0,0 +1,200 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.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)
+{
+ ext2_extent_handle_t handle;
+ 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;
+ int inline_data = 0;
+ int drop_refcount = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /*
+ * Create a new dir with inline data iff this feature is enabled
+ * and ino >= EXT2_FIRST_INO.
+ */
+ if ((!ino || ino >= EXT2_FIRST_INO(fs->super)) &&
+ ext2fs_has_feature_inline_data(fs->super))
+ inline_data = 1;
+
+ /*
+ * 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
+ */
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ if (!inline_data) {
+ retval = ext2fs_new_block2(fs, ext2fs_find_inode_goal(fs, ino,
+ &inode,
+ 0),
+ NULL, &blk);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Create a scratch template for the directory
+ */
+ if (inline_data)
+ retval = ext2fs_new_dir_inline_data(fs, ino, parent,
+ inode.i_block);
+ else
+ 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....
+ */
+ inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
+ inode.i_uid = inode.i_gid = 0;
+ if (inline_data) {
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
+ } else {
+ if (ext2fs_has_feature_extents(fs->super))
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ else
+ inode.i_block[0] = blk;
+ inode.i_size = fs->blocksize;
+ ext2fs_iblk_set(fs, &inode, 1);
+ }
+ inode.i_links_count = 2;
+
+ /*
+ * Write out the inode and inode data block. The inode generation
+ * number is assigned by write_new_inode, which means that the call
+ * to write_dir_block must come after that.
+ */
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+ if (inline_data) {
+ /* init "system.data" for new dir */
+ retval = ext2fs_inline_data_init(fs, ino);
+ } else {
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
+ if (retval)
+ goto cleanup;
+
+ if (ext2fs_has_feature_extents(fs->super)) {
+ retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
+ ext2fs_extent_free(handle);
+ if (retval)
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Update accounting....
+ */
+ if (!inline_data)
+ ext2fs_block_alloc_stats2(fs, blk, +1);
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
+ drop_refcount = 1;
+
+ /*
+ * 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) {
+ /* reload parent inode due to inline data */
+ retval = ext2fs_read_inode(fs, parent, &parent_inode);
+ if (retval)
+ goto cleanup;
+ parent_inode.i_links_count++;
+ retval = ext2fs_write_inode(fs, parent, &parent_inode);
+ if (retval)
+ goto cleanup;
+ }
+ drop_refcount = 0;
+
+cleanup:
+ if (block)
+ ext2fs_free_mem(&block);
+ if (drop_refcount) {
+ if (!inline_data)
+ ext2fs_block_alloc_stats2(fs, blk, -1);
+ ext2fs_inode_alloc_stats2(fs, ino, -1, 1);
+ }
+ return retval;
+
+}
+
+
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
new file mode 100644
index 0000000..54772dd
--- /dev/null
+++ b/lib/ext2fs/mkjournal.c
@@ -0,0 +1,654 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "ext2_fs.h"
+#include "e2p/e2p.h"
+#include "ext2fs.h"
+
+#include "kernel-jbd.h"
+
+/*
+ * This function automatically sets up the journal superblock and
+ * returns it as an allocated block.
+ */
+errcode_t ext2fs_create_journal_superblock2(ext2_filsys fs,
+ struct ext2fs_journal_params *jparams,
+ int flags, char **ret_jsb)
+{
+ errcode_t retval;
+ journal_superblock_t *jsb;
+
+ if (jparams->num_journal_blocks < JBD2_MIN_JOURNAL_BLOCKS)
+ 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(JBD2_MAGIC_NUMBER);
+ if (flags & EXT2_MKJOURNAL_V1_SUPER)
+ jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V1);
+ else
+ jsb->s_header.h_blocktype = htonl(JBD2_SUPERBLOCK_V2);
+ jsb->s_blocksize = htonl(fs->blocksize);
+ jsb->s_maxlen = htonl(jparams->num_journal_blocks + jparams->num_fc_blocks);
+ jsb->s_nr_users = htonl(1);
+ jsb->s_first = htonl(1);
+ jsb->s_sequence = htonl(1);
+ jsb->s_num_fc_blks = htonl(jparams->num_fc_blocks);
+ 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 (ext2fs_has_feature_journal_dev(fs->super)) {
+ jsb->s_nr_users = 0;
+ jsb->s_first = htonl(ext2fs_journal_sb_start(fs->blocksize) + 1);
+ }
+
+ *ret_jsb = (char *) jsb;
+ return 0;
+}
+
+errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 num_blocks,
+ int flags, char **ret_sb)
+{
+ struct ext2fs_journal_params jparams;
+
+ jparams.num_journal_blocks = num_blocks;
+ jparams.num_fc_blocks = 0;
+
+ return ext2fs_create_journal_superblock2(fs, &jparams, flags, ret_sb);
+}
+
+/*
+ * 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,
+ struct ext2fs_journal_params *jparams,
+ int flags)
+{
+ errcode_t retval;
+ char *buf = 0;
+ int fd, ret_size;
+ blk_t i;
+
+ if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
+ &buf)))
+ return retval;
+
+ /* Open the device or journal file */
+ if ((fd = open(filename, O_WRONLY)) < 0) {
+ retval = errno;
+ goto errfree;
+ }
+
+ /* 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);
+
+ if (flags & EXT2_MKJOURNAL_LAZYINIT)
+ goto success;
+
+ for (i = 1; i < jparams->num_journal_blocks + jparams->num_fc_blocks; i++) {
+ ret_size = write(fd, buf, fs->blocksize);
+ if (ret_size < 0) {
+ retval = errno;
+ goto errout;
+ }
+ if (ret_size != (int) fs->blocksize)
+ goto errout;
+ }
+
+success:
+ retval = 0;
+errout:
+ close(fd);
+errfree:
+ 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 MAX_STRIDE_LENGTH (4194304 / (int) fs->blocksize)
+errcode_t ext2fs_zero_blocks2(ext2_filsys fs, blk64_t blk, int num,
+ blk64_t *ret_blk, int *ret_count)
+{
+ int j, count;
+ static void *buf;
+ static int stride_length;
+ errcode_t retval;
+
+ /* If fs is null, clean up the static buffer and return */
+ if (!fs) {
+ if (buf) {
+ free(buf);
+ buf = 0;
+ stride_length = 0;
+ }
+ return 0;
+ }
+
+ /* Deal with zeroing less than 1 block */
+ if (num <= 0)
+ return 0;
+
+ /* Try a zero out command, if supported */
+ retval = io_channel_zeroout(fs->io, blk, num);
+ if (retval == 0)
+ return 0;
+
+ /* Allocate the zeroizing buffer if necessary */
+ if (num > stride_length && stride_length < MAX_STRIDE_LENGTH) {
+ void *p;
+ int new_stride = num;
+
+ if (new_stride > MAX_STRIDE_LENGTH)
+ new_stride = MAX_STRIDE_LENGTH;
+ p = realloc(buf, fs->blocksize * new_stride);
+ if (!p)
+ return EXT2_ET_NO_MEMORY;
+ buf = p;
+ stride_length = new_stride;
+ 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;
+}
+
+/*
+ * Calculate the initial goal block to be roughly at the middle of the
+ * filesystem. Pick a group that has the largest number of free
+ * blocks.
+ */
+static blk64_t get_midpoint_journal_block(ext2_filsys fs)
+{
+ dgrp_t group, start, end, i, log_flex;
+
+ group = ext2fs_group_of_blk2(fs, (ext2fs_blocks_count(fs->super) -
+ fs->super->s_first_data_block) / 2);
+ log_flex = 1U << 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;
+ return ext2fs_group_first_block2(fs, group);
+}
+
+/*
+ * This function creates a journal using direct I/O routines.
+ */
+static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
+ struct ext2fs_journal_params *jparams,
+ blk64_t goal, int flags)
+{
+ char *buf;
+ errcode_t retval;
+ struct ext2_inode inode;
+ unsigned long long inode_size;
+ int falloc_flags = EXT2_FALLOCATE_FORCE_INIT;
+ blk64_t zblk;
+
+ if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
+ &buf)))
+ return retval;
+
+ if ((retval = ext2fs_read_bitmaps(fs)))
+ goto out2;
+
+ if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+ goto out2;
+
+ if (inode.i_blocks > 0) {
+ retval = EEXIST;
+ goto out2;
+ }
+
+ if (goal == ~0ULL)
+ goal = get_midpoint_journal_block(fs);
+
+ if (ext2fs_has_feature_extents(fs->super))
+ inode.i_flags |= EXT4_EXTENTS_FL;
+
+ if (!(flags & EXT2_MKJOURNAL_LAZYINIT))
+ falloc_flags |= EXT2_FALLOCATE_ZERO_BLOCKS;
+
+ inode_size = (unsigned long long)fs->blocksize *
+ (jparams->num_journal_blocks + jparams->num_fc_blocks);
+ inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0);
+ inode.i_links_count = 1;
+ inode.i_mode = LINUX_S_IFREG | 0600;
+ retval = ext2fs_inode_size_set(fs, &inode, inode_size);
+ if (retval)
+ goto out2;
+
+ retval = ext2fs_fallocate(fs, falloc_flags, journal_ino,
+ &inode, goal, 0,
+ jparams->num_journal_blocks + jparams->num_fc_blocks);
+ if (retval)
+ goto out2;
+
+ if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode)))
+ goto out2;
+
+ retval = ext2fs_bmap2(fs, journal_ino, &inode, NULL, 0, 0, NULL, &zblk);
+ if (retval)
+ goto out2;
+
+ retval = io_channel_write_blk64(fs->io, zblk, 1, buf);
+ if (retval)
+ goto out2;
+
+ memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
+ fs->super->s_jnl_blocks[15] = inode.i_size_high;
+ fs->super->s_jnl_blocks[16] = inode.i_size;
+ fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+ ext2fs_mark_super_dirty(fs);
+
+out2:
+ 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.
+ *
+ * n.b. comments assume 4k blocks
+ */
+int ext2fs_default_journal_size(__u64 num_blocks)
+{
+ if (num_blocks < 2048)
+ return -1;
+ if (num_blocks < 32768) /* 128 MB */
+ return (1024); /* 4 MB */
+ if (num_blocks < 256*1024) /* 1 GB */
+ return (4096); /* 16 MB */
+ if (num_blocks < 512*1024) /* 2 GB */
+ return (8192); /* 32 MB */
+ if (num_blocks < 4096*1024) /* 16 GB */
+ return (16384); /* 64 MB */
+ if (num_blocks < 8192*1024) /* 32 GB */
+ return (32768); /* 128 MB */
+ if (num_blocks < 16384*1024) /* 64 GB */
+ return (65536); /* 256 MB */
+ if (num_blocks < 32768*1024) /* 128 GB */
+ return (131072); /* 512 MB */
+ return 262144; /* 1 GB */
+}
+
+errcode_t ext2fs_get_journal_params(struct ext2fs_journal_params *params,
+ ext2_filsys fs)
+{
+ blk_t total_blks;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ if (ext2fs_has_feature_journal_dev(fs->super)) {
+ total_blks = ext2fs_blocks_count(fs->super);
+ if (total_blks < JBD2_MIN_JOURNAL_BLOCKS)
+ return EXT2_ET_JOURNAL_TOO_SMALL;
+
+ if (!ext2fs_has_feature_fast_commit(fs->super)) {
+ params->num_journal_blocks = total_blks;
+ params->num_fc_blocks = 0;
+ return 0;
+ }
+ params->num_journal_blocks = ext2fs_blocks_count(fs->super) *
+ EXT2_JOURNAL_TO_FC_BLKS_RATIO /
+ (EXT2_JOURNAL_TO_FC_BLKS_RATIO + 1);
+ if (JBD2_MIN_JOURNAL_BLOCKS > params->num_journal_blocks)
+ params->num_journal_blocks = JBD2_MIN_JOURNAL_BLOCKS;
+ params->num_fc_blocks = total_blks - params->num_journal_blocks;
+ return 0;
+ }
+
+ ret = ext2fs_default_journal_size(ext2fs_blocks_count(fs->super));
+ if (ret < 0)
+ return EXT2_ET_JOURNAL_TOO_SMALL;
+
+ params->num_journal_blocks = ret;
+ if (ext2fs_has_feature_fast_commit(fs->super))
+ params->num_fc_blocks = params->num_journal_blocks /
+ EXT2_JOURNAL_TO_FC_BLKS_RATIO;
+ return 0;
+}
+
+int ext2fs_journal_sb_start(int blocksize)
+{
+ if (blocksize == EXT2_MIN_BLOCK_SIZE)
+ return 2;
+ return 1;
+}
+
+/*
+ * 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[SUPERBLOCK_SIZE];
+ 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 = ext2fs_journal_sb_start(journal_dev->blocksize);
+ if ((retval = io_channel_read_blk64(journal_dev->io, start,
+ -SUPERBLOCK_SIZE,
+ buf)))
+ return retval;
+
+ jsb = (journal_superblock_t *) buf;
+ if ((jsb->s_header.h_magic != (unsigned) ntohl(JBD2_MAGIC_NUMBER)) ||
+ (jsb->s_header.h_blocktype != (unsigned) ntohl(JBD2_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);
+ if (nr_users > JBD2_USERS_MAX)
+ return EXT2_ET_CORRUPT_JOURNAL_SB;
+ 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,
+ -SUPERBLOCK_SIZE, 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));
+ memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
+ ext2fs_set_feature_journal(fs->super);
+ 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_inode3(ext2_filsys fs, struct ext2fs_journal_params *jparams,
+ blk64_t goal, int flags)
+{
+ errcode_t retval;
+ ext2_ino_t journal_ino;
+ struct stat st;
+ char jfile[1024];
+ int mount_flags;
+ int fd = -1;
+
+ if (flags & EXT2_MKJOURNAL_NO_MNT_CHECK)
+ mount_flags = 0;
+ else if ((retval = ext2fs_check_mount_point(fs->device_name,
+ &mount_flags,
+ jfile, sizeof(jfile)-10)))
+ return retval;
+
+ if (mount_flags & EXT2_MF_MOUNTED) {
+#if HAVE_EXT2_IOCTLS
+ int f = 0;
+#endif
+ 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) {
+ retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+ close(fd);
+ if (retval)
+ return errno;
+ }
+#endif
+#endif
+
+ /* Create the journal file */
+ if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
+ return errno;
+
+ /* Note that we can't do lazy journal initialization for mounted
+ * filesystems, since the zero writing is also allocating the
+ * journal blocks. We could use fallocate, but not all kernels
+ * support that, and creating a journal on a mounted ext2
+ * filesystems is extremely rare these days... Ignore it. */
+ flags &= ~EXT2_MKJOURNAL_LAZYINIT;
+
+ if ((retval = write_journal_file(fs, jfile, jparams, 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;
+ memset(fs->super->s_jnl_blocks, 0,
+ sizeof(fs->super->s_jnl_blocks));
+ } 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,
+ jparams, goal, 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));
+ ext2fs_set_feature_journal(fs->super);
+
+ ext2fs_mark_super_dirty(fs);
+ return 0;
+errout:
+ if (fd >= 0)
+ close(fd);
+ return retval;
+}
+
+errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks,
+ blk64_t goal, int flags)
+{
+ struct ext2fs_journal_params jparams;
+
+ jparams.num_journal_blocks = num_blocks;
+ jparams.num_fc_blocks = 0;
+
+ return ext2fs_add_journal_inode3(fs, &jparams, goal, flags);
+}
+
+errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t num_blocks, int flags)
+{
+ return ext2fs_add_journal_inode2(fs, num_blocks, ~0ULL, flags);
+}
+
+
+#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, JBD2_MIN_JOURNAL_BLOCKS, 0);
+ 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_free(&fs);
+ exit(0);
+
+}
+#endif
diff --git a/lib/ext2fs/mmp.c b/lib/ext2fs/mmp.c
new file mode 100644
index 0000000..eb94170
--- /dev/null
+++ b/lib/ext2fs/mmp.c
@@ -0,0 +1,488 @@
+/*
+ * Helper functions for multiple mount protection (MMP).
+ *
+ * Copyright (C) 2011 Whamcloud, Inc.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */
+#endif
+
+#include "config.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic push
+#ifndef CONFIG_MMP
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#endif
+
+errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
+{
+#ifdef CONFIG_MMP
+ struct mmp_struct *mmp_cmp;
+ errcode_t retval = 0;
+
+ if ((mmp_blk <= fs->super->s_first_data_block) ||
+ (mmp_blk >= ext2fs_blocks_count(fs->super)))
+ return EXT2_ET_MMP_BAD_BLOCK;
+
+ /* ext2fs_open() reserves fd0,1,2 to avoid stdio collision, so checking
+ * mmp_fd <= 0 is OK to validate that the fd is valid. This opens its
+ * own fd to read the MMP block to ensure that it is using O_DIRECT,
+ * regardless of how the io_manager is doing reads, to avoid caching of
+ * the MMP block by the io_manager or the VM. It needs to be fresh. */
+ if (fs->mmp_fd <= 0) {
+ struct stat st;
+ int flags = O_RDONLY | O_DIRECT;
+
+ /*
+ * There is no reason for using O_DIRECT if we're working with
+ * regular file. Disabling it also avoids problems with
+ * alignment when the device of the host file system has sector
+ * size larger than blocksize of the fs we're working with.
+ */
+ if (stat(fs->device_name, &st) == 0 &&
+ S_ISREG(st.st_mode))
+ flags &= ~O_DIRECT;
+
+ fs->mmp_fd = open(fs->device_name, flags);
+ if (fs->mmp_fd < 0) {
+ retval = EXT2_ET_MMP_OPEN_DIRECT;
+ goto out;
+ }
+ }
+
+ if (fs->mmp_cmp == NULL) {
+ int align = ext2fs_get_dio_alignment(fs->mmp_fd);
+
+ retval = ext2fs_get_memalign(fs->blocksize, align,
+ &fs->mmp_cmp);
+ if (retval)
+ return retval;
+ }
+
+ if ((blk64_t) ext2fs_llseek(fs->mmp_fd, mmp_blk * fs->blocksize,
+ SEEK_SET) !=
+ mmp_blk * fs->blocksize) {
+ retval = EXT2_ET_LLSEEK_FAILED;
+ goto out;
+ }
+
+ if (read(fs->mmp_fd, fs->mmp_cmp, fs->blocksize) != fs->blocksize) {
+ retval = EXT2_ET_SHORT_READ;
+ goto out;
+ }
+
+ mmp_cmp = fs->mmp_cmp;
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_mmp_csum_verify(fs, mmp_cmp))
+ retval = EXT2_ET_MMP_CSUM_INVALID;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_mmp(mmp_cmp);
+#endif
+
+ if (buf != NULL && buf != fs->mmp_cmp)
+ memcpy(buf, fs->mmp_cmp, fs->blocksize);
+
+ if (mmp_cmp->mmp_magic != EXT4_MMP_MAGIC) {
+ retval = EXT2_ET_MMP_MAGIC_INVALID;
+ goto out;
+ }
+
+out:
+ return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
+{
+#ifdef CONFIG_MMP
+ struct mmp_struct *mmp_s = buf;
+ struct timeval tv;
+ errcode_t retval = 0;
+
+ gettimeofday(&tv, 0);
+ mmp_s->mmp_time = tv.tv_sec;
+ fs->mmp_last_written = tv.tv_sec;
+
+ if (fs->super->s_mmp_block < fs->super->s_first_data_block ||
+ fs->super->s_mmp_block > ext2fs_blocks_count(fs->super))
+ return EXT2_ET_MMP_BAD_BLOCK;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_mmp(mmp_s);
+#endif
+
+ retval = ext2fs_mmp_csum_set(fs, mmp_s);
+ if (retval)
+ return retval;
+
+ /* I was tempted to make this use O_DIRECT and the mmp_fd, but
+ * this caused no end of grief, while leaving it as-is works. */
+ retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf);
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_mmp(mmp_s);
+#endif
+
+ /* Make sure the block gets to disk quickly */
+ io_channel_flush(fs->io);
+ return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+unsigned ext2fs_mmp_new_seq(void)
+{
+#ifdef CONFIG_MMP
+ unsigned new_seq;
+ struct timeval tv;
+ unsigned long pid = getpid();
+
+ gettimeofday(&tv, 0);
+ pid = (pid >> 16) | ((pid & 0xFFFF) << 16);
+ srand(pid ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+
+ gettimeofday(&tv, 0);
+ /* Crank the random number generator a few times */
+ for (new_seq = (tv.tv_sec ^ tv.tv_usec) & 0x1F; new_seq > 0; new_seq--)
+ rand();
+
+ do {
+ new_seq = rand();
+ } while (new_seq > EXT4_MMP_SEQ_MAX);
+
+ return new_seq;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+#ifdef CONFIG_MMP
+static errcode_t ext2fs_mmp_reset(ext2_filsys fs)
+{
+ struct mmp_struct *mmp_s = NULL;
+ errcode_t retval = 0;
+
+ if (fs->mmp_buf == NULL) {
+ retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
+ if (retval)
+ goto out;
+ }
+
+ memset(fs->mmp_buf, 0, fs->blocksize);
+ mmp_s = fs->mmp_buf;
+
+ mmp_s->mmp_magic = EXT4_MMP_MAGIC;
+ mmp_s->mmp_seq = EXT4_MMP_SEQ_CLEAN;
+ mmp_s->mmp_time = 0;
+#ifdef HAVE_GETHOSTNAME
+ gethostname((char *) mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
+#else
+ mmp_s->mmp_nodename[0] = '\0';
+#endif
+ strncpy((char *) mmp_s->mmp_bdevname, fs->device_name,
+ sizeof(mmp_s->mmp_bdevname));
+
+ mmp_s->mmp_check_interval = fs->super->s_mmp_update_interval;
+ if (mmp_s->mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL)
+ mmp_s->mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL;
+
+ retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
+out:
+ return retval;
+}
+#endif
+
+errcode_t ext2fs_mmp_update(ext2_filsys fs)
+{
+ return ext2fs_mmp_update2(fs, 0);
+}
+
+errcode_t ext2fs_mmp_clear(ext2_filsys fs)
+{
+#ifdef CONFIG_MMP
+ errcode_t retval = 0;
+
+ if (!(fs->flags & EXT2_FLAG_RW))
+ return EXT2_ET_RO_FILSYS;
+
+ retval = ext2fs_mmp_reset(fs);
+
+ return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+errcode_t ext2fs_mmp_init(ext2_filsys fs)
+{
+#ifdef CONFIG_MMP
+ struct ext2_super_block *sb = fs->super;
+ blk64_t mmp_block;
+ errcode_t retval;
+
+ if (sb->s_mmp_update_interval == 0)
+ sb->s_mmp_update_interval = EXT4_MMP_UPDATE_INTERVAL;
+ /* This is probably excessively large, but who knows? */
+ else if (sb->s_mmp_update_interval > EXT4_MMP_MAX_UPDATE_INTERVAL)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (fs->mmp_buf == NULL) {
+ retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
+ if (retval)
+ goto out;
+ }
+
+ retval = ext2fs_alloc_block2(fs, 0, fs->mmp_buf, &mmp_block);
+ if (retval)
+ goto out;
+
+ sb->s_mmp_block = mmp_block;
+
+ retval = ext2fs_mmp_reset(fs);
+ if (retval)
+ goto out;
+
+out:
+ return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+/*
+ * Make sure that the fs is not mounted or being fsck'ed while opening the fs.
+ */
+errcode_t ext2fs_mmp_start(ext2_filsys fs)
+{
+#ifdef CONFIG_MMP
+ struct mmp_struct *mmp_s;
+ unsigned seq;
+ unsigned int mmp_check_interval;
+ errcode_t retval = 0;
+
+ if (fs->mmp_buf == NULL) {
+ retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
+ if (retval)
+ goto mmp_error;
+ }
+
+ retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
+ if (retval)
+ goto mmp_error;
+
+ mmp_s = fs->mmp_buf;
+
+ mmp_check_interval = fs->super->s_mmp_update_interval;
+ if (mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL)
+ mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL;
+
+ seq = mmp_s->mmp_seq;
+ if (seq == EXT4_MMP_SEQ_CLEAN)
+ goto clean_seq;
+ if (seq == EXT4_MMP_SEQ_FSCK) {
+ retval = EXT2_ET_MMP_FSCK_ON;
+ goto mmp_error;
+ }
+
+ if (seq > EXT4_MMP_SEQ_FSCK) {
+ retval = EXT2_ET_MMP_UNKNOWN_SEQ;
+ goto mmp_error;
+ }
+
+ /*
+ * If check_interval in MMP block is larger, use that instead of
+ * check_interval from the superblock.
+ */
+ if (mmp_s->mmp_check_interval > mmp_check_interval)
+ mmp_check_interval = mmp_s->mmp_check_interval;
+
+ sleep(min(mmp_check_interval * 2 + 1, mmp_check_interval + 60));
+
+ retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
+ if (retval)
+ goto mmp_error;
+
+ if (seq != mmp_s->mmp_seq) {
+ retval = EXT2_ET_MMP_FAILED;
+ goto mmp_error;
+ }
+
+clean_seq:
+ if (!(fs->flags & EXT2_FLAG_RW))
+ goto mmp_error;
+
+ mmp_s->mmp_seq = seq = ext2fs_mmp_new_seq();
+#ifdef HAVE_GETHOSTNAME
+ gethostname((char *) mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
+#else
+ strcpy((char *) mmp_s->mmp_nodename, "unknown host");
+#endif
+ strncpy((char *) mmp_s->mmp_bdevname, fs->device_name,
+ sizeof(mmp_s->mmp_bdevname));
+
+ retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
+ if (retval)
+ goto mmp_error;
+
+ sleep(min(2 * mmp_check_interval + 1, mmp_check_interval + 60));
+
+ retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
+ if (retval)
+ goto mmp_error;
+
+ if (seq != mmp_s->mmp_seq) {
+ retval = EXT2_ET_MMP_FAILED;
+ goto mmp_error;
+ }
+
+ mmp_s->mmp_seq = EXT4_MMP_SEQ_FSCK;
+ retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
+ if (retval)
+ goto mmp_error;
+
+ return 0;
+
+mmp_error:
+ return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+/*
+ * Clear the MMP usage in the filesystem. If this function returns an
+ * error EXT2_ET_MMP_CHANGE_ABORT it means the filesystem was modified
+ * by some other process while in use, and changes should be dropped, or
+ * risk filesystem corruption.
+ */
+errcode_t ext2fs_mmp_stop(ext2_filsys fs)
+{
+#ifdef CONFIG_MMP
+ struct mmp_struct *mmp, *mmp_cmp;
+ errcode_t retval = 0;
+
+ if (!ext2fs_has_feature_mmp(fs->super) ||
+ !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP) ||
+ (fs->mmp_buf == NULL) || (fs->mmp_cmp == NULL))
+ goto mmp_error;
+
+ retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, NULL);
+ if (retval)
+ goto mmp_error;
+
+ /* Check if the MMP block is not changed. */
+ mmp = fs->mmp_buf;
+ mmp_cmp = fs->mmp_cmp;
+ if (memcmp(mmp, mmp_cmp, sizeof(*mmp_cmp))) {
+ retval = EXT2_ET_MMP_CHANGE_ABORT;
+ goto mmp_error;
+ }
+
+ mmp_cmp->mmp_seq = EXT4_MMP_SEQ_CLEAN;
+ retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_cmp);
+
+mmp_error:
+ if (fs->mmp_fd > 0) {
+ close(fs->mmp_fd);
+ fs->mmp_fd = -1;
+ }
+
+ return retval;
+#else
+ if (!ext2fs_has_feature_mmp(fs->super) ||
+ !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
+ return 0;
+
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+#define EXT2_MIN_MMP_UPDATE_INTERVAL 60
+
+/*
+ * Update the on-disk mmp buffer, after checking that it hasn't been changed.
+ */
+errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately)
+{
+#ifdef CONFIG_MMP
+ struct mmp_struct *mmp, *mmp_cmp;
+ struct timeval tv;
+ errcode_t retval = 0;
+
+ if (!ext2fs_has_feature_mmp(fs->super) ||
+ !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
+ return 0;
+
+ gettimeofday(&tv, 0);
+ if (!immediately &&
+ tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
+ return 0;
+
+ retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, NULL);
+ if (retval)
+ goto mmp_error;
+
+ mmp = fs->mmp_buf;
+ mmp_cmp = fs->mmp_cmp;
+
+ if (memcmp(mmp, mmp_cmp, sizeof(*mmp_cmp)))
+ return EXT2_ET_MMP_CHANGE_ABORT;
+
+ mmp->mmp_time = tv.tv_sec;
+ mmp->mmp_seq = EXT4_MMP_SEQ_FSCK;
+ retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
+
+mmp_error:
+ return retval;
+#else
+ if (!ext2fs_has_feature_mmp(fs->super) ||
+ !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
+ return 0;
+
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic pop
+#endif
diff --git a/lib/ext2fs/namei.c b/lib/ext2fs/namei.c
new file mode 100644
index 0000000..1064ab5
--- /dev/null
+++ b/lib/ext2fs/namei.c
@@ -0,0 +1,227 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* #define NAMEI_DEBUG */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.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;
+ blk64_t blk;
+
+#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++ >= EXT2FS_MAX_NESTED_LINKS)
+ return EXT2_ET_SYMLINK_LOOP;
+
+ if (ext2fs_is_fast_symlink(&ei))
+ pathname = (char *)&(ei.i_block[0]);
+ else if (ei.i_flags & EXT4_INLINE_DATA_FL) {
+ retval = ext2fs_get_memzero(ei.i_size, &buffer);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_inline_data_get(fs, inode,
+ &ei, buffer, NULL);
+ if (retval) {
+ ext2fs_free_mem(&buffer);
+ return retval;
+ }
+ pathname = buffer;
+ } else {
+ retval = ext2fs_bmap2(fs, inode, &ei, NULL, 0, 0, NULL, &blk);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_get_mem(fs->blocksize, &buffer);
+ if (retval)
+ return retval;
+
+ retval = io_channel_read_blk64(fs->io, blk, 1, buffer);
+ if (retval) {
+ ext2fs_free_mem(&buffer);
+ return retval;
+ }
+ pathname = buffer;
+ }
+
+ 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/ext2fs/native.c b/lib/ext2fs/native.c
new file mode 100644
index 0000000..ba3e0c8
--- /dev/null
+++ b/lib/ext2fs/native.c
@@ -0,0 +1,28 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+
+#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/ext2fs/newdir.c b/lib/ext2fs/newdir.c
new file mode 100644
index 0000000..7f47285
--- /dev/null
+++ b/lib/ext2fs/newdir.c
@@ -0,0 +1,126 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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;
+ struct ext2_dir_entry_tail *t;
+ int csum_size = 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;
+
+ if (ext2fs_has_feature_metadata_csum(fs->super))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ retval = ext2fs_set_rec_len(fs, fs->blocksize - csum_size, dir);
+ if (retval) {
+ ext2fs_free_mem(&buf);
+ return retval;
+ }
+
+ if (dir_ino) {
+ if (ext2fs_has_feature_filetype(fs->super))
+ filetype = EXT2_FT_DIR;
+ /*
+ * Set up entry for '.'
+ */
+ dir->inode = dir_ino;
+ ext2fs_dirent_set_name_len(dir, 1);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ rec_len = (fs->blocksize - csum_size) - 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) {
+ ext2fs_free_mem(&buf);
+ return retval;
+ }
+ dir->inode = parent_ino;
+ ext2fs_dirent_set_name_len(dir, 2);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ dir->name[1] = '.';
+
+ }
+
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(buf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
+ *block = buf;
+ return 0;
+}
+
+/*
+ * Create new directory on inline data
+ */
+errcode_t ext2fs_new_dir_inline_data(ext2_filsys fs,
+ ext2_ino_t dir_ino EXT2FS_ATTR((unused)),
+ ext2_ino_t parent_ino, __u32 *iblock)
+{
+ struct ext2_dir_entry *dir = NULL;
+ errcode_t retval;
+ int rec_len;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ iblock[0] = ext2fs_cpu_to_le32(parent_ino);
+
+ dir = (struct ext2_dir_entry *)((char *)iblock +
+ EXT4_INLINE_DATA_DOTDOT_SIZE);
+ dir->inode = 0;
+ rec_len = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
+ retval = ext2fs_set_rec_len(fs, rec_len, dir);
+ if (retval)
+ goto errout;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_out2(fs, (char *)dir, rec_len, 0);
+ if (retval)
+ goto errout;
+#endif
+
+errout:
+ return retval;
+}
diff --git a/lib/ext2fs/nls_utf8.c b/lib/ext2fs/nls_utf8.c
new file mode 100644
index 0000000..b07e66e
--- /dev/null
+++ b/lib/ext2fs/nls_utf8.c
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2014 SGI.
+ * Copyright (c) 2018 Collabora Ltd.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * This code is adapted from the Linux Kernel. We have a
+ * userspace version here such that the hashes will match that
+ * implementation.
+ */
+
+#include "config.h"
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+/* Encoding a unicode version number as a single unsigned int. */
+#define UNICODE_MAJ_SHIFT (16)
+#define UNICODE_MIN_SHIFT (8)
+
+#define UNICODE_AGE(MAJ, MIN, REV) \
+ (((unsigned int)(MAJ) << UNICODE_MAJ_SHIFT) | \
+ ((unsigned int)(MIN) << UNICODE_MIN_SHIFT) | \
+ ((unsigned int)(REV)))
+
+/* Needed in struct utf8cursor below. */
+#define UTF8HANGULLEAF (12)
+
+/*
+ * Cursor structure used by the normalizer.
+ */
+struct utf8cursor {
+ const struct utf8data *data;
+ const char *s;
+ const char *p;
+ const char *ss;
+ const char *sp;
+ unsigned int len;
+ unsigned int slen;
+ short int ccc;
+ short int nccc;
+ unsigned char hangul[UTF8HANGULLEAF];
+};
+
+/*
+ * Initialize a utf8cursor to normalize a string.
+ * Returns 0 on success.
+ * Returns -1 on failure.
+ */
+// extern int utf8cursor(struct utf8cursor *u8c, const struct utf8data *data,
+// const char *s);
+// extern int utf8ncursor(struct utf8cursor *u8c, const struct utf8data *data,
+// const char *s, size_t len);
+
+/*
+ * Get the next byte in the normalization.
+ * Returns a value > 0 && < 256 on success.
+ * Returns 0 when the end of the normalization is reached.
+ * Returns -1 if the string being normalized is not valid UTF-8.
+ */
+// extern int utf8byte(struct utf8cursor *u8c);
+
+
+struct utf8data {
+ unsigned int maxage;
+ unsigned int offset;
+};
+
+#define __INCLUDED_FROM_UTF8NORM_C__
+#include "utf8data.h"
+#undef __INCLUDED_FROM_UTF8NORM_C__
+
+#define ARRAY_SIZE(array) \
+ (sizeof(array) / sizeof(array[0]))
+
+#if 0
+/* Highest unicode version supported by the data tables. */
+static int utf8version_is_supported(uint8_t maj, uint8_t min, uint8_t rev)
+{
+ int i = ARRAY_SIZE(utf8agetab) - 1;
+ unsigned int sb_utf8version = UNICODE_AGE(maj, min, rev);
+
+ while (i >= 0 && utf8agetab[i] != 0) {
+ if (sb_utf8version == utf8agetab[i])
+ return 1;
+ i--;
+ }
+ return 0;
+}
+#endif
+
+#if 0
+static int utf8version_latest(void)
+{
+ return utf8vers;
+}
+#endif
+
+/*
+ * UTF-8 valid ranges.
+ *
+ * The UTF-8 encoding spreads the bits of a 32bit word over several
+ * bytes. This table gives the ranges that can be held and how they'd
+ * be represented.
+ *
+ * 0x00000000 0x0000007F: 0xxxxxxx
+ * 0x00000000 0x000007FF: 110xxxxx 10xxxxxx
+ * 0x00000000 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
+ * 0x00000000 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 0x00000000 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 0x00000000 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *
+ * There is an additional requirement on UTF-8, in that only the
+ * shortest representation of a 32bit value is to be used. A decoder
+ * must not decode sequences that do not satisfy this requirement.
+ * Thus the allowed ranges have a lower bound.
+ *
+ * 0x00000000 0x0000007F: 0xxxxxxx
+ * 0x00000080 0x000007FF: 110xxxxx 10xxxxxx
+ * 0x00000800 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
+ * 0x00010000 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 0x00200000 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ * 0x04000000 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ *
+ * Actual unicode characters are limited to the range 0x0 - 0x10FFFF,
+ * 17 planes of 65536 values. This limits the sequences actually seen
+ * even more, to just the following.
+ *
+ * 0 - 0x7F: 0 - 0x7F
+ * 0x80 - 0x7FF: 0xC2 0x80 - 0xDF 0xBF
+ * 0x800 - 0xFFFF: 0xE0 0xA0 0x80 - 0xEF 0xBF 0xBF
+ * 0x10000 - 0x10FFFF: 0xF0 0x90 0x80 0x80 - 0xF4 0x8F 0xBF 0xBF
+ *
+ * Within those ranges the surrogates 0xD800 - 0xDFFF are not allowed.
+ *
+ * Note that the longest sequence seen with valid usage is 4 bytes,
+ * the same a single UTF-32 character. This makes the UTF-8
+ * representation of Unicode strictly smaller than UTF-32.
+ *
+ * The shortest sequence requirement was introduced by:
+ * Corrigendum #1: UTF-8 Shortest Form
+ * It can be found here:
+ * http://www.unicode.org/versions/corrigendum1.html
+ *
+ */
+
+/*
+ * Return the number of bytes used by the current UTF-8 sequence.
+ * Assumes the input points to the first byte of a valid UTF-8
+ * sequence.
+ */
+static inline int utf8clen(const char *s)
+{
+ unsigned char c = *s;
+
+ return 1 + (c >= 0xC0) + (c >= 0xE0) + (c >= 0xF0);
+}
+
+/*
+ * Decode a 3-byte UTF-8 sequence.
+ */
+static unsigned int
+utf8decode3(const char *str)
+{
+ unsigned int uc;
+
+ uc = *str++ & 0x0F;
+ uc <<= 6;
+ uc |= *str++ & 0x3F;
+ uc <<= 6;
+ uc |= *str++ & 0x3F;
+
+ return uc;
+}
+
+/*
+ * Encode a 3-byte UTF-8 sequence.
+ */
+static int
+utf8encode3(char *str, unsigned int val)
+{
+ str[2] = (val & 0x3F) | 0x80;
+ val >>= 6;
+ str[1] = (val & 0x3F) | 0x80;
+ val >>= 6;
+ str[0] = val | 0xE0;
+
+ return 3;
+}
+
+/*
+ * utf8trie_t
+ *
+ * A compact binary tree, used to decode UTF-8 characters.
+ *
+ * Internal nodes are one byte for the node itself, and up to three
+ * bytes for an offset into the tree. The first byte contains the
+ * following information:
+ * NEXTBYTE - flag - advance to next byte if set
+ * BITNUM - 3 bit field - the bit number to tested
+ * OFFLEN - 2 bit field - number of bytes in the offset
+ * if offlen == 0 (non-branching node)
+ * RIGHTPATH - 1 bit field - set if the following node is for the
+ * right-hand path (tested bit is set)
+ * TRIENODE - 1 bit field - set if the following node is an internal
+ * node, otherwise it is a leaf node
+ * if offlen != 0 (branching node)
+ * LEFTNODE - 1 bit field - set if the left-hand node is internal
+ * RIGHTNODE - 1 bit field - set if the right-hand node is internal
+ *
+ * Due to the way utf8 works, there cannot be branching nodes with
+ * NEXTBYTE set, and moreover those nodes always have a righthand
+ * descendant.
+ */
+typedef const unsigned char utf8trie_t;
+#define BITNUM 0x07
+#define NEXTBYTE 0x08
+#define OFFLEN 0x30
+#define OFFLEN_SHIFT 4
+#define RIGHTPATH 0x40
+#define TRIENODE 0x80
+#define RIGHTNODE 0x40
+#define LEFTNODE 0x80
+
+/*
+ * utf8leaf_t
+ *
+ * The leaves of the trie are embedded in the trie, and so the same
+ * underlying datatype: unsigned char.
+ *
+ * leaf[0]: The unicode version, stored as a generation number that is
+ * an index into utf8agetab[]. With this we can filter code
+ * points based on the unicode version in which they were
+ * defined. The CCC of a non-defined code point is 0.
+ * leaf[1]: Canonical Combining Class. During normalization, we need
+ * to do a stable sort into ascending order of all characters
+ * with a non-zero CCC that occur between two characters with
+ * a CCC of 0, or at the begin or end of a string.
+ * The unicode standard guarantees that all CCC values are
+ * between 0 and 254 inclusive, which leaves 255 available as
+ * a special value.
+ * Code points with CCC 0 are known as stoppers.
+ * leaf[2]: Decomposition. If leaf[1] == 255, then leaf[2] is the
+ * start of a NUL-terminated string that is the decomposition
+ * of the character.
+ * The CCC of a decomposable character is the same as the CCC
+ * of the first character of its decomposition.
+ * Some characters decompose as the empty string: these are
+ * characters with the Default_Ignorable_Code_Point property.
+ * These do affect normalization, as they all have CCC 0.
+ *
+ * The decompositions in the trie have been fully expanded, with the
+ * exception of Hangul syllables, which are decomposed algorithmically.
+ *
+ * Casefolding, if applicable, is also done using decompositions.
+ *
+ * The trie is constructed in such a way that leaves exist for all
+ * UTF-8 sequences that match the criteria from the "UTF-8 valid
+ * ranges" comment above, and only for those sequences. Therefore a
+ * lookup in the trie can be used to validate the UTF-8 input.
+ */
+typedef const unsigned char utf8leaf_t;
+
+#define LEAF_GEN(LEAF) ((LEAF)[0])
+#define LEAF_CCC(LEAF) ((LEAF)[1])
+#define LEAF_STR(LEAF) ((const char *)((LEAF) + 2))
+
+#define MINCCC (0)
+#define MAXCCC (254)
+#define STOPPER (0)
+#define DECOMPOSE (255)
+
+/* Marker for hangul syllable decomposition. */
+#define HANGUL ((char)(255))
+/* Size of the synthesized leaf used for Hangul syllable decomposition. */
+#define UTF8HANGULLEAF (12)
+
+/*
+ * Hangul decomposition (algorithm from Section 3.12 of Unicode 6.3.0)
+ *
+ * AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
+ * D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
+ *
+ * SBase = 0xAC00
+ * LBase = 0x1100
+ * VBase = 0x1161
+ * TBase = 0x11A7
+ * LCount = 19
+ * VCount = 21
+ * TCount = 28
+ * NCount = 588 (VCount * TCount)
+ * SCount = 11172 (LCount * NCount)
+ *
+ * Decomposition:
+ * SIndex = s - SBase
+ *
+ * LV (Canonical/Full)
+ * LIndex = SIndex / NCount
+ * VIndex = (Sindex % NCount) / TCount
+ * LPart = LBase + LIndex
+ * VPart = VBase + VIndex
+ *
+ * LVT (Canonical)
+ * LVIndex = (SIndex / TCount) * TCount
+ * TIndex = (Sindex % TCount)
+ * LVPart = SBase + LVIndex
+ * TPart = TBase + TIndex
+ *
+ * LVT (Full)
+ * LIndex = SIndex / NCount
+ * VIndex = (Sindex % NCount) / TCount
+ * TIndex = (Sindex % TCount)
+ * LPart = LBase + LIndex
+ * VPart = VBase + VIndex
+ * if (TIndex == 0) {
+ * d = <LPart, VPart>
+ * } else {
+ * TPart = TBase + TIndex
+ * d = <LPart, TPart, VPart>
+ * }
+ */
+
+/* Constants */
+#define SB (0xAC00)
+#define LB (0x1100)
+#define VB (0x1161)
+#define TB (0x11A7)
+#define LC (19)
+#define VC (21)
+#define TC (28)
+#define NC (VC * TC)
+#define SC (LC * NC)
+
+/* Algorithmic decomposition of hangul syllable. */
+static utf8leaf_t *
+utf8hangul(const char *str, unsigned char *hangul)
+{
+ unsigned int si;
+ unsigned int li;
+ unsigned int vi;
+ unsigned int ti;
+ unsigned char *h;
+
+ /* Calculate the SI, LI, VI, and TI values. */
+ si = utf8decode3(str) - SB;
+ li = si / NC;
+ vi = (si % NC) / TC;
+ ti = si % TC;
+
+ /* Fill in base of leaf. */
+ h = hangul;
+ LEAF_GEN(h) = 2;
+ LEAF_CCC(h) = DECOMPOSE;
+ h += 2;
+
+ /* Add LPart, a 3-byte UTF-8 sequence. */
+ h += utf8encode3((char *)h, li + LB);
+
+ /* Add VPart, a 3-byte UTF-8 sequence. */
+ h += utf8encode3((char *)h, vi + VB);
+
+ /* Add TPart if required, also a 3-byte UTF-8 sequence. */
+ if (ti)
+ h += utf8encode3((char *)h, ti + TB);
+
+ /* Terminate string. */
+ h[0] = '\0';
+
+ return hangul;
+}
+
+/*
+ * Use trie to scan s, touching at most len bytes.
+ * Returns the leaf if one exists, NULL otherwise.
+ *
+ * A non-NULL return guarantees that the UTF-8 sequence starting at s
+ * is well-formed and corresponds to a known unicode code point. The
+ * shorthand for this will be "is valid UTF-8 unicode".
+ */
+static utf8leaf_t *utf8nlookup(const struct utf8data *data,
+ unsigned char *hangul, const char *s, size_t len)
+{
+ utf8trie_t *trie;
+ int offlen;
+ int offset;
+ int mask;
+ int node;
+
+ if (!data)
+ return NULL;
+ if (len == 0)
+ return NULL;
+
+ trie = utf8data + data->offset;
+ node = 1;
+ while (node) {
+ offlen = (*trie & OFFLEN) >> OFFLEN_SHIFT;
+ if (*trie & NEXTBYTE) {
+ if (--len == 0)
+ return NULL;
+ s++;
+ }
+ mask = 1 << (*trie & BITNUM);
+ if (*s & mask) {
+ /* Right leg */
+ if (offlen) {
+ /* Right node at offset of trie */
+ node = (*trie & RIGHTNODE);
+ offset = trie[offlen];
+ while (--offlen) {
+ offset <<= 8;
+ offset |= trie[offlen];
+ }
+ trie += offset;
+ } else if (*trie & RIGHTPATH) {
+ /* Right node after this node */
+ node = (*trie & TRIENODE);
+ trie++;
+ } else {
+ /* No right node. */
+ return NULL;
+ }
+ } else {
+ /* Left leg */
+ if (offlen) {
+ /* Left node after this node. */
+ node = (*trie & LEFTNODE);
+ trie += offlen + 1;
+ } else if (*trie & RIGHTPATH) {
+ /* No left node. */
+ return NULL;
+ } else {
+ /* Left node after this node */
+ node = (*trie & TRIENODE);
+ trie++;
+ }
+ }
+ }
+ /*
+ * Hangul decomposition is done algorithmically. These are the
+ * codepoints >= 0xAC00 and <= 0xD7A3. Their UTF-8 encoding is
+ * always 3 bytes long, so s has been advanced twice, and the
+ * start of the sequence is at s-2.
+ */
+ if (LEAF_CCC(trie) == DECOMPOSE && LEAF_STR(trie)[0] == HANGUL)
+ trie = utf8hangul(s - 2, hangul);
+ return trie;
+}
+
+/*
+ * Use trie to scan s.
+ * Returns the leaf if one exists, NULL otherwise.
+ *
+ * Forwards to utf8nlookup().
+ */
+static utf8leaf_t *utf8lookup(const struct utf8data *data,
+ unsigned char *hangul, const char *s)
+{
+ return utf8nlookup(data, hangul, s, (size_t)-1);
+}
+
+#if 0
+/*
+ * Maximum age of any character in s.
+ * Return -1 if s is not valid UTF-8 unicode.
+ * Return 0 if only non-assigned code points are used.
+ */
+static int utf8agemax(const struct utf8data *data, const char *s)
+{
+ utf8leaf_t *leaf;
+ int age = 0;
+ int leaf_age;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+
+ while (*s) {
+ leaf = utf8lookup(data, hangul, s);
+ if (!leaf)
+ return -1;
+
+ leaf_age = utf8agetab[LEAF_GEN(leaf)];
+ if (leaf_age <= data->maxage && leaf_age > age)
+ age = leaf_age;
+ s += utf8clen(s);
+ }
+ return age;
+}
+#endif
+
+#if 0
+/*
+ * Minimum age of any character in s.
+ * Return -1 if s is not valid UTF-8 unicode.
+ * Return 0 if non-assigned code points are used.
+ */
+static int utf8agemin(const struct utf8data *data, const char *s)
+{
+ utf8leaf_t *leaf;
+ int age;
+ int leaf_age;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+ age = data->maxage;
+ while (*s) {
+ leaf = utf8lookup(data, hangul, s);
+ if (!leaf)
+ return -1;
+ leaf_age = utf8agetab[LEAF_GEN(leaf)];
+ if (leaf_age <= data->maxage && leaf_age < age)
+ age = leaf_age;
+ s += utf8clen(s);
+ }
+ return age;
+}
+#endif
+
+#if 0
+/*
+ * Maximum age of any character in s, touch at most len bytes.
+ * Return -1 if s is not valid UTF-8 unicode.
+ */
+static int utf8nagemax(const struct utf8data *data, const char *s, size_t len)
+{
+ utf8leaf_t *leaf;
+ int age = 0;
+ int leaf_age;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+
+ while (len && *s) {
+ leaf = utf8nlookup(data, hangul, s, len);
+ if (!leaf)
+ return -1;
+ leaf_age = utf8agetab[LEAF_GEN(leaf)];
+ if (leaf_age <= data->maxage && leaf_age > age)
+ age = leaf_age;
+ len -= utf8clen(s);
+ s += utf8clen(s);
+ }
+ return age;
+}
+#endif
+
+#if 0
+/*
+ * Maximum age of any character in s, touch at most len bytes.
+ * Return -1 if s is not valid UTF-8 unicode.
+ */
+static int utf8nagemin(const struct utf8data *data, const char *s, size_t len)
+{
+ utf8leaf_t *leaf;
+ int leaf_age;
+ int age;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+ age = data->maxage;
+ while (len && *s) {
+ leaf = utf8nlookup(data, hangul, s, len);
+ if (!leaf)
+ return -1;
+ leaf_age = utf8agetab[LEAF_GEN(leaf)];
+ if (leaf_age <= data->maxage && leaf_age < age)
+ age = leaf_age;
+ len -= utf8clen(s);
+ s += utf8clen(s);
+ }
+ return age;
+}
+#endif
+
+#if 0
+/*
+ * Length of the normalization of s.
+ * Return -1 if s is not valid UTF-8 unicode.
+ *
+ * A string of Default_Ignorable_Code_Point has length 0.
+ */
+static ssize_t utf8len(const struct utf8data *data, const char *s)
+{
+ utf8leaf_t *leaf;
+ size_t ret = 0;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+ while (*s) {
+ leaf = utf8lookup(data, hangul, s);
+ if (!leaf)
+ return -1;
+ if (utf8agetab[LEAF_GEN(leaf)] > data->maxage)
+ ret += utf8clen(s);
+ else if (LEAF_CCC(leaf) == DECOMPOSE)
+ ret += strlen(LEAF_STR(leaf));
+ else
+ ret += utf8clen(s);
+ s += utf8clen(s);
+ }
+ return ret;
+}
+#endif
+
+#if 0
+/*
+ * Length of the normalization of s, touch at most len bytes.
+ * Return -1 if s is not valid UTF-8 unicode.
+ */
+static ssize_t utf8nlen(const struct utf8data *data, const char *s, size_t len)
+{
+ utf8leaf_t *leaf;
+ size_t ret = 0;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+ while (len && *s) {
+ leaf = utf8nlookup(data, hangul, s, len);
+ if (!leaf)
+ return -1;
+ if (utf8agetab[LEAF_GEN(leaf)] > data->maxage)
+ ret += utf8clen(s);
+ else if (LEAF_CCC(leaf) == DECOMPOSE)
+ ret += strlen(LEAF_STR(leaf));
+ else
+ ret += utf8clen(s);
+ len -= utf8clen(s);
+ s += utf8clen(s);
+ }
+ return ret;
+}
+#endif
+
+/*
+ * Set up an utf8cursor for use by utf8byte().
+ *
+ * u8c : pointer to cursor.
+ * data : const struct utf8data to use for normalization.
+ * s : string.
+ * len : length of s.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+static int utf8ncursor(struct utf8cursor *u8c, const struct utf8data *data,
+ const char *s, size_t len)
+{
+ if (!data)
+ return -1;
+ if (!s)
+ return -1;
+ u8c->data = data;
+ u8c->s = s;
+ u8c->p = NULL;
+ u8c->ss = NULL;
+ u8c->sp = NULL;
+ u8c->len = len;
+ u8c->slen = 0;
+ u8c->ccc = STOPPER;
+ u8c->nccc = STOPPER;
+ /* Check we didn't clobber the maximum length. */
+ if (u8c->len != len)
+ return -1;
+ /* The first byte of s may not be an utf8 continuation. */
+ if (len > 0 && (*s & 0xC0) == 0x80)
+ return -1;
+ return 0;
+}
+
+#if 0
+/*
+ * Set up an utf8cursor for use by utf8byte().
+ *
+ * u8c : pointer to cursor.
+ * data : const struct utf8data to use for normalization.
+ * s : NUL-terminated string.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+static int utf8cursor(struct utf8cursor *u8c, const struct utf8data *data,
+ const char *s)
+{
+ return utf8ncursor(u8c, data, s, (unsigned int)-1);
+}
+#endif
+
+/*
+ * Get one byte from the normalized form of the string described by u8c.
+ *
+ * Returns the byte cast to an unsigned char on success, and -1 on failure.
+ *
+ * The cursor keeps track of the location in the string in u8c->s.
+ * When a character is decomposed, the current location is stored in
+ * u8c->p, and u8c->s is set to the start of the decomposition. Note
+ * that bytes from a decomposition do not count against u8c->len.
+ *
+ * Characters are emitted if they match the current CCC in u8c->ccc.
+ * Hitting end-of-string while u8c->ccc == STOPPER means we're done,
+ * and the function returns 0 in that case.
+ *
+ * Sorting by CCC is done by repeatedly scanning the string. The
+ * values of u8c->s and u8c->p are stored in u8c->ss and u8c->sp at
+ * the start of the scan. The first pass finds the lowest CCC to be
+ * emitted and stores it in u8c->nccc, the second pass emits the
+ * characters with this CCC and finds the next lowest CCC. This limits
+ * the number of passes to 1 + the number of different CCCs in the
+ * sequence being scanned.
+ *
+ * Therefore:
+ * u8c->p != NULL -> a decomposition is being scanned.
+ * u8c->ss != NULL -> this is a repeating scan.
+ * u8c->ccc == -1 -> this is the first scan of a repeating scan.
+ */
+static int utf8byte(struct utf8cursor *u8c)
+{
+ utf8leaf_t *leaf;
+ int ccc;
+
+ for (;;) {
+ /* Check for the end of a decomposed character. */
+ if (u8c->p && *u8c->s == '\0') {
+ u8c->s = u8c->p;
+ u8c->p = NULL;
+ }
+
+ /* Check for end-of-string. */
+ if (!u8c->p && (u8c->len == 0 || *u8c->s == '\0')) {
+ /* There is no next byte. */
+ if (u8c->ccc == STOPPER)
+ return 0;
+ /* End-of-string during a scan counts as a stopper. */
+ ccc = STOPPER;
+ goto ccc_mismatch;
+ } else if ((*u8c->s & 0xC0) == 0x80) {
+ /* This is a continuation of the current character. */
+ if (!u8c->p)
+ u8c->len--;
+ return (unsigned char)*u8c->s++;
+ }
+
+ /* Look up the data for the current character. */
+ if (u8c->p) {
+ leaf = utf8lookup(u8c->data, u8c->hangul, u8c->s);
+ } else {
+ leaf = utf8nlookup(u8c->data, u8c->hangul,
+ u8c->s, u8c->len);
+ }
+
+ /* No leaf found implies that the input is a binary blob. */
+ if (!leaf)
+ return -1;
+
+ ccc = LEAF_CCC(leaf);
+ /* Characters that are too new have CCC 0. */
+ if (utf8agetab[LEAF_GEN(leaf)] > u8c->data->maxage) {
+ ccc = STOPPER;
+ } else if (ccc == DECOMPOSE) {
+ u8c->len -= utf8clen(u8c->s);
+ u8c->p = u8c->s + utf8clen(u8c->s);
+ u8c->s = LEAF_STR(leaf);
+ /* Empty decomposition implies CCC 0. */
+ if (*u8c->s == '\0') {
+ if (u8c->ccc == STOPPER)
+ continue;
+ ccc = STOPPER;
+ goto ccc_mismatch;
+ }
+
+ leaf = utf8lookup(u8c->data, u8c->hangul, u8c->s);
+ if (!leaf)
+ return -1;
+ ccc = LEAF_CCC(leaf);
+ }
+
+ /*
+ * If this is not a stopper, then see if it updates
+ * the next canonical class to be emitted.
+ */
+ if (ccc != STOPPER && u8c->ccc < ccc && ccc < u8c->nccc)
+ u8c->nccc = ccc;
+
+ /*
+ * Return the current byte if this is the current
+ * combining class.
+ */
+ if (ccc == u8c->ccc) {
+ if (!u8c->p)
+ u8c->len--;
+ return (unsigned char)*u8c->s++;
+ }
+
+ /* Current combining class mismatch. */
+ccc_mismatch:
+ if (u8c->nccc == STOPPER) {
+ /*
+ * Scan forward for the first canonical class
+ * to be emitted. Save the position from
+ * which to restart.
+ */
+ u8c->ccc = MINCCC - 1;
+ u8c->nccc = ccc;
+ u8c->sp = u8c->p;
+ u8c->ss = u8c->s;
+ u8c->slen = u8c->len;
+ if (!u8c->p)
+ u8c->len -= utf8clen(u8c->s);
+ u8c->s += utf8clen(u8c->s);
+ } else if (ccc != STOPPER) {
+ /* Not a stopper, and not the ccc we're emitting. */
+ if (!u8c->p)
+ u8c->len -= utf8clen(u8c->s);
+ u8c->s += utf8clen(u8c->s);
+ } else if (u8c->nccc != MAXCCC + 1) {
+ /* At a stopper, restart for next ccc. */
+ u8c->ccc = u8c->nccc;
+ u8c->nccc = MAXCCC + 1;
+ u8c->s = u8c->ss;
+ u8c->p = u8c->sp;
+ u8c->len = u8c->slen;
+ } else {
+ /* All done, proceed from here. */
+ u8c->ccc = STOPPER;
+ u8c->nccc = STOPPER;
+ u8c->sp = NULL;
+ u8c->ss = NULL;
+ u8c->slen = 0;
+ }
+ }
+}
+
+#if 0
+/*
+ * Look for the correct const struct utf8data for a unicode version.
+ * Returns NULL if the version requested is too new.
+ *
+ * Two normalization forms are supported: nfdi and nfdicf.
+ *
+ * nfdi:
+ * - Apply unicode normalization form NFD.
+ * - Remove any Default_Ignorable_Code_Point.
+ *
+ * nfdicf:
+ * - Apply unicode normalization form NFD.
+ * - Remove any Default_Ignorable_Code_Point.
+ * - Apply a full casefold (C + F).
+ */
+static const struct utf8data *utf8nfdi(unsigned int maxage)
+{
+ int i = ARRAY_SIZE(utf8nfdidata) - 1;
+
+ while (maxage < utf8nfdidata[i].maxage)
+ i--;
+ if (maxage > utf8nfdidata[i].maxage)
+ return NULL;
+ return &utf8nfdidata[i];
+}
+#endif
+
+static const struct utf8data *utf8nfdicf(unsigned int maxage)
+{
+ int i = ARRAY_SIZE(utf8nfdicfdata) - 1;
+
+ while (maxage < utf8nfdicfdata[i].maxage)
+ i--;
+ if (maxage > utf8nfdicfdata[i].maxage)
+ return NULL;
+ return &utf8nfdicfdata[i];
+}
+
+static int utf8_casefold(const struct ext2fs_nls_table *table,
+ const unsigned char *str, size_t len,
+ unsigned char *dest, size_t dlen)
+{
+ const struct utf8data *data = utf8nfdicf(table->version);
+ struct utf8cursor cur;
+ size_t nlen = 0;
+
+ if (utf8ncursor(&cur, data, (const char *) str, len) < 0)
+ goto invalid_seq;
+
+ for (nlen = 0; nlen < dlen; nlen++) {
+ int c = utf8byte(&cur);
+
+ dest[nlen] = c;
+ if (!c)
+ return nlen;
+ if (c == -1)
+ break;
+ }
+
+ return -ENAMETOOLONG;
+
+invalid_seq:
+ if (dlen < len)
+ return -ENAMETOOLONG;
+
+ /* Signal invalid sequence */
+ return -EINVAL;
+}
+
+static int utf8_validate(const struct ext2fs_nls_table *table,
+ char *s, size_t len, char **pos)
+{
+ const struct utf8data *data = utf8nfdicf(table->version);
+ utf8leaf_t *leaf;
+ unsigned char hangul[UTF8HANGULLEAF];
+
+ if (!data)
+ return -1;
+ while (len && *s) {
+ leaf = utf8nlookup(data, hangul, s, len);
+ if (!leaf) {
+ *pos = s;
+ return 1;
+ }
+ len -= utf8clen(s);
+ s += utf8clen(s);
+ }
+ return 0;
+}
+
+static int utf8_casefold_cmp(const struct ext2fs_nls_table *table,
+ const unsigned char *str1, size_t len1,
+ const unsigned char *str2, size_t len2)
+{
+ const struct utf8data *data = utf8nfdicf(table->version);
+ int c1, c2;
+ struct utf8cursor cur1, cur2;
+
+ if (utf8ncursor(&cur1, data, (const char *) str1, len1) < 0)
+ return -1;
+ if (utf8ncursor(&cur2, data, (const char *) str2, len2) < 0)
+ return -1;
+
+ do {
+ c1 = utf8byte(&cur1);
+ c2 = utf8byte(&cur2);
+
+ if (c1 < 0 || c2 < 0)
+ return -1;
+ if (c1 != c2)
+ return c1 - c2;
+ } while (c1);
+
+ return 0;
+}
+
+static const struct ext2fs_nls_ops utf8_ops = {
+ .casefold = utf8_casefold,
+ .validate = utf8_validate,
+ .casefold_cmp = utf8_casefold_cmp,
+};
+
+static const struct ext2fs_nls_table nls_utf8 = {
+ .ops = &utf8_ops,
+ .version = UNICODE_AGE(12, 1, 0),
+};
+
+const struct ext2fs_nls_table *ext2fs_load_nls_table(int encoding)
+{
+ if (encoding == EXT4_ENC_UTF8_12_1)
+ return &nls_utf8;
+
+ return NULL;
+}
+
+int ext2fs_check_encoded_name(const struct ext2fs_nls_table *table,
+ char *name, size_t len, char **pos)
+{
+ return table->ops->validate(table, name, len, pos);
+}
+
+int ext2fs_casefold_cmp(const struct ext2fs_nls_table *table,
+ const unsigned char *str1, size_t len1,
+ const unsigned char *str2, size_t len2)
+{
+ return table->ops->casefold_cmp(table, str1, len1, str2, len2);
+}
diff --git a/lib/ext2fs/nt_io.c b/lib/ext2fs/nt_io.c
new file mode 100644
index 0000000..234f6b1
--- /dev/null
+++ b/lib/ext2fs/nt_io.c
@@ -0,0 +1,1494 @@
+/*
+ * nt_io.c --- This is the Nt I/O interface to the I/O manager.
+ *
+ * Implements a one-block write-through cache.
+ *
+ * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ * Copyright (C) 1998 Andrey Shedel (andreys@ns.cr.cyco.com)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+//
+// I need some warnings to disable...
+//
+
+
+#pragma warning(disable:4514) // unreferenced inline function has been removed
+#pragma warning(push,4)
+
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4115) // named type definition in parentheses
+
+#include <ntddk.h>
+#include <ntdddisk.h>
+#include <ntstatus.h>
+
+#pragma warning(pop)
+
+
+//
+// Some native APIs.
+//
+
+NTSYSAPI
+ULONG
+NTAPI
+RtlNtStatusToDosError(
+ IN NTSTATUS Status
+ );
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtClose(
+ IN HANDLE Handle
+ );
+
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtOpenFile(
+ OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG ShareAccess,
+ IN ULONG OpenOptions
+ );
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtFlushBuffersFile(
+ IN HANDLE FileHandle,
+ OUT PIO_STATUS_BLOCK IoStatusBlock
+ );
+
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtReadFile(
+ IN HANDLE FileHandle,
+ IN HANDLE Event OPTIONAL,
+ IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+ IN PVOID ApcContext OPTIONAL,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ OUT PVOID Buffer,
+ IN ULONG Length,
+ IN PLARGE_INTEGER ByteOffset OPTIONAL,
+ IN PULONG Key OPTIONAL
+ );
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtWriteFile(
+ IN HANDLE FileHandle,
+ IN HANDLE Event OPTIONAL,
+ IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+ IN PVOID ApcContext OPTIONAL,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PVOID Buffer,
+ IN ULONG Length,
+ IN PLARGE_INTEGER ByteOffset OPTIONAL,
+ IN PULONG Key OPTIONAL
+ );
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtDeviceIoControlFile(
+ IN HANDLE FileHandle,
+ IN HANDLE Event OPTIONAL,
+ IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+ IN PVOID ApcContext OPTIONAL,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG IoControlCode,
+ IN PVOID InputBuffer OPTIONAL,
+ IN ULONG InputBufferLength,
+ OUT PVOID OutputBuffer OPTIONAL,
+ IN ULONG OutputBufferLength
+ );
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtFsControlFile(
+ IN HANDLE FileHandle,
+ IN HANDLE Event OPTIONAL,
+ IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+ IN PVOID ApcContext OPTIONAL,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG IoControlCode,
+ IN PVOID InputBuffer OPTIONAL,
+ IN ULONG InputBufferLength,
+ OUT PVOID OutputBuffer OPTIONAL,
+ IN ULONG OutputBufferLength
+ );
+
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+NtDelayExecution(
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Interval
+ );
+
+
+#define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_UNLOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_IS_VOLUME_MOUNTED CTL_CODE(FILE_DEVICE_FILE_SYSTEM,10, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+//
+// useful macros
+//
+
+#define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0)))
+
+
+//
+// Include Win32 error codes.
+//
+
+#include <winerror.h>
+
+//
+// standard stuff
+//
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#include <linux/types.h>
+#include "ext2_fs.h"
+#include <errno.h>
+
+#include "et/com_err.h"
+#include "ext2fs/ext2fs.h"
+#include "ext2fs/ext2_err.h"
+
+
+
+
+//
+// For checking structure magic numbers...
+//
+
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+#define EXT2_ET_MAGIC_NT_IO_CHANNEL 0x10ed
+
+
+//
+// Private data block
+//
+
+typedef struct _NT_PRIVATE_DATA {
+ int magic;
+ HANDLE Handle;
+ int Flags;
+ PCHAR Buffer;
+ __u32 BufferBlockNumber;
+ ULONG BufferSize;
+ BOOLEAN OpenedReadonly;
+ BOOLEAN Written;
+}NT_PRIVATE_DATA, *PNT_PRIVATE_DATA;
+
+
+
+//
+// Standard interface prototypes
+//
+
+static errcode_t nt_open(const char *name, int flags, io_channel *channel);
+static errcode_t nt_close(io_channel channel);
+static errcode_t nt_set_blksize(io_channel channel, int blksize);
+static errcode_t nt_read_blk(io_channel channel, unsigned long block,
+ int count, void *data);
+static errcode_t nt_write_blk(io_channel channel, unsigned long block,
+ int count, const void *data);
+static errcode_t nt_flush(io_channel channel);
+
+static struct struct_io_manager struct_nt_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "NT I/O Manager",
+ .open = nt_open,
+ .close = nt_close,
+ .set_blksize = nt_set_blksize,
+ .read_blk = nt_read_blk,
+ .write_blk = nt_write_blk,
+ .flush = nt_flush
+};
+
+//
+// function to get API
+//
+
+io_manager nt_io_manager()
+{
+ return &struct_nt_manager;
+}
+
+
+
+
+
+//
+// This is a code to convert Win32 errors to unix errno
+//
+
+typedef struct {
+ ULONG WinError;
+ int errnocode;
+}ERROR_ENTRY;
+
+static ERROR_ENTRY ErrorTable[] = {
+ { ERROR_INVALID_FUNCTION, EINVAL },
+ { ERROR_FILE_NOT_FOUND, ENOENT },
+ { ERROR_PATH_NOT_FOUND, ENOENT },
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
+ { ERROR_ACCESS_DENIED, EACCES },
+ { ERROR_INVALID_HANDLE, EBADF },
+ { ERROR_ARENA_TRASHED, ENOMEM },
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
+ { ERROR_INVALID_BLOCK, ENOMEM },
+ { ERROR_BAD_ENVIRONMENT, E2BIG },
+ { ERROR_BAD_FORMAT, ENOEXEC },
+ { ERROR_INVALID_ACCESS, EINVAL },
+ { ERROR_INVALID_DATA, EINVAL },
+ { ERROR_INVALID_DRIVE, ENOENT },
+ { ERROR_CURRENT_DIRECTORY, EACCES },
+ { ERROR_NOT_SAME_DEVICE, EXDEV },
+ { ERROR_NO_MORE_FILES, ENOENT },
+ { ERROR_LOCK_VIOLATION, EACCES },
+ { ERROR_BAD_NETPATH, ENOENT },
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES },
+ { ERROR_BAD_NET_NAME, ENOENT },
+ { ERROR_FILE_EXISTS, EEXIST },
+ { ERROR_CANNOT_MAKE, EACCES },
+ { ERROR_FAIL_I24, EACCES },
+ { ERROR_INVALID_PARAMETER, EINVAL },
+ { ERROR_NO_PROC_SLOTS, EAGAIN },
+ { ERROR_DRIVE_LOCKED, EACCES },
+ { ERROR_BROKEN_PIPE, EPIPE },
+ { ERROR_DISK_FULL, ENOSPC },
+ { ERROR_INVALID_TARGET_HANDLE, EBADF },
+ { ERROR_INVALID_HANDLE, EINVAL },
+ { ERROR_WAIT_NO_CHILDREN, ECHILD },
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD },
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
+ { ERROR_NEGATIVE_SEEK, EINVAL },
+ { ERROR_SEEK_ON_DEVICE, EACCES },
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
+ { ERROR_NOT_LOCKED, EACCES },
+ { ERROR_BAD_PATHNAME, ENOENT },
+ { ERROR_MAX_THRDS_REACHED, EAGAIN },
+ { ERROR_LOCK_FAILED, EACCES },
+ { ERROR_ALREADY_EXISTS, EEXIST },
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT },
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM }
+};
+
+
+
+
+static
+unsigned
+_MapDosError (
+ IN ULONG WinError
+ )
+{
+ int i;
+
+ //
+ // Lookup
+ //
+
+ for (i = 0; i < (sizeof(ErrorTable)/sizeof(ErrorTable[0])); ++i)
+ {
+ if (WinError == ErrorTable[i].WinError)
+ {
+ return ErrorTable[i].errnocode;
+ }
+ }
+
+ //
+ // not in table. Check ranges
+ //
+
+ if ((WinError >= ERROR_WRITE_PROTECT) &&
+ (WinError <= ERROR_SHARING_BUFFER_EXCEEDED))
+ {
+ return EACCES;
+ }
+ else if ((WinError >= ERROR_INVALID_STARTING_CODESEG) &&
+ (WinError <= ERROR_INFLOOP_IN_RELOC_CHAIN))
+ {
+ return ENOEXEC;
+ }
+ else
+ {
+ return EINVAL;
+ }
+}
+
+
+
+
+
+
+
+//
+// Function to map NT status to dos error.
+//
+
+static
+__inline
+unsigned
+_MapNtStatus(
+ IN NTSTATUS Status
+ )
+{
+ return _MapDosError(RtlNtStatusToDosError(Status));
+}
+
+
+
+
+
+//
+// Helper functions to make things easier
+//
+
+static
+NTSTATUS
+_OpenNtName(
+ IN PCSTR Name,
+ IN BOOLEAN Readonly,
+ OUT PHANDLE Handle,
+ OUT PBOOLEAN OpenedReadonly OPTIONAL
+ )
+{
+ UNICODE_STRING UnicodeString;
+ ANSI_STRING AnsiString;
+ WCHAR Buffer[512];
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // Make Unicode name from input string
+ //
+
+ UnicodeString.Buffer = &Buffer[0];
+ UnicodeString.Length = 0;
+ UnicodeString.MaximumLength = sizeof(Buffer); // in bytes!!!
+
+ RtlInitAnsiString(&AnsiString, Name);
+
+ Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE);
+
+ if(!NT_SUCCESS(Status))
+ {
+ return Status; // Unmappable character?
+ }
+
+ //
+ // Initialize object
+ //
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &UnicodeString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ //
+ // Try to open it in initial mode
+ //
+
+ if(ARGUMENT_PRESENT(OpenedReadonly))
+ {
+ *OpenedReadonly = Readonly;
+ }
+
+
+ Status = NtOpenFile(Handle,
+ SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA),
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ if(!NT_SUCCESS(Status))
+ {
+ //
+ // Maybe was just mounted? wait 0.5 sec and retry.
+ //
+
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = -5000000; // 0.5 sec. from now
+
+ NtDelayExecution(FALSE, &Interval);
+
+ Status = NtOpenFile(Handle,
+ SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA),
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+
+ //
+ // Try to satisfy mode
+ //
+
+ if((STATUS_ACCESS_DENIED == Status) && !Readonly)
+ {
+ if(ARGUMENT_PRESENT(OpenedReadonly))
+ {
+ *OpenedReadonly = TRUE;
+ }
+
+ Status = NtOpenFile(Handle,
+ SYNCHRONIZE | FILE_READ_DATA,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+ }
+ }
+
+
+
+ //
+ // done
+ //
+
+ return Status;
+}
+
+
+static
+NTSTATUS
+_OpenDriveLetter(
+ IN CHAR Letter,
+ IN BOOLEAN ReadOnly,
+ OUT PHANDLE Handle,
+ OUT PBOOLEAN OpenedReadonly OPTIONAL
+ )
+{
+ CHAR Buffer[100];
+
+ sprintf(Buffer, "\\DosDevices\\%c:", Letter);
+
+ return _OpenNtName(Buffer, ReadOnly, Handle, OpenedReadonly);
+}
+
+
+//
+// Flush device
+//
+
+static
+__inline
+NTSTATUS
+_FlushDrive(
+ IN HANDLE Handle
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ return NtFlushBuffersFile(Handle, &IoStatusBlock);
+}
+
+
+//
+// lock drive
+//
+
+static
+__inline
+NTSTATUS
+_LockDrive(
+ IN HANDLE Handle
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_LOCK_VOLUME, 0, 0, 0, 0);
+}
+
+
+//
+// unlock drive
+//
+
+static
+__inline
+NTSTATUS
+_UnlockDrive(
+ IN HANDLE Handle
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0);
+}
+
+static
+__inline
+NTSTATUS
+_DismountDrive(
+ IN HANDLE Handle
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0);
+}
+
+
+//
+// is mounted
+//
+
+static
+__inline
+BOOLEAN
+_IsMounted(
+ IN HANDLE Handle
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS Status;
+ Status = NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_IS_VOLUME_MOUNTED, 0, 0, 0, 0);
+ return (BOOLEAN)(STATUS_SUCCESS == Status);
+}
+
+
+static
+__inline
+NTSTATUS
+_CloseDisk(
+ IN HANDLE Handle
+ )
+{
+ return NtClose(Handle);
+}
+
+
+
+
+//
+// Make NT name from any recognized name
+//
+
+static
+PCSTR
+_NormalizeDeviceName(
+ IN PCSTR Device,
+ IN PSTR NormalizedDeviceNameBuffer
+ )
+{
+ int PartitionNumber = -1;
+ UCHAR DiskNumber;
+ PSTR p;
+
+
+ //
+ // Do not try to parse NT name
+ //
+
+ if('\\' == *Device)
+ return Device;
+
+
+
+ //
+ // Strip leading '/dev/' if any
+ //
+
+ if(('/' == *(Device)) &&
+ ('d' == *(Device + 1)) &&
+ ('e' == *(Device + 2)) &&
+ ('v' == *(Device + 3)) &&
+ ('/' == *(Device + 4)))
+ {
+ Device += 5;
+ }
+
+ if('\0' == *Device)
+ {
+ return NULL;
+ }
+
+
+ //
+ // forms: hda[n], fd[n]
+ //
+
+ if('d' != *(Device + 1))
+ {
+ return NULL;
+ }
+
+ if('h' == *Device)
+ {
+ if((*(Device + 2) < 'a') || (*(Device + 2) > ('a' + 9)) ||
+ ((*(Device + 3) != '\0') &&
+ ((*(Device + 4) != '\0') ||
+ ((*(Device + 3) < '0') || (*(Device + 3) > '9'))
+ )
+ )
+ )
+ {
+ return NULL;
+ }
+
+ DiskNumber = (UCHAR)(*(Device + 2) - 'a');
+
+ if(*(Device + 3) != '\0')
+ {
+ PartitionNumber = (*(Device + 3) - '0');
+ }
+
+ }
+ else if('f' == *Device)
+ {
+ //
+ // 3-d letter should be a digit.
+ //
+
+ if((*(Device + 3) != '\0') ||
+ (*(Device + 2) < '0') || (*(Device + 2) > '9'))
+ {
+ return NULL;
+ }
+
+ DiskNumber = (UCHAR)(*(Device + 2) - '0');
+
+ }
+ else
+ {
+ //
+ // invalid prefix
+ //
+
+ return NULL;
+ }
+
+
+
+ //
+ // Prefix
+ //
+
+ strcpy(NormalizedDeviceNameBuffer, "\\Device\\");
+
+ //
+ // Media name
+ //
+
+ switch(*Device)
+ {
+
+ case 'f':
+ strcat(NormalizedDeviceNameBuffer, "Floppy0");
+ break;
+
+ case 'h':
+ strcat(NormalizedDeviceNameBuffer, "Harddisk0");
+ break;
+ }
+
+
+ p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1;
+ *p = (CHAR)(*p + DiskNumber);
+
+
+ //
+ // Partition nr.
+ //
+
+ if(PartitionNumber >= 0)
+ {
+ strcat(NormalizedDeviceNameBuffer, "\\Partition0");
+
+ p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1;
+ *p = (CHAR)(*p + PartitionNumber);
+ }
+
+
+ return NormalizedDeviceNameBuffer;
+}
+
+
+
+
+static
+VOID
+_GetDeviceSize(
+ IN HANDLE h,
+ OUT unsigned __int64 *FsSize
+ )
+{
+ PARTITION_INFORMATION pi;
+ DISK_GEOMETRY gi;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // Zero it
+ //
+
+ *FsSize = 0;
+
+ //
+ // Call driver
+ //
+
+ RtlZeroMemory(&pi, sizeof(PARTITION_INFORMATION));
+
+ Status = NtDeviceIoControlFile(
+ h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO,
+ &pi, sizeof(PARTITION_INFORMATION),
+ &pi, sizeof(PARTITION_INFORMATION));
+
+
+ if(NT_SUCCESS(Status))
+ {
+ *FsSize = pi.PartitionLength.QuadPart;
+ }
+ else if(STATUS_INVALID_DEVICE_REQUEST == Status)
+ {
+ //
+ // No partitions: get device info.
+ //
+
+ RtlZeroMemory(&gi, sizeof(DISK_GEOMETRY));
+
+ Status = NtDeviceIoControlFile(
+ h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ &gi, sizeof(DISK_GEOMETRY),
+ &gi, sizeof(DISK_GEOMETRY));
+
+
+ if(NT_SUCCESS(Status))
+ {
+ *FsSize =
+ gi.BytesPerSector *
+ gi.SectorsPerTrack *
+ gi.TracksPerCylinder *
+ gi.Cylinders.QuadPart;
+ }
+
+ }
+}
+
+
+
+//
+// Open device by name.
+//
+
+static
+BOOLEAN
+_Ext2OpenDevice(
+ IN PCSTR Name,
+ IN BOOLEAN ReadOnly,
+ OUT PHANDLE Handle,
+ OUT PBOOLEAN OpenedReadonly OPTIONAL,
+ OUT unsigned *Errno OPTIONAL
+ )
+{
+ CHAR NormalizedDeviceName[512];
+ NTSTATUS Status;
+
+ if(NULL == Name)
+ {
+ //
+ // Set not found
+ //
+
+ if(ARGUMENT_PRESENT(Errno))
+ *Errno = ENOENT;
+
+ return FALSE;
+ }
+
+
+ if((((*Name) | 0x20) >= 'a') && (((*Name) | 0x20) <= 'z') &&
+ (':' == *(Name + 1)) && ('\0' == *(Name + 2)))
+ {
+ Status = _OpenDriveLetter(*Name, ReadOnly, Handle, OpenedReadonly);
+ }
+ else
+ {
+ //
+ // Make name
+ //
+
+ Name = _NormalizeDeviceName(Name, NormalizedDeviceName);
+
+ if(NULL == Name)
+ {
+ //
+ // Set not found
+ //
+
+ if(ARGUMENT_PRESENT(Errno))
+ *Errno = ENOENT;
+
+ return FALSE;
+ }
+
+ //
+ // Try to open it
+ //
+
+ Status = _OpenNtName(Name, ReadOnly, Handle, OpenedReadonly);
+ }
+
+
+ if(!NT_SUCCESS(Status))
+ {
+ if(ARGUMENT_PRESENT(Errno))
+ *Errno = _MapNtStatus(Status);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+//
+// Raw block io. Sets dos errno
+//
+
+static
+BOOLEAN
+_BlockIo(
+ IN HANDLE Handle,
+ IN LARGE_INTEGER Offset,
+ IN ULONG Bytes,
+ IN OUT PCHAR Buffer,
+ IN BOOLEAN Read,
+ OUT unsigned* Errno
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS Status;
+
+ //
+ // Should be aligned
+ //
+
+ ASSERT(0 == (Bytes % 512));
+ ASSERT(0 == (Offset.LowPart % 512));
+
+
+ //
+ // perform io
+ //
+
+ if(Read)
+ {
+ Status = NtReadFile(Handle, NULL, NULL, NULL,
+ &IoStatusBlock, Buffer, Bytes, &Offset, NULL);
+ }
+ else
+ {
+ Status = NtWriteFile(Handle, NULL, NULL, NULL,
+ &IoStatusBlock, Buffer, Bytes, &Offset, NULL);
+ }
+
+
+ //
+ // translate error
+ //
+
+ if(NT_SUCCESS(Status))
+ {
+ *Errno = 0;
+ return TRUE;
+ }
+
+ *Errno = _MapNtStatus(Status);
+
+ return FALSE;
+}
+
+
+
+__inline
+BOOLEAN
+_RawWrite(
+ IN HANDLE Handle,
+ IN LARGE_INTEGER Offset,
+ IN ULONG Bytes,
+ OUT const CHAR* Buffer,
+ OUT unsigned* Errno
+ )
+{
+ return _BlockIo(Handle, Offset, Bytes, (PCHAR)Buffer, FALSE, Errno);
+}
+
+__inline
+BOOLEAN
+_RawRead(
+ IN HANDLE Handle,
+ IN LARGE_INTEGER Offset,
+ IN ULONG Bytes,
+ IN PCHAR Buffer,
+ OUT unsigned* Errno
+ )
+{
+ return _BlockIo(Handle, Offset, Bytes, Buffer, TRUE, Errno);
+}
+
+
+
+__inline
+BOOLEAN
+_SetPartType(
+ IN HANDLE Handle,
+ IN UCHAR Type
+ )
+{
+ IO_STATUS_BLOCK IoStatusBlock;
+ return STATUS_SUCCESS == NtDeviceIoControlFile(
+ Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_SET_PARTITION_INFO,
+ &Type, sizeof(Type),
+ NULL, 0);
+}
+
+
+
+//--------------------- interface part
+
+//
+// Interface functions.
+// Is_mounted is set to 1 if the device is mounted, 0 otherwise
+//
+
+errcode_t
+ext2fs_check_if_mounted(const char *file, int *mount_flags)
+{
+ HANDLE h;
+ BOOLEAN Readonly;
+
+ *mount_flags = 0;
+
+ if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL))
+ {
+ return 0;
+ }
+
+
+ __try{
+ *mount_flags &= _IsMounted(h) ? EXT2_MF_MOUNTED : 0;
+ }
+ __finally{
+ _CloseDisk(h);
+ }
+
+ return 0;
+}
+
+
+
+//
+// Returns the number of blocks in a partition
+//
+
+static __int64 FsSize = 0;
+static char knowndevice[1024] = "";
+
+
+errcode_t
+ext2fs_get_device_size(const char *file, int blocksize,
+ blk_t *retblocks)
+{
+ HANDLE h;
+ BOOLEAN Readonly;
+
+ if((0 == FsSize) || (0 != strcmp(knowndevice, file)))
+ {
+
+ if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL))
+ {
+ return 0;
+ }
+
+
+ __try{
+
+ //
+ // Get size
+ //
+
+ _GetDeviceSize(h, &FsSize);
+ strcpy(knowndevice, file);
+ }
+ __finally{
+ _CloseDisk(h);
+ }
+
+ }
+
+ *retblocks = (blk_t)(unsigned __int64)(FsSize / blocksize);
+ UNREFERENCED_PARAMETER(file);
+ return 0;
+}
+
+
+
+
+
+
+//
+// Table elements
+//
+
+
+static
+errcode_t
+nt_open(const char *name, int flags, io_channel *channel)
+{
+ io_channel io = NULL;
+ PNT_PRIVATE_DATA NtData = NULL;
+ errcode_t Errno = 0;
+
+ //
+ // Check name
+ //
+
+ if (NULL == name)
+ {
+ return EXT2_ET_BAD_DEVICE_NAME;
+ }
+
+ __try{
+
+ //
+ // Allocate channel handle
+ //
+
+ io = (io_channel) malloc(sizeof(struct struct_io_channel));
+
+ if (NULL == io)
+ {
+ Errno = ENOMEM;
+ __leave;
+ }
+
+ RtlZeroMemory(io, sizeof(struct struct_io_channel));
+ io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+
+ NtData = (PNT_PRIVATE_DATA)malloc(sizeof(NT_PRIVATE_DATA));
+
+ if (NULL == NtData)
+ {
+ Errno = ENOMEM;
+ __leave;
+ }
+
+
+ io->manager = nt_io_manager();
+ io->name = malloc(strlen(name) + 1);
+ if (NULL == io->name)
+ {
+ Errno = ENOMEM;
+ __leave;
+ }
+
+ strcpy(io->name, name);
+ io->private_data = NtData;
+ io->block_size = 1024;
+ io->read_error = 0;
+ io->write_error = 0;
+ io->refcount = 1;
+
+ //
+ // Initialize data
+ //
+
+ RtlZeroMemory(NtData, sizeof(NT_PRIVATE_DATA));
+
+ NtData->magic = EXT2_ET_MAGIC_NT_IO_CHANNEL;
+ NtData->BufferBlockNumber = 0xffffffff;
+ NtData->BufferSize = 1024;
+ NtData->Buffer = malloc(NtData->BufferSize);
+
+ if (NULL == NtData->Buffer)
+ {
+ Errno = ENOMEM;
+ __leave;
+ }
+
+ //
+ // Open it
+ //
+
+ if(!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &NtData->Handle, &NtData->OpenedReadonly, &Errno))
+ {
+ __leave;
+ }
+
+
+ //
+ // get size
+ //
+
+ _GetDeviceSize(NtData->Handle, &FsSize);
+ strcpy(knowndevice, name);
+
+
+ //
+ // Lock/dismount
+ //
+
+ if(!NT_SUCCESS(_LockDrive(NtData->Handle)) /*|| !NT_SUCCESS(_DismountDrive(NtData->Handle))*/)
+ {
+ NtData->OpenedReadonly = TRUE;
+ }
+
+ //
+ // Done
+ //
+
+ *channel = io;
+
+
+ }
+ __finally{
+
+ if(0 != Errno)
+ {
+ //
+ // Cleanup
+ //
+
+ if (NULL != io)
+ {
+ free(io->name);
+ free(io);
+ }
+
+ if (NULL != NtData)
+ {
+ if(NULL != NtData->Handle)
+ {
+ _UnlockDrive(NtData->Handle);
+ _CloseDisk(NtData->Handle);
+ }
+
+ free(NtData->Buffer);
+ free(NtData);
+ }
+ }
+ }
+
+ return Errno;
+}
+
+
+//
+// Close api
+//
+
+static
+errcode_t
+nt_close(io_channel channel)
+{
+ PNT_PRIVATE_DATA NtData = NULL;
+
+ if(NULL == channel)
+ {
+ return 0;
+ }
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ NtData = (PNT_PRIVATE_DATA) channel->private_data;
+ EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ {
+ return 0;
+ }
+
+ free(channel->name);
+ free(channel);
+
+ if (NULL != NtData)
+ {
+ if(NULL != NtData->Handle)
+ {
+ _DismountDrive(NtData->Handle);
+ _UnlockDrive(NtData->Handle);
+ _CloseDisk(NtData->Handle);
+ }
+
+ free(NtData->Buffer);
+ free(NtData);
+ }
+
+ return 0;
+}
+
+
+
+//
+// set block size
+//
+
+static
+errcode_t
+nt_set_blksize(io_channel channel, int blksize)
+{
+ PNT_PRIVATE_DATA NtData = NULL;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ NtData = (PNT_PRIVATE_DATA) channel->private_data;
+ EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
+
+ if (channel->block_size != blksize)
+ {
+ channel->block_size = blksize;
+
+ free(NtData->Buffer);
+ NtData->BufferBlockNumber = 0xffffffff;
+ NtData->BufferSize = channel->block_size;
+ ASSERT(0 == (NtData->BufferSize % 512));
+
+ NtData->Buffer = malloc(NtData->BufferSize);
+
+ if (NULL == NtData->Buffer)
+ {
+ return ENOMEM;
+ }
+
+ }
+
+ return 0;
+}
+
+
+//
+// read block
+//
+
+static
+errcode_t
+nt_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ PVOID BufferToRead;
+ ULONG SizeToRead;
+ ULONG Size;
+ LARGE_INTEGER Offset;
+ PNT_PRIVATE_DATA NtData = NULL;
+ unsigned Errno = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ NtData = (PNT_PRIVATE_DATA) channel->private_data;
+ EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
+
+ //
+ // If it's in the cache, use it!
+ //
+
+ if ((1 == count) &&
+ (block == NtData->BufferBlockNumber) &&
+ (NtData->BufferBlockNumber != 0xffffffff))
+ {
+ memcpy(buf, NtData->Buffer, channel->block_size);
+ return 0;
+ }
+
+ Size = (count < 0) ? (ULONG)(-count) : (ULONG)(count * channel->block_size);
+
+ Offset.QuadPart = block * channel->block_size;
+
+ //
+ // If not fit to the block
+ //
+
+ if(Size <= NtData->BufferSize)
+ {
+ //
+ // Update the cache
+ //
+
+ NtData->BufferBlockNumber = block;
+ BufferToRead = NtData->Buffer;
+ SizeToRead = NtData->BufferSize;
+ }
+ else
+ {
+ SizeToRead = Size;
+ BufferToRead = buf;
+ ASSERT(0 == (SizeToRead % channel->block_size));
+ }
+
+ if(!_RawRead(NtData->Handle, Offset, SizeToRead, BufferToRead, &Errno))
+ {
+
+ if (channel->read_error)
+ {
+ return (channel->read_error)(channel, block, count, buf,
+ Size, 0, Errno);
+ }
+ else
+ {
+ return Errno;
+ }
+ }
+
+
+ if(BufferToRead != buf)
+ {
+ ASSERT(Size <= SizeToRead);
+ memcpy(buf, BufferToRead, Size);
+ }
+
+ return 0;
+}
+
+
+//
+// write block
+//
+
+static
+errcode_t
+nt_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ ULONG SizeToWrite;
+ LARGE_INTEGER Offset;
+ PNT_PRIVATE_DATA NtData = NULL;
+ unsigned Errno = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ NtData = (PNT_PRIVATE_DATA) channel->private_data;
+ EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
+
+ if(NtData->OpenedReadonly)
+ {
+ return EACCES;
+ }
+
+ if (count == 1)
+ {
+ SizeToWrite = channel->block_size;
+ }
+ else
+ {
+ NtData->BufferBlockNumber = 0xffffffff;
+
+ if (count < 0)
+ {
+ SizeToWrite = (ULONG)(-count);
+ }
+ else
+ {
+ SizeToWrite = (ULONG)(count * channel->block_size);
+ }
+ }
+
+
+ ASSERT(0 == (SizeToWrite % 512));
+ Offset.QuadPart = block * channel->block_size;
+
+ if(!_RawWrite(NtData->Handle, Offset, SizeToWrite, buf, &Errno))
+ {
+ if (channel->write_error)
+ {
+ return (channel->write_error)(channel, block, count, buf,
+ SizeToWrite, 0, Errno);
+ }
+ else
+ {
+ return Errno;
+ }
+ }
+
+
+ //
+ // Stash a copy.
+ //
+
+ if(SizeToWrite >= NtData->BufferSize)
+ {
+ NtData->BufferBlockNumber = block;
+ memcpy(NtData->Buffer, buf, NtData->BufferSize);
+ }
+
+ NtData->Written = TRUE;
+
+ return 0;
+
+}
+
+
+
+//
+// Flush data buffers to disk. Since we are currently using a
+// write-through cache, this is a no-op.
+//
+
+static
+errcode_t
+nt_flush(io_channel channel)
+{
+ PNT_PRIVATE_DATA NtData = NULL;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ NtData = (PNT_PRIVATE_DATA) channel->private_data;
+ EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL);
+
+ if(NtData->OpenedReadonly)
+ {
+ return 0; // EACCESS;
+ }
+
+
+ //
+ // Flush file buffers.
+ //
+
+ _FlushDrive(NtData->Handle);
+
+
+ //
+ // Test and correct partition type.
+ //
+
+ if(NtData->Written)
+ {
+ _SetPartType(NtData->Handle, 0x83);
+ }
+
+ return 0;
+}
+
+
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
new file mode 100644
index 0000000..fd56a9a
--- /dev/null
+++ b/lib/ext2fs/openfs.c
@@ -0,0 +1,590 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#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, group_zero_adjust = 0;
+ blk64_t ret_blk;
+
+ /*
+ * On a bigalloc FS with 1K blocks, block 0 is reserved for non-ext4
+ * stuff, so adjust for that if we're being asked for group 0.
+ */
+ if (i == 0 && fs->blocksize == 1024 && EXT2FS_CLUSTER_RATIO(fs) > 1)
+ group_zero_adjust = 1;
+
+ if (!ext2fs_has_feature_meta_bg(fs->super) ||
+ (i < fs->super->s_first_meta_bg))
+ return group_block + i + 1 + group_zero_adjust;
+
+ 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);
+ /*
+ * 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 + has_super + fs->super->s_blocks_per_group) <
+ ext2fs_blocks_count(fs->super))) {
+ ret_blk += fs->super->s_blocks_per_group;
+
+ /*
+ * If we're going to jump forward a block group, make sure
+ * that we adjust has_super to account for the next group's
+ * backup superblock (or lack thereof).
+ */
+ if (ext2fs_bg_has_super(fs, bg + 1))
+ has_super = 1;
+ else
+ has_super = 0;
+ }
+ return ret_blk + has_super + group_zero_adjust;
+}
+
+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_manager manager,
+ ext2_filsys *ret_fs)
+{
+ return ext2fs_open2(name, 0, flags, superblock, block_size,
+ manager, ret_fs);
+}
+
+static void block_sha_map_free_entry(void *data)
+{
+ free(data);
+ return;
+}
+
+/*
+ * 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
+ * EXT2_FLAG_SKIP_MMP - Open without multi-mount protection check.
+ * EXT2_FLAG_64BITS - Allow 64-bit bitfields (needed for large
+ * filesystems)
+ */
+errcode_t ext2fs_open2(const char *name, const char *io_options,
+ int flags, int superblock,
+ unsigned int block_size, io_manager manager,
+ ext2_filsys *ret_fs)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned long i, first_meta_bg;
+ __u32 features;
+ unsigned int blocks_per_group, io_flags;
+ blk64_t group_block, blk;
+ char *dest, *cp;
+ int group_zero_adjust = 0;
+ unsigned int inode_size;
+ __u64 groups_cnt;
+#ifdef WORDS_BIGENDIAN
+ unsigned int groups_per_block;
+ struct ext2_group_desc *gdp;
+ int j;
+#endif
+ char *time_env;
+ int csum_retries = 0;
+
+ 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->flags = flags;
+ /* don't overwrite sb backups unless flag is explicitly cleared */
+ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+ fs->umask = 022;
+
+ time_env = getenv("E2FSPROGS_FAKE_TIME");
+ if (time_env)
+ fs->now = strtoul(time_env, NULL, 0);
+
+ 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;
+ if (flags & EXT2_FLAG_THREADS)
+ io_flags |= IO_FLAG_THREADS;
+ 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 = io_channel_alloc_buf(fs->io, -SUPERBLOCK_SIZE, &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 (ext2fs_le32_to_cpu(fs->image_header->magic_number) != EXT2_ET_MAGIC_E2IMAGE)
+ return EXT2_ET_MAGIC_E2IMAGE;
+ superblock = 1;
+ block_size = ext2fs_le32_to_cpu(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;
+ }
+retry:
+ 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);
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS)) {
+ retval = 0;
+ if (!ext2fs_verify_csum_type(fs, fs->super))
+ retval = EXT2_ET_UNKNOWN_CSUM;
+ if (!ext2fs_superblock_csum_verify(fs, fs->super)) {
+ if (csum_retries++ < 3)
+ goto retry;
+ retval = EXT2_ET_SB_CSUM_INVALID;
+ }
+ }
+
+#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;
+ if (retval)
+ 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) &&
+ ext2fs_has_feature_journal_dev(fs->super)) {
+ retval = EXT2_ET_UNSUPP_FEATURE;
+ goto cleanup;
+ }
+ }
+
+ if ((fs->super->s_log_block_size >
+ (unsigned) (EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE)) ||
+ (fs->super->s_log_cluster_size >
+ (unsigned) (EXT2_MAX_CLUSTER_LOG_SIZE - EXT2_MIN_CLUSTER_LOG_SIZE)) ||
+ (fs->super->s_log_block_size > fs->super->s_log_cluster_size) ||
+ (fs->super->s_log_groups_per_flex > 31)) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+
+ /*
+ * bigalloc requires cluster-aware bitfield operations, which at the
+ * moment means we need EXT2_FLAG_64BITS.
+ */
+ if (ext2fs_has_feature_bigalloc(fs->super) &&
+ !(flags & EXT2_FLAG_64BITS)) {
+ retval = EXT2_ET_CANT_USE_LEGACY_BITMAPS;
+ goto cleanup;
+ }
+
+ if (!ext2fs_has_feature_bigalloc(fs->super) &&
+ (fs->super->s_log_block_size != fs->super->s_log_cluster_size)) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+ fs->fragsize = fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
+ inode_size = EXT2_INODE_SIZE(fs->super);
+ if ((inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||
+ (inode_size > fs->blocksize) ||
+ (inode_size & (inode_size - 1))) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+
+ /* Enforce the block group descriptor size */
+ if (!(flags & EXT2_FLAG_IGNORE_SB_ERRORS) &&
+ ext2fs_has_feature_64bit(fs->super)) {
+ unsigned desc_size = fs->super->s_desc_size;
+
+ if ((desc_size < EXT2_MIN_DESC_SIZE_64BIT) ||
+ (desc_size > EXT2_MAX_DESC_SIZE) ||
+ (desc_size & (desc_size - 1)) != 0) {
+ retval = EXT2_ET_BAD_DESC_SIZE;
+ goto cleanup;
+ }
+ }
+
+ fs->cluster_ratio_bits = fs->super->s_log_cluster_size -
+ fs->super->s_log_block_size;
+ if (EXT2_BLOCKS_PER_GROUP(fs->super) !=
+ EXT2_CLUSTERS_PER_GROUP(fs->super) << fs->cluster_ratio_bits) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+ 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 (ext2fs_has_feature_journal_dev(fs->super)) {
+ 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;
+ }
+ /* Precompute the FS UUID to seed other checksums */
+ ext2fs_init_csum_seed(fs);
+
+ /*
+ * Read group descriptors
+ */
+ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super);
+ if (blocks_per_group < 8 ||
+ 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;
+ }
+ groups_cnt = ext2fs_div64_ceil(ext2fs_blocks_count(fs->super) -
+ fs->super->s_first_data_block,
+ blocks_per_group);
+ if (groups_cnt >> 32) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+ fs->group_desc_count = groups_cnt;
+ if (!(flags & EXT2_FLAG_IGNORE_SB_ERRORS) &&
+ (__u64)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));
+ if (ext2fs_has_feature_meta_bg(fs->super) &&
+ (fs->super->s_first_meta_bg > fs->desc_blocks) &&
+ !(flags & EXT2_FLAG_IGNORE_SB_ERRORS)) {
+ retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+ goto cleanup;
+ }
+ if (flags & EXT2_FLAG_SUPER_ONLY)
+ goto skip_read_bg;
+ 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;
+ /*
+ * On a FS with a 1K blocksize, block 0 is reserved for bootloaders
+ * so we must increment block numbers to any group 0 items.
+ *
+ * However, we cannot touch group_block directly because in the meta_bg
+ * case, the ext2fs_descriptor_block_loc2() function will interpret
+ * group_block != s_first_data_block to mean that we want to access the
+ * backup group descriptors. This is not what we want if the caller
+ * set superblock == 0 (i.e. auto-detect the superblock), which is
+ * what's going on here.
+ */
+ if (group_block == 0 && fs->blocksize == 1024)
+ group_zero_adjust = 1;
+ dest = (char *) fs->group_desc;
+#ifdef WORDS_BIGENDIAN
+ groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
+#endif
+ if (ext2fs_has_feature_meta_bg(fs->super) &&
+ !(flags & EXT2_FLAG_IMAGE_FILE)) {
+ first_meta_bg = fs->super->s_first_meta_bg;
+ if (first_meta_bg > fs->desc_blocks)
+ first_meta_bg = fs->desc_blocks;
+ } else
+ first_meta_bg = fs->desc_blocks;
+ if (first_meta_bg) {
+ retval = io_channel_read_blk(fs->io, group_block +
+ group_zero_adjust + 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);
+ if (gdp)
+ 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);
+ io_channel_cache_readahead(fs->io, blk, 1);
+ }
+
+ 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++) {
+ gdp = ext2fs_group_desc(fs, fs->group_desc,
+ i * groups_per_block + j);
+ if (gdp)
+ 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 && ext2fs_has_group_desc_csum(fs)) {
+ 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);
+ /* The checksum will be reset later, but fix it here
+ * anyway to avoid printing a lot of spurious errors. */
+ ext2fs_group_desc_csum_set(fs, group);
+ }
+ if (fs->flags & EXT2_FLAG_RW)
+ ext2fs_mark_super_dirty(fs);
+ }
+skip_read_bg:
+ if (ext2fs_has_feature_mmp(fs->super) &&
+ !(flags & EXT2_FLAG_SKIP_MMP) &&
+ (flags & (EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE))) {
+ retval = ext2fs_mmp_start(fs);
+ if (retval) {
+ fs->flags |= EXT2_FLAG_SKIP_MMP; /* just do cleanup */
+ ext2fs_mmp_stop(fs);
+ goto cleanup;
+ }
+ }
+
+ if (fs->flags & EXT2_FLAG_SHARE_DUP) {
+ fs->block_sha_map = ext2fs_hashmap_create(ext2fs_djb2_hash,
+ block_sha_map_free_entry, 4096);
+ if (!fs->block_sha_map) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto cleanup;
+ }
+ ext2fs_set_feature_shared_blocks(fs->super);
+ }
+
+ if (ext2fs_has_feature_casefold(fs->super))
+ fs->encoding = ext2fs_load_nls_table(fs->super->s_encoding);
+
+ fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR;
+ *ret_fs = fs;
+
+ return 0;
+cleanup:
+ if (!(flags & EXT2_FLAG_NOFREE_ON_ERROR)) {
+ ext2fs_free(fs);
+ fs = NULL;
+ }
+ *ret_fs = 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)
+{
+ errcode_t err;
+
+ if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+ return EXT2_ET_NOT_IMAGE_FILE;
+ err = io_channel_set_blksize(new_io, fs->blocksize);
+ if (err)
+ return err;
+ if ((new_io == fs->image_io) || (new_io == fs->io))
+ return 0;
+ if ((fs->image_io != fs->io) &&
+ fs->image_io)
+ io_channel_close(fs->image_io);
+ if (fs->io)
+ io_channel_close(fs->io);
+ 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/ext2fs/orphan.c b/lib/ext2fs/orphan.c
new file mode 100644
index 0000000..e25f20c
--- /dev/null
+++ b/lib/ext2fs/orphan.c
@@ -0,0 +1,273 @@
+/*
+ * orphan.c --- utility function to handle orphan file
+ *
+ * Copyright (C) 2015 Jan Kara.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t ext2fs_truncate_orphan_file(ext2_filsys fs)
+{
+ struct ext2_inode inode;
+ errcode_t err;
+ ext2_ino_t ino = fs->super->s_orphan_file_inum;
+
+ err = ext2fs_read_inode(fs, ino, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL);
+ if (err)
+ return err;
+
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ err = ext2fs_write_inode(fs, ino, &inode);
+
+ ext2fs_clear_feature_orphan_file(fs->super);
+ ext2fs_clear_feature_orphan_present(fs->super);
+ ext2fs_mark_super_dirty(fs);
+ /* Need to update group descriptors as well */
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+
+ return err;
+}
+
+__u32 ext2fs_do_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino,
+ __u32 gen, blk64_t blk, char *buf)
+{
+ int inodes_per_ob = ext2fs_inodes_per_orphan_block(fs);
+ __u32 crc;
+
+ ino = ext2fs_cpu_to_le32(ino);
+ gen = ext2fs_cpu_to_le32(gen);
+ blk = ext2fs_cpu_to_le64(blk);
+ crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&ino,
+ sizeof(ino));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)&gen, sizeof(gen));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)&blk, sizeof(blk));
+ crc = ext2fs_crc32c_le(crc, (unsigned char *)buf,
+ inodes_per_ob * sizeof(__u32));
+
+ return ext2fs_cpu_to_le32(crc);
+}
+
+struct mkorphan_info {
+ char *buf;
+ char *zerobuf;
+ blk_t num_blocks;
+ blk_t alloc_blocks;
+ blk64_t last_blk;
+ errcode_t err;
+ ext2_ino_t ino;
+ __u32 generation;
+};
+
+static int mkorphan_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 mkorphan_info *oi = (struct mkorphan_info *)priv_data;
+ blk64_t new_blk;
+ errcode_t err;
+
+ /* Can we just continue in currently allocated cluster? */
+ if (blockcnt &&
+ EXT2FS_B2C(fs, oi->last_blk) == EXT2FS_B2C(fs, oi->last_blk + 1)) {
+ new_blk = oi->last_blk + 1;
+ } else {
+ err = ext2fs_new_block2(fs, oi->last_blk, 0, &new_blk);
+ if (err) {
+ oi->err = err;
+ return BLOCK_ABORT;
+ }
+ ext2fs_block_alloc_stats2(fs, new_blk, +1);
+ oi->alloc_blocks++;
+ }
+ if (blockcnt >= 0) {
+ if (ext2fs_has_feature_metadata_csum(fs->super)) {
+ struct ext4_orphan_block_tail *tail;
+
+ tail = ext2fs_orphan_block_tail(fs, oi->buf);
+ tail->ob_checksum = ext2fs_do_orphan_file_block_csum(fs,
+ oi->ino, oi->generation, new_blk, oi->buf);
+ }
+ err = io_channel_write_blk64(fs->io, new_blk, 1, oi->buf);
+ } else /* zerobuf is used to initialize new indirect blocks... */
+ err = io_channel_write_blk64(fs->io, new_blk, 1, oi->zerobuf);
+ if (err) {
+ oi->err = err;
+ return BLOCK_ABORT;
+ }
+ oi->last_blk = new_blk;
+ *blocknr = new_blk;
+ if (blockcnt >= 0 && --oi->num_blocks == 0)
+ return BLOCK_CHANGED | BLOCK_ABORT;
+ return BLOCK_CHANGED;
+}
+
+errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks)
+{
+ struct ext2_inode inode;
+ ext2_ino_t ino = fs->super->s_orphan_file_inum;
+ errcode_t err;
+ char *buf = NULL, *zerobuf = NULL;
+ struct mkorphan_info oi;
+ struct ext4_orphan_block_tail *ob_tail;
+
+ if (!ino) {
+ err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
+ 0, &ino);
+ if (err)
+ return err;
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
+ ext2fs_mark_ib_dirty(fs);
+ }
+
+ err = ext2fs_read_inode(fs, ino, &inode);
+ if (err)
+ return err;
+ if (EXT2_I_SIZE(&inode)) {
+ err = ext2fs_truncate_orphan_file(fs);
+ if (err)
+ return err;
+ }
+
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ if (ext2fs_has_feature_extents(fs->super)) {
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ err = ext2fs_write_inode(fs, ino, &inode);
+ if (err)
+ return err;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &buf);
+ if (err)
+ return err;
+ err = ext2fs_get_mem(fs->blocksize, &zerobuf);
+ if (err)
+ goto out;
+ memset(buf, 0, fs->blocksize);
+ memset(zerobuf, 0, fs->blocksize);
+ ob_tail = ext2fs_orphan_block_tail(fs, buf);
+ ob_tail->ob_magic = ext2fs_cpu_to_le32(EXT4_ORPHAN_BLOCK_MAGIC);
+ oi.num_blocks = num_blocks;
+ oi.alloc_blocks = 0;
+ oi.last_blk = 0;
+ oi.generation = inode.i_generation;
+ oi.ino = ino;
+ oi.buf = buf;
+ oi.zerobuf = zerobuf;
+ oi.err = 0;
+ err = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_APPEND,
+ 0, mkorphan_proc, &oi);
+ if (err)
+ goto out;
+ if (oi.err) {
+ err = oi.err;
+ goto out;
+ }
+
+ /* Reread inode after blocks were allocated */
+ err = ext2fs_read_inode(fs, ino, &inode);
+ if (err)
+ goto out;
+ ext2fs_iblk_set(fs, &inode, 0);
+ inode.i_atime = inode.i_mtime =
+ inode.i_ctime = fs->now ? fs->now : time(0);
+ inode.i_links_count = 1;
+ inode.i_mode = LINUX_S_IFREG | 0600;
+ ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks);
+ err = ext2fs_inode_size_set(fs, &inode,
+ (unsigned long long)fs->blocksize * num_blocks);
+ if (err)
+ goto out;
+ err = ext2fs_write_new_inode(fs, ino, &inode);
+ if (err)
+ goto out;
+
+ fs->super->s_orphan_file_inum = ino;
+ ext2fs_set_feature_orphan_file(fs->super);
+ ext2fs_mark_super_dirty(fs);
+ /* Need to update group descriptors as well */
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+out:
+ if (buf)
+ ext2fs_free_mem(&buf);
+ if (zerobuf)
+ ext2fs_free_mem(&zerobuf);
+ return err;
+}
+
+/*
+ * Find reasonable size for orphan file. We choose orphan file size to be
+ * between 32 and 512 filesystem blocks and not more than 1/4096 of the
+ * filesystem unless it is really small.
+ */
+e2_blkcnt_t ext2fs_default_orphan_file_blocks(ext2_filsys fs)
+{
+ __u64 num_blocks = ext2fs_blocks_count(fs->super);
+ e2_blkcnt_t blks = 512;
+
+ if (num_blocks < 128 * 1024)
+ blks = 32;
+ else if (num_blocks < 2 * 1024 * 1024)
+ blks = num_blocks / 4096;
+ return (blks + EXT2FS_CLUSTER_MASK(fs)) & ~EXT2FS_CLUSTER_MASK(fs);
+}
+
+static errcode_t ext2fs_orphan_file_block_csum(ext2_filsys fs, ext2_ino_t ino,
+ blk64_t blk, char *buf,
+ __u32 *crcp)
+{
+ struct ext2_inode inode;
+ errcode_t retval;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+ *crcp = ext2fs_do_orphan_file_block_csum(fs, ino, inode.i_generation,
+ blk, buf);
+ return 0;
+}
+
+errcode_t ext2fs_orphan_file_block_csum_set(ext2_filsys fs, ext2_ino_t ino,
+ blk64_t blk, char *buf)
+{
+ struct ext4_orphan_block_tail *tail;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 0;
+
+ tail = ext2fs_orphan_block_tail(fs, buf);
+ return ext2fs_orphan_file_block_csum(fs, ino, blk, buf,
+ &tail->ob_checksum);
+}
+
+int ext2fs_orphan_file_block_csum_verify(ext2_filsys fs, ext2_ino_t ino,
+ blk64_t blk, char *buf)
+{
+ struct ext4_orphan_block_tail *tail;
+ __u32 crc;
+ errcode_t retval;
+
+ if (!ext2fs_has_feature_metadata_csum(fs->super))
+ return 1;
+ retval = ext2fs_orphan_file_block_csum(fs, ino, blk, buf, &crc);
+ if (retval)
+ return 0;
+ tail = ext2fs_orphan_block_tail(fs, buf);
+ return ext2fs_le32_to_cpu(tail->ob_checksum) == crc;
+}
diff --git a/lib/ext2fs/progress.c b/lib/ext2fs/progress.c
new file mode 100644
index 0000000..fe4292f
--- /dev/null
+++ b/lib/ext2fs/progress.c
@@ -0,0 +1,103 @@
+/*
+ * 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 "config.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+#include <time.h>
+
+static char spaces[80], backspaces[80];
+static time_t last_update;
+
+struct ext2fs_progress_ops ext2fs_numeric_progress_ops = {
+ .init = ext2fs_numeric_progress_init,
+ .update = ext2fs_numeric_progress_update,
+ .close = ext2fs_numeric_progress_close,
+};
+
+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;
+
+ memset(progress, 0, sizeof(*progress));
+ if (getenv("E2FSPROGS_SKIP_PROGRESS"))
+ progress->skip_progress++;
+
+
+ /*
+ * Figure out how many digits we need
+ */
+ progress->max = max;
+ progress->log_max = int_log10(max);
+
+ if (label) {
+ fputs(label, stdout);
+ fflush(stdout);
+ }
+ last_update = 0;
+}
+
+void ext2fs_numeric_progress_update(ext2_filsys fs,
+ struct ext2fs_numeric_progress_struct * progress,
+ __u64 val)
+{
+ time_t now;
+
+ if (!(fs->flags & EXT2_FLAG_PRINT_PROGRESS))
+ return;
+ if (progress->skip_progress)
+ return;
+ now = time(0);
+ if (now == last_update)
+ return;
+ last_update = now;
+
+ printf("%*llu/%*llu", progress->log_max, (unsigned long long) val,
+ progress->log_max, (unsigned long long) 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/ext2fs/punch.c b/lib/ext2fs/punch.c
new file mode 100644
index 0000000..e2543e1
--- /dev/null
+++ b/lib/ext2fs/punch.c
@@ -0,0 +1,513 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.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,
+ blk64_t start, blk64_t count, int max)
+{
+ errcode_t retval;
+ blk_t b;
+ int i;
+ blk64_t offset, incr;
+ int freed = 0;
+
+#ifdef PUNCH_DEBUG
+ printf("Entering ind_punch, level %d, start %llu, count %llu, "
+ "max %d\n", level, start, count, max);
+#endif
+ incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super) - 2) * level);
+ for (i = 0, offset = 0; i < max; i++, p++, offset += incr) {
+ if (offset >= start + 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 %llu)\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);
+}
+
+#define BLK_T_MAX ((blk_t)~0ULL)
+static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
+ char *block_buf, blk64_t start, blk64_t end)
+{
+ errcode_t retval;
+ char *buf = 0;
+ int level;
+ int num = EXT2_NDIR_BLOCKS;
+ blk_t *bp = inode->i_block;
+ blk_t addr_per_block;
+ blk64_t max = EXT2_NDIR_BLOCKS;
+ blk_t count;
+
+ /* Check start/end don't overflow the 2^32-1 indirect block limit */
+ if (start > BLK_T_MAX)
+ return 0;
+ if (end >= BLK_T_MAX || end - start + 1 >= BLK_T_MAX)
+ count = BLK_T_MAX - start;
+ else
+ count = end - start + 1;
+
+ 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 *= (blk64_t)addr_per_block) {
+#ifdef PUNCH_DEBUG
+ printf("Main loop level %d, start %llu count %u "
+ "max %llu 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;
+}
+#undef BLK_T_MAX
+
+#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
+
+/* Free a range of blocks, respecting cluster boundaries */
+static errcode_t punch_extent_blocks(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ blk64_t lfree_start, blk64_t free_start,
+ __u32 free_count, int *freed)
+{
+ blk64_t pblk;
+ int freed_now = 0;
+ __u32 cluster_freed;
+ errcode_t retval = 0;
+
+ if (free_start < fs->super->s_first_data_block ||
+ (free_start + free_count) >= ext2fs_blocks_count(fs->super))
+ return EXT2_ET_BAD_BLOCK_NUM;
+
+ /* No bigalloc? Just free each block. */
+ if (EXT2FS_CLUSTER_RATIO(fs) == 1) {
+ *freed += free_count;
+ while (free_count-- > 0)
+ ext2fs_block_alloc_stats2(fs, free_start++, -1);
+ return retval;
+ }
+
+ /*
+ * Try to free up to the next cluster boundary. We assume that all
+ * blocks in a logical cluster map to blocks from the same physical
+ * cluster, and that the offsets within the [pl]clusters match.
+ */
+ if (free_start & EXT2FS_CLUSTER_MASK(fs)) {
+ retval = ext2fs_map_cluster_block(fs, ino, inode,
+ lfree_start, &pblk);
+ if (retval)
+ goto errout;
+ if (!pblk) {
+ ext2fs_block_alloc_stats2(fs, free_start, -1);
+ freed_now++;
+ }
+ cluster_freed = EXT2FS_CLUSTER_RATIO(fs) -
+ (free_start & EXT2FS_CLUSTER_MASK(fs));
+ if (cluster_freed > free_count)
+ cluster_freed = free_count;
+ free_count -= cluster_freed;
+ free_start += cluster_freed;
+ lfree_start += cluster_freed;
+ }
+
+ /* Free whole clusters from the middle of the range. */
+ while (free_count > 0 && free_count >= (unsigned) EXT2FS_CLUSTER_RATIO(fs)) {
+ ext2fs_block_alloc_stats2(fs, free_start, -1);
+ freed_now++;
+ cluster_freed = EXT2FS_CLUSTER_RATIO(fs);
+ free_count -= cluster_freed;
+ free_start += cluster_freed;
+ lfree_start += cluster_freed;
+ }
+
+ /* Try to free the last cluster. */
+ if (free_count > 0) {
+ retval = ext2fs_map_cluster_block(fs, ino, inode,
+ lfree_start, &pblk);
+ if (retval)
+ goto errout;
+ if (!pblk) {
+ ext2fs_block_alloc_stats2(fs, free_start, -1);
+ freed_now++;
+ }
+ }
+
+errout:
+ *freed += freed_now;
+ return retval;
+}
+
+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, lfree_start;
+ __u32 free_count, newlen;
+ int freed = 0;
+ int op;
+
+ retval = ext2fs_extent_open2(fs, ino, inode, &handle);
+ if (retval)
+ return retval;
+ /*
+ * Find the extent closest to the start of the punch range. We don't
+ * check the return value because _goto() sets the current node to the
+ * next-lowest extent if 'start' is in a hole, and doesn't set a
+ * current node if there was a real error reading the extent tree.
+ * In that case, _get() will error out.
+ *
+ * Note: If _get() returns 'no current node', that simply means that
+ * there aren't any blocks mapped past this point in the file, so we're
+ * done.
+ */
+ ext2fs_extent_goto(handle, start);
+ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
+ if (retval == EXT2_ET_NO_CURRENT_NODE) {
+ retval = 0;
+ goto errout;
+ } else if (retval)
+ goto errout;
+ while (1) {
+ op = EXT2_EXTENT_NEXT_LEAF;
+ 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) {
+ /*
+ * Have we iterated past the end of the punch region?
+ * If so, we can stop.
+ */
+ if (end < extent.e_lblk)
+ break;
+ dbg_printf("Case #%d\n", 1);
+ /* Start of deleted region before extent;
+ adjust beginning of extent */
+ free_start = extent.e_pblk;
+ lfree_start = extent.e_lblk;
+ 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) {
+ /*
+ * Is the punch region beyond this extent? This can
+ * happen if start is already inside a hole. Try to
+ * advance to the next extent if this is the case.
+ */
+ if (start >= next)
+ goto next_extent;
+ /* End of deleted region after extent;
+ adjust end of extent */
+ dbg_printf("Case #%d\n", 2);
+ newlen = start - extent.e_lblk;
+ free_start = extent.e_pblk + newlen;
+ lfree_start = extent.e_lblk + newlen;
+ free_count = extent.e_len - newlen;
+ extent.e_len = newlen;
+ } else {
+ struct ext2fs_extent newex;
+
+ dbg_printf("Case #%d\n", 3);
+ /* 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;
+ lfree_start = extent.e_lblk + 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;
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval)
+ goto errout;
+ /*
+ * Now pointing at inserted extent; so go back.
+ *
+ * We cannot use EXT2_EXTENT_PREV to go back; note the
+ * subtlety in the comment for fix_parents().
+ */
+ retval = ext2fs_extent_goto(handle, extent.e_lblk);
+ if (retval)
+ goto errout;
+ }
+ if (extent.e_len) {
+ dbg_print_extent("replacing", &extent);
+ retval = ext2fs_extent_replace(handle, 0, &extent);
+ if (retval)
+ goto errout;
+ retval = ext2fs_extent_fix_parents(handle);
+ } else {
+ struct ext2fs_extent newex;
+ blk64_t old_lblk, next_lblk;
+ dbg_printf("deleting current extent%s\n", "");
+
+ /*
+ * Save the location of the next leaf, then slip
+ * back to the current extent.
+ */
+ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &newex);
+ if (retval)
+ goto errout;
+ old_lblk = newex.e_lblk;
+
+ retval = ext2fs_extent_get(handle,
+ EXT2_EXTENT_NEXT_LEAF,
+ &newex);
+ if (retval == EXT2_ET_EXTENT_NO_NEXT)
+ next_lblk = old_lblk;
+ else if (retval)
+ goto errout;
+ else
+ next_lblk = newex.e_lblk;
+
+ retval = ext2fs_extent_goto(handle, old_lblk);
+ if (retval)
+ goto errout;
+
+ /* Now delete the extent. */
+ retval = ext2fs_extent_delete(handle, 0);
+ if (retval)
+ goto errout;
+
+ retval = ext2fs_extent_fix_parents(handle);
+ if (retval && retval != EXT2_ET_NO_CURRENT_NODE)
+ goto errout;
+ retval = 0;
+
+ /*
+ * Jump forward to the next extent. If there are
+ * errors, the ext2fs_extent_get down below will
+ * capture them for us.
+ */
+ (void)ext2fs_extent_goto(handle, next_lblk);
+ op = EXT2_EXTENT_CURRENT;
+ }
+ if (retval)
+ goto errout;
+ dbg_printf("Free start %llu, free count = %u\n",
+ free_start, free_count);
+ retval = punch_extent_blocks(fs, ino, inode, lfree_start,
+ free_start, free_count, &freed);
+ if (retval)
+ goto errout;
+ next_extent:
+ retval = ext2fs_extent_get(handle, op,
+ &extent);
+ if (retval == EXT2_ET_EXTENT_NO_NEXT ||
+ retval == EXT2_ET_NO_CURRENT_NODE)
+ 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;
+}
+
+static errcode_t ext2fs_punch_inline_data(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ blk64_t start,
+ blk64_t end EXT2FS_ATTR((unused)))
+{
+ errcode_t retval;
+
+ /*
+ * In libext2fs ext2fs_punch is based on block unit. So that
+ * means that if start > 0 we don't need to do nothing. Due
+ * to this we will remove all inline data in ext2fs_punch()
+ * now.
+ */
+ if (start > 0)
+ return 0;
+
+ memset((char *)inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ return ext2fs_inline_data_ea_remove(fs, ino);
+}
+
+/*
+ * Deallocate all logical _blocks_ starting at start to end, inclusive.
+ * If end is ~0ULL, then this is effectively truncate.
+ */
+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;
+
+ /* 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_INLINE_DATA_FL)
+ return ext2fs_punch_inline_data(fs, ino, inode, start, end);
+ else if (inode->i_flags & EXT4_EXTENTS_FL)
+ retval = ext2fs_punch_extent(fs, ino, inode, start, end);
+ else
+ retval = ext2fs_punch_ind(fs, inode, block_buf, start, end);
+ if (retval)
+ return retval;
+
+#ifdef PUNCH_DEBUG
+ printf("%u: write inode size now %lu blocks %u\n",
+ ino, EXT2_I_SIZE(inode), inode->i_blocks);
+#endif
+ return ext2fs_write_inode(fs, ino, inode);
+}
diff --git a/lib/ext2fs/qcow2.c b/lib/ext2fs/qcow2.c
new file mode 100644
index 0000000..2082417
--- /dev/null
+++ b/lib/ext2fs/qcow2.c
@@ -0,0 +1,272 @@
+/*
+ * qcow2.c --- Functions to generate qcow2 formatted disk images. This
+ * format is used originally by QEMU for virtual machines, and stores the
+ * filesystem data on disk in a packed format to avoid creating sparse
+ * image files that need lots of seeking to read and write.
+ *
+ * The qcow2 format supports zlib compression, but that is not yet
+ * implemented.
+ *
+ * It is possible to directly mount a qcow2 image using qemu-nbd:
+ *
+ * [root]# modprobe nbd max_part=63
+ * [root]# qemu-nbd -c /dev/nbd0 image.img
+ * [root]# mount /dev/nbd0p1 /mnt/qemu
+ *
+ * Format details at http://people.gnome.org/~markmc/qcow-image-format.html
+ *
+ * Copyright (C) 2010 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include "ext2fs/ext2fs.h"
+#include "qcow2.h"
+
+/* Functions for converting qcow2 image into raw image */
+
+struct ext2_qcow2_hdr *qcow2_read_header(int fd)
+{
+ void *buffer = NULL;
+ struct ext2_qcow2_hdr *hdr = NULL;
+ size_t size;
+ errcode_t ret;
+
+ ret = ext2fs_get_mem(sizeof(struct ext2_qcow2_hdr), &buffer);
+ if (ret)
+ return NULL;
+ memset(buffer, 0, sizeof(struct ext2_qcow2_hdr));
+
+ if (ext2fs_llseek(fd, 0, SEEK_SET < 0)) {
+ ext2fs_free_mem(&buffer);
+ return NULL;
+ }
+
+ size = read(fd, buffer, sizeof(struct ext2_qcow2_hdr));
+ if (size != sizeof(struct ext2_qcow2_hdr)) {
+ ext2fs_free_mem(&buffer);
+ return NULL;
+ }
+
+ hdr = (struct ext2_qcow2_hdr *)(buffer);
+
+ if ((ext2fs_be32_to_cpu(hdr->magic) != QCOW_MAGIC) ||
+ (ext2fs_be32_to_cpu(hdr->version) != 2)) {
+ ext2fs_free_mem(&hdr);
+ return NULL;
+ }
+
+ return hdr;
+}
+
+static int qcow2_read_l1_table(struct ext2_qcow2_image *img)
+{
+ int fd = img->fd;
+ size_t size, l1_size = img->l1_size * sizeof(blk64_t);
+ blk64_t *table;
+ errcode_t ret;
+
+ ret = ext2fs_get_memzero(l1_size, &table);
+ if (ret)
+ return ret;
+
+ if (ext2fs_llseek(fd, img->l1_offset, SEEK_SET) < 0) {
+ ext2fs_free_mem(&table);
+ return errno;
+ }
+
+ size = read(fd, table, l1_size);
+ if (size != l1_size) {
+ ext2fs_free_mem(&table);
+ return errno;
+ }
+
+ img->l1_table = table;
+
+ return 0;
+}
+
+static int qcow2_read_l2_table(struct ext2_qcow2_image *img,
+ __u64 offset, blk64_t **l2_table)
+{
+ int fd = img->fd;
+ size_t size;
+
+ assert(*l2_table);
+
+ if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
+ return errno;
+
+ size = read(fd, *l2_table, img->cluster_size);
+ if (size != img->cluster_size)
+ return errno;
+
+ return 0;
+}
+
+static int qcow2_copy_data(int fdin, int fdout, __u64 off_in,
+ __u64 off_out, void *buf, size_t count)
+{
+ size_t size;
+
+ assert(buf);
+
+ if (ext2fs_llseek(fdout, off_out, SEEK_SET) < 0)
+ return errno;
+
+ if (ext2fs_llseek(fdin, off_in, SEEK_SET) < 0)
+ return errno;
+
+ size = read(fdin, buf, count);
+ if (size != count)
+ return errno;
+
+ size = write(fdout, buf, count);
+ if (size != count)
+ return errno;
+
+ return 0;
+}
+
+
+int qcow2_write_raw_image(int qcow2_fd, int raw_fd,
+ struct ext2_qcow2_hdr *hdr)
+{
+ struct ext2_qcow2_image img;
+ errcode_t ret = 0;
+ unsigned int l1_index, l2_index;
+ __u64 offset;
+ blk64_t *l1_table, *l2_table = NULL;
+ void *copy_buf = NULL;
+ size_t size;
+ unsigned int max_l1_size;
+
+ if (hdr->crypt_method)
+ return -QCOW_ENCRYPTED;
+
+ img.fd = qcow2_fd;
+ img.hdr = hdr;
+ img.l2_cache = NULL;
+ img.l1_table = NULL;
+ img.cluster_bits = ext2fs_be32_to_cpu(hdr->cluster_bits);
+ if (img.cluster_bits < 9 || img.cluster_bits > 31)
+ return -QCOW_CORRUPTED;
+ img.cluster_size = 1 << img.cluster_bits;
+ img.l1_size = ext2fs_be32_to_cpu(hdr->l1_size);
+ img.l1_offset = ext2fs_be64_to_cpu(hdr->l1_table_offset);
+ img.l2_size = 1 << (img.cluster_bits - 3);
+ img.image_size = ext2fs_be64_to_cpu(hdr->size);
+
+ if (img.l1_offset & (img.cluster_size - 1))
+ return -QCOW_CORRUPTED;
+
+ max_l1_size = (img.image_size >> ((2 * img.cluster_bits) - 3)) +
+ img.cluster_size;
+ if (img.l1_size > max_l1_size)
+ return -QCOW_CORRUPTED;
+
+ ret = ext2fs_get_memzero(img.cluster_size, &l2_table);
+ if (ret)
+ goto out;
+
+ ret = ext2fs_get_memzero(1 << img.cluster_bits, &copy_buf);
+ if (ret)
+ goto out;
+
+ if (ext2fs_llseek(raw_fd, 0, SEEK_SET) < 0) {
+ ret = errno;
+ goto out;
+ }
+
+ ret = qcow2_read_l1_table(&img);
+ if (ret)
+ goto out;
+
+ l1_table = img.l1_table;
+ /* Walk through l1 table */
+ for (l1_index = 0; l1_index < img.l1_size; l1_index++) {
+ __u64 off_out;
+
+ offset = ext2fs_be64_to_cpu(l1_table[l1_index]) &
+ ~QCOW_OFLAG_COPIED;
+
+ if ((offset > img.image_size) ||
+ (offset <= 0))
+ continue;
+
+ if (offset & QCOW_OFLAG_COMPRESSED) {
+ ret = -QCOW_COMPRESSED;
+ goto out;
+ }
+
+ ret = qcow2_read_l2_table(&img, offset, &l2_table);
+ if (ret)
+ break;
+
+ /* Walk through l2 table and copy data blocks into raw image */
+ for (l2_index = 0; l2_index < img.l2_size; l2_index++) {
+ offset = ext2fs_be64_to_cpu(l2_table[l2_index]) &
+ ~QCOW_OFLAG_COPIED;
+
+ if (offset == 0)
+ continue;
+
+ off_out = ((__u64)l1_index * img.l2_size) +
+ l2_index;
+ off_out <<= img.cluster_bits;
+ ret = qcow2_copy_data(qcow2_fd, raw_fd, offset,
+ off_out, copy_buf, img.cluster_size);
+ if (ret)
+ goto out;
+ }
+ }
+
+ /* Resize the output image to the filesystem size */
+ if (ext2fs_llseek(raw_fd, img.image_size - 1, SEEK_SET) < 0) {
+ ret = errno;
+ goto out;
+ }
+
+ ((char *)copy_buf)[0] = 0;
+ size = write(raw_fd, copy_buf, 1);
+ if (size != 1) {
+ ret = errno;
+ goto out;
+ }
+
+out:
+ if (copy_buf)
+ ext2fs_free_mem(&copy_buf);
+ if (img.l1_table)
+ ext2fs_free_mem(&img.l1_table);
+ if (l2_table)
+ ext2fs_free_mem(&l2_table);
+ return ret;
+}
diff --git a/lib/ext2fs/qcow2.h b/lib/ext2fs/qcow2.h
new file mode 100644
index 0000000..b649c9c
--- /dev/null
+++ b/lib/ext2fs/qcow2.h
@@ -0,0 +1,114 @@
+/*
+ * qcow2.h --- structures and function prototypes for qcow2.c to generate
+ * qcow2 formatted disk images. This format is used originally by QEMU
+ * for virtual machines, and stores the filesystem data on disk in a
+ * packed format to avoid creating sparse image files that need lots of
+ * seeking to read and write.
+ *
+ * The qcow2 format supports zlib compression, but that is not yet
+ * implemented.
+ *
+ * It is possible to directly mount a qcow2 image using qemu-nbd:
+ *
+ * [root]# modprobe nbd max_part=63
+ * [root]# qemu-nbd -c /dev/nbd0 image.img
+ * [root]# mount /dev/nbd0p1 /mnt/qemu
+ *
+ * Format details at http://people.gnome.org/~markmc/qcow-image-format.html
+ *
+ * Copyright (C) 2010 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+/* Number of l2 tables in memory before writeback */
+#define L2_CACHE_PREALLOC 512
+
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW_VERSION 2
+#define QCOW_OFLAG_COPIED (1ULL << 63)
+#define QCOW_OFLAG_COMPRESSED (1ULL << 62)
+
+#define QCOW_COMPRESSED 1
+#define QCOW_ENCRYPTED 2
+#define QCOW_CORRUPTED 3
+
+struct ext2_qcow2_hdr {
+ __u32 magic;
+ __u32 version;
+
+ __u64 backing_file_offset;
+ __u32 backing_file_size;
+
+ __u32 cluster_bits;
+ __u64 size;
+ __u32 crypt_method;
+
+ __u32 l1_size;
+ __u64 l1_table_offset;
+
+ __u64 refcount_table_offset;
+ __u32 refcount_table_clusters;
+
+ __u32 nb_snapshots;
+ __u64 snapshots_offset;
+};
+
+typedef struct ext2_qcow2_l2_table L2_CACHE_HEAD;
+
+struct ext2_qcow2_l2_table {
+ __u32 l1_index;
+ __u64 offset;
+ __u64 *data;
+ L2_CACHE_HEAD *next;
+};
+
+struct ext2_qcow2_l2_cache {
+ L2_CACHE_HEAD *used_head;
+ L2_CACHE_HEAD *used_tail;
+ L2_CACHE_HEAD *free_head;
+ __u32 free;
+ __u32 count;
+ __u64 next_offset;
+};
+
+struct ext2_qcow2_refcount {
+ __u64 *refcount_table;
+ __u64 refcount_table_offset;
+ __u64 refcount_block_offset;
+
+ __u32 refcount_table_clusters;
+ __u32 refcount_table_index;
+ __u32 refcount_block_index;
+
+ __u16 *refcount_block;
+};
+
+struct ext2_qcow2_image {
+ int fd;
+ struct ext2_qcow2_hdr *hdr;
+ struct ext2_qcow2_l2_cache *l2_cache;
+ struct ext2_qcow2_refcount refcount;
+ __u32 cluster_size;
+ __u32 cluster_bits;
+ __u32 l1_size;
+ __u32 l2_size;
+
+ __u64 *l1_table;
+ __u64 l2_offset;
+ __u64 l1_offset;
+ __u64 image_size;
+};
+
+/* Function prototypes */
+
+/* qcow2.c */
+
+/* Functions for converting qcow2 image into raw image */
+struct ext2_qcow2_hdr *qcow2_read_header(int);
+int qcow2_write_raw_image(int, int, struct ext2_qcow2_hdr *);
+
diff --git a/lib/ext2fs/rbtree.c b/lib/ext2fs/rbtree.c
new file mode 100644
index 0000000..74426fa
--- /dev/null
+++ b/lib/ext2fs/rbtree.c
@@ -0,0 +1,383 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+ (C) 2002 David Woodhouse <dwmw2@infradead.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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
+
+ linux/lib/rbtree.c
+*/
+
+#include "rbtree.h"
+
+static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *right = node->rb_right;
+ struct rb_node *parent = ext2fs_rb_parent(node);
+
+ if ((node->rb_right = right->rb_left))
+ ext2fs_rb_set_parent(right->rb_left, node);
+ right->rb_left = node;
+
+ ext2fs_rb_set_parent(right, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_left)
+ parent->rb_left = right;
+ else
+ parent->rb_right = right;
+ }
+ else
+ root->rb_node = right;
+ ext2fs_rb_set_parent(node, right);
+}
+
+static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *left = node->rb_left;
+ struct rb_node *parent = ext2fs_rb_parent(node);
+
+ if ((node->rb_left = left->rb_right))
+ ext2fs_rb_set_parent(left->rb_right, node);
+ left->rb_right = node;
+
+ ext2fs_rb_set_parent(left, parent);
+
+ if (parent)
+ {
+ if (node == parent->rb_right)
+ parent->rb_right = left;
+ else
+ parent->rb_left = left;
+ }
+ else
+ root->rb_node = left;
+ ext2fs_rb_set_parent(node, left);
+}
+
+void ext2fs_rb_insert_color(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *parent, *gparent;
+
+ while ((parent = ext2fs_rb_parent(node)) && ext2fs_rb_is_red(parent))
+ {
+ gparent = ext2fs_rb_parent(parent);
+
+ if (parent == gparent->rb_left)
+ {
+ {
+ register struct rb_node *uncle = gparent->rb_right;
+ if (uncle && ext2fs_rb_is_red(uncle))
+ {
+ ext2fs_rb_set_black(uncle);
+ ext2fs_rb_set_black(parent);
+ ext2fs_rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_right == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_left(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ ext2fs_rb_set_black(parent);
+ ext2fs_rb_set_red(gparent);
+ __rb_rotate_right(gparent, root);
+ } else {
+ {
+ register struct rb_node *uncle = gparent->rb_left;
+ if (uncle && ext2fs_rb_is_red(uncle))
+ {
+ ext2fs_rb_set_black(uncle);
+ ext2fs_rb_set_black(parent);
+ ext2fs_rb_set_red(gparent);
+ node = gparent;
+ continue;
+ }
+ }
+
+ if (parent->rb_left == node)
+ {
+ register struct rb_node *tmp;
+ __rb_rotate_right(parent, root);
+ tmp = parent;
+ parent = node;
+ node = tmp;
+ }
+
+ ext2fs_rb_set_black(parent);
+ ext2fs_rb_set_red(gparent);
+ __rb_rotate_left(gparent, root);
+ }
+ }
+
+ ext2fs_rb_set_black(root->rb_node);
+}
+
+static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
+ struct rb_root *root)
+{
+ struct rb_node *other;
+
+ while ((!node || ext2fs_rb_is_black(node)) && node != root->rb_node)
+ {
+ if (parent->rb_left == node)
+ {
+ other = parent->rb_right;
+ if (ext2fs_rb_is_red(other))
+ {
+ ext2fs_rb_set_black(other);
+ ext2fs_rb_set_red(parent);
+ __rb_rotate_left(parent, root);
+ other = parent->rb_right;
+ }
+ if ((!other->rb_left || ext2fs_rb_is_black(other->rb_left)) &&
+ (!other->rb_right || ext2fs_rb_is_black(other->rb_right)))
+ {
+ ext2fs_rb_set_red(other);
+ node = parent;
+ parent = ext2fs_rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_right || ext2fs_rb_is_black(other->rb_right))
+ {
+ ext2fs_rb_set_black(other->rb_left);
+ ext2fs_rb_set_red(other);
+ __rb_rotate_right(other, root);
+ other = parent->rb_right;
+ }
+ ext2fs_rb_set_color(other, ext2fs_rb_color(parent));
+ ext2fs_rb_set_black(parent);
+ ext2fs_rb_set_black(other->rb_right);
+ __rb_rotate_left(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ else
+ {
+ other = parent->rb_left;
+ if (ext2fs_rb_is_red(other))
+ {
+ ext2fs_rb_set_black(other);
+ ext2fs_rb_set_red(parent);
+ __rb_rotate_right(parent, root);
+ other = parent->rb_left;
+ }
+ if ((!other->rb_left || ext2fs_rb_is_black(other->rb_left)) &&
+ (!other->rb_right || ext2fs_rb_is_black(other->rb_right)))
+ {
+ ext2fs_rb_set_red(other);
+ node = parent;
+ parent = ext2fs_rb_parent(node);
+ }
+ else
+ {
+ if (!other->rb_left || ext2fs_rb_is_black(other->rb_left))
+ {
+ ext2fs_rb_set_black(other->rb_right);
+ ext2fs_rb_set_red(other);
+ __rb_rotate_left(other, root);
+ other = parent->rb_left;
+ }
+ ext2fs_rb_set_color(other, ext2fs_rb_color(parent));
+ ext2fs_rb_set_black(parent);
+ ext2fs_rb_set_black(other->rb_left);
+ __rb_rotate_right(parent, root);
+ node = root->rb_node;
+ break;
+ }
+ }
+ }
+ if (node)
+ ext2fs_rb_set_black(node);
+}
+
+void ext2fs_rb_erase(struct rb_node *node, struct rb_root *root)
+{
+ struct rb_node *child, *parent;
+ int color;
+
+ if (!node->rb_left)
+ child = node->rb_right;
+ else if (!node->rb_right)
+ child = node->rb_left;
+ else
+ {
+ struct rb_node *old = node, *left;
+
+ node = node->rb_right;
+ while ((left = node->rb_left) != NULL)
+ node = left;
+
+ if (ext2fs_rb_parent(old)) {
+ if (ext2fs_rb_parent(old)->rb_left == old)
+ ext2fs_rb_parent(old)->rb_left = node;
+ else
+ ext2fs_rb_parent(old)->rb_right = node;
+ } else
+ root->rb_node = node;
+
+ child = node->rb_right;
+ parent = ext2fs_rb_parent(node);
+ color = ext2fs_rb_color(node);
+
+ if (parent == old) {
+ parent = node;
+ } else {
+ if (child)
+ ext2fs_rb_set_parent(child, parent);
+ parent->rb_left = child;
+
+ node->rb_right = old->rb_right;
+ ext2fs_rb_set_parent(old->rb_right, node);
+ }
+
+ node->rb_parent_color = old->rb_parent_color;
+ node->rb_left = old->rb_left;
+ ext2fs_rb_set_parent(old->rb_left, node);
+
+ goto color;
+ }
+
+ parent = ext2fs_rb_parent(node);
+ color = ext2fs_rb_color(node);
+
+ if (child)
+ ext2fs_rb_set_parent(child, parent);
+ if (parent)
+ {
+ if (parent->rb_left == node)
+ parent->rb_left = child;
+ else
+ parent->rb_right = child;
+ }
+ else
+ root->rb_node = child;
+
+ color:
+ if (color == RB_BLACK)
+ __rb_erase_color(child, parent, root);
+}
+
+/*
+ * This function returns the first node (in sort order) of the tree.
+ */
+struct rb_node *ext2fs_rb_first(const struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_left)
+ n = n->rb_left;
+ return n;
+}
+
+struct rb_node *ext2fs_rb_last(const struct rb_root *root)
+{
+ struct rb_node *n;
+
+ n = root->rb_node;
+ if (!n)
+ return NULL;
+ while (n->rb_right)
+ n = n->rb_right;
+ return n;
+}
+
+struct rb_node *ext2fs_rb_next(struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (ext2fs_rb_parent(node) == node)
+ return NULL;
+
+ /* If we have a right-hand child, go down and then left as far
+ as we can. */
+ if (node->rb_right) {
+ node = node->rb_right;
+ while (node->rb_left)
+ node=node->rb_left;
+ return (struct rb_node *)node;
+ }
+
+ /* No right-hand children. Everything down and left is
+ smaller than us, so any 'next' node must be in the general
+ direction of our parent. Go up the tree; any time the
+ ancestor is a right-hand child of its parent, keep going
+ up. First time it's a left-hand child of its parent, said
+ parent is our 'next' node. */
+ while ((parent = ext2fs_rb_parent(node)) && node == parent->rb_right)
+ node = parent;
+
+ return parent;
+}
+
+struct rb_node *ext2fs_rb_prev(struct rb_node *node)
+{
+ struct rb_node *parent;
+
+ if (ext2fs_rb_parent(node) == node)
+ return NULL;
+
+ /* If we have a left-hand child, go down and then right as far
+ as we can. */
+ if (node->rb_left) {
+ node = node->rb_left;
+ while (node->rb_right)
+ node=node->rb_right;
+ return (struct rb_node *)node;
+ }
+
+ /* No left-hand children. Go up till we find an ancestor which
+ is a right-hand child of its parent */
+ while ((parent = ext2fs_rb_parent(node)) && node == parent->rb_left)
+ node = parent;
+
+ return parent;
+}
+
+void ext2fs_rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root)
+{
+ struct rb_node *parent = ext2fs_rb_parent(victim);
+
+ /* Set the surrounding nodes to point to the replacement */
+ if (parent) {
+ if (victim == parent->rb_left)
+ parent->rb_left = new;
+ else
+ parent->rb_right = new;
+ } else {
+ root->rb_node = new;
+ }
+ if (victim->rb_left)
+ ext2fs_rb_set_parent(victim->rb_left, new);
+ if (victim->rb_right)
+ ext2fs_rb_set_parent(victim->rb_right, new);
+
+ /* Copy the pointers/colour from the victim to the replacement */
+ *new = *victim;
+}
diff --git a/lib/ext2fs/rbtree.h b/lib/ext2fs/rbtree.h
new file mode 100644
index 0000000..790f5c1
--- /dev/null
+++ b/lib/ext2fs/rbtree.h
@@ -0,0 +1,183 @@
+/*
+ Red Black Trees
+ (C) 1999 Andrea Arcangeli <andrea@suse.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU 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
+
+ linux/include/linux/rbtree.h
+
+ To use rbtrees you'll have to implement your own insert and search cores.
+ This will avoid us to use callbacks and to drop dramatically performances.
+ I know it's not the cleaner way, but in C (not in C++) to get
+ performances and genericity...
+
+ Some example of insert and search follows here. The search is a plain
+ normal search over an ordered tree. The insert instead must be implemented
+ in two steps: First, the code must insert the element in order as a red leaf
+ in the tree, and then the support library function rb_insert_color() must
+ be called. Such function will do the not trivial work to rebalance the
+ rbtree, if necessary.
+
+-----------------------------------------------------------------------
+static inline struct page * rb_search_page_cache(struct inode * inode,
+ unsigned long offset)
+{
+ struct rb_node * n = inode->i_rb_page_cache.rb_node;
+ struct page * page;
+
+ while (n)
+ {
+ page = rb_entry(n, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ n = n->rb_left;
+ else if (offset > page->offset)
+ n = n->rb_right;
+ else
+ return page;
+ }
+ return NULL;
+}
+
+static inline struct page * __rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
+ struct rb_node * parent = NULL;
+ struct page * page;
+
+ while (*p)
+ {
+ parent = *p;
+ page = rb_entry(parent, struct page, rb_page_cache);
+
+ if (offset < page->offset)
+ p = &(*p)->rb_left;
+ else if (offset > page->offset)
+ p = &(*p)->rb_right;
+ else
+ return page;
+ }
+
+ rb_link_node(node, parent, p);
+
+ return NULL;
+}
+
+static inline struct page * rb_insert_page_cache(struct inode * inode,
+ unsigned long offset,
+ struct rb_node * node)
+{
+ struct page * ret;
+ if ((ret = __rb_insert_page_cache(inode, offset, node)))
+ goto out;
+ rb_insert_color(node, &inode->i_rb_page_cache);
+ out:
+ return ret;
+}
+-----------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_RBTREE_H
+#define _LINUX_RBTREE_H
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "compiler.h"
+
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+struct rb_node
+{
+ uintptr_t rb_parent_color;
+#define RB_RED 0
+#define RB_BLACK 1
+ struct rb_node *rb_right;
+ struct rb_node *rb_left;
+} __attribute__((aligned(sizeof(long))));
+ /* The alignment might seem pointless, but allegedly CRIS needs it */
+
+struct rb_root
+{
+ struct rb_node *rb_node;
+};
+
+
+#define ext2fs_rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
+#define ext2fs_rb_color(r) ((r)->rb_parent_color & 1)
+#define ext2fs_rb_is_red(r) (!ext2fs_rb_color(r))
+#define ext2fs_rb_is_black(r) ext2fs_rb_color(r)
+#define ext2fs_rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
+#define ext2fs_rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
+
+static inline void ext2fs_rb_set_parent(struct rb_node *rb, struct rb_node *p)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & 3) | (uintptr_t)p;
+}
+static inline void ext2fs_rb_set_color(struct rb_node *rb, int color)
+{
+ rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
+}
+
+#define RB_ROOT (struct rb_root) { NULL, }
+#define ext2fs_rb_entry(ptr, type, member) container_of(ptr, type, member)
+
+static inline int ext2fs_rb_empty_root(struct rb_root *root)
+{
+ return root->rb_node == NULL;
+}
+
+static inline int ext2fs_rb_empty_node(struct rb_node *node)
+{
+ return ext2fs_rb_parent(node) == node;
+}
+
+static inline void ext2fs_rb_clear_node(struct rb_node *node)
+{
+ ext2fs_rb_set_parent(node, node);
+}
+
+extern void ext2fs_rb_insert_color(struct rb_node *, struct rb_root *);
+extern void ext2fs_rb_erase(struct rb_node *, struct rb_root *);
+
+/* Find logical next and previous nodes in a tree */
+extern struct rb_node *ext2fs_rb_next(struct rb_node *);
+extern struct rb_node *ext2fs_rb_prev(struct rb_node *);
+extern struct rb_node *ext2fs_rb_first(const struct rb_root *);
+extern struct rb_node *ext2fs_rb_last(const struct rb_root *);
+
+/* Fast replacement of a single node without remove/rebalance/add/rebalance */
+extern void ext2fs_rb_replace_node(struct rb_node *victim, struct rb_node *new,
+ struct rb_root *root);
+
+static inline void ext2fs_rb_link_node(struct rb_node * node,
+ struct rb_node * parent,
+ struct rb_node ** rb_link)
+{
+ node->rb_parent_color = (uintptr_t)parent;
+ node->rb_left = node->rb_right = NULL;
+
+ *rb_link = node;
+}
+
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic pop
+#endif
+
+#endif /* _LINUX_RBTREE_H */
diff --git a/lib/ext2fs/read_bb.c b/lib/ext2fs/read_bb.c
new file mode 100644
index 0000000..e58b7cb
--- /dev/null
+++ b/lib/ext2fs/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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 (!(ext2fs_has_feature_huge_file(fs->super) &&
+ (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/ext2fs/read_bb_file.c b/lib/ext2fs/read_bb_file.c
new file mode 100644
index 0000000..a6d3beb
--- /dev/null
+++ b/lib/ext2fs/read_bb_file.c
@@ -0,0 +1,109 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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;
+ unsigned long long 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, "%llu", &blockno);
+ if (count <= 0)
+ continue;
+ /* Badblocks isn't going to be updated for 64bit */
+ if (blockno >> 32)
+ return EOVERFLOW;
+ if (fs &&
+ ((blockno < fs->super->s_first_data_block) ||
+ (blockno >= ext2fs_blocks_count(fs->super)))) {
+ if (invalid)
+ (invalid)(fs, (blk64_t) blockno, buf, priv_data);
+ continue;
+ }
+ retval = ext2fs_badblocks_list_add(*bb_list, (blk64_t) 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/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
new file mode 100644
index 0000000..fa8d8d6
--- /dev/null
+++ b/lib/ext2fs/res_gdt.c
@@ -0,0 +1,239 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#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 (ext2fs_has_feature_sparse_super2(fs->super)) {
+ if (*min == 1) {
+ *min += 1;
+ if (fs->super->s_backup_bgs[0])
+ return fs->super->s_backup_bgs[0];
+ }
+ if (*min == 2) {
+ *min += 1;
+ if (fs->super->s_backup_bgs[1])
+ return fs->super->s_backup_bgs[1];
+ }
+ return fs->group_desc_count;
+ }
+ if (!ext2fs_has_feature_sparse_super(fs->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, *gdt_buf;
+ 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, sb_blk = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ sb = fs->super;
+
+ retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf);
+ if (retval)
+ return retval;
+ gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
+
+ retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+ if (retval)
+ goto out_free;
+
+ /*
+ * File systems with a blocksize of 1024 and bigalloc have
+ * sb->s_first_data_block of 0; yet the superblock is still at
+ * block #1. We compensate for it here.
+ */
+ sb_blk = sb->s_first_data_block;
+ if (fs->blocksize == 1024 && sb_blk == 0)
+ sb_blk = 1;
+
+ /* Maximum possible file size (we only use double indirect 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_blk + 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;
+ retval = ext2fs_inode_size_set(fs, &inode, inode_size);
+ if (retval)
+ goto out_free;
+ inode.i_ctime = fs->now ? fs->now : time(0);
+ }
+
+ for (rsv_off = 0, gdt_off = fs->desc_blocks,
+ gdt_blk = sb_blk + 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 = %lu\n", inode.i_blocks,
+ EXT2_I_SIZE(&inode));
+#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/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
new file mode 100644
index 0000000..1fe65f7
--- /dev/null
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -0,0 +1,682 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "e2image.h"
+
+#ifdef HAVE_PTHREAD
+typedef pthread_mutex_t mutex_t;
+
+static void unix_pthread_mutex_lock(mutex_t *mutex)
+{
+ if (mutex)
+ pthread_mutex_lock(mutex);
+}
+static void unix_pthread_mutex_unlock(mutex_t *mutex)
+{
+ if (mutex)
+ pthread_mutex_unlock(mutex);
+}
+#else
+typedef int mutex_t;
+#define unix_pthread_mutex_lock(mutex_t) do {} while (0)
+#define unix_pthread_mutex_unlock(mutex_t) do {} while (0)
+#endif
+
+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 = NULL, *inode_buf = NULL;
+ int csum_flag;
+ blk64_t blk;
+ blk64_t blk_itr = EXT2FS_B2C(fs, 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;
+
+ csum_flag = ext2fs_has_group_desc_csum(fs);
+
+ inode_nbytes = block_nbytes = 0;
+ if (do_block) {
+ block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
+ retval = io_channel_alloc_buf(fs->io, 0, &block_buf);
+ if (retval)
+ goto errout;
+ memset(block_buf, 0xff, fs->blocksize);
+ }
+ if (do_inode) {
+ inode_nbytes = (size_t)
+ ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
+ retval = io_channel_alloc_buf(fs->io, 0, &inode_buf);
+ if (retval)
+ goto errout;
+ 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)
+ goto errout;
+
+ if (i == fs->group_desc_count - 1) {
+ /* Force bitmap padding for the last group */
+ nbits = EXT2FS_NUM_B2C(fs,
+ ((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);
+ }
+
+ retval = ext2fs_block_bitmap_csum_set(fs, i, block_buf,
+ block_nbytes);
+ if (retval)
+ return retval;
+ ext2fs_group_desc_csum_set(fs, i);
+ fs->flags |= EXT2_FLAG_DIRTY;
+
+ blk = ext2fs_block_bitmap_loc(fs, i);
+ if (blk && blk < ext2fs_blocks_count(fs->super)) {
+ retval = io_channel_write_blk64(fs->io, blk, 1,
+ block_buf);
+ if (retval) {
+ retval = EXT2_ET_BLOCK_BITMAP_WRITE;
+ goto errout;
+ }
+ }
+ 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)
+ goto errout;
+
+ retval = ext2fs_inode_bitmap_csum_set(fs, i, inode_buf,
+ inode_nbytes);
+ if (retval)
+ goto errout;
+ ext2fs_group_desc_csum_set(fs, i);
+ fs->flags |= EXT2_FLAG_DIRTY;
+
+ blk = ext2fs_inode_bitmap_loc(fs, i);
+ if (blk && blk < ext2fs_blocks_count(fs->super)) {
+ retval = io_channel_write_blk64(fs->io, blk, 1,
+ inode_buf);
+ if (retval) {
+ retval = EXT2_ET_INODE_BITMAP_WRITE;
+ goto errout;
+ }
+ }
+ 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;
+errout:
+ if (inode_buf)
+ ext2fs_free_mem(&inode_buf);
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+ return retval;
+}
+
+static errcode_t mark_uninit_bg_group_blocks(ext2_filsys fs)
+{
+ dgrp_t i;
+ blk64_t blk;
+ ext2fs_block_bitmap bmap = fs->block_map;
+
+ for (i = 0; i < fs->group_desc_count; i++) {
+ if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT))
+ continue;
+
+ ext2fs_reserve_super_and_bgd(fs, i, bmap);
+
+ /*
+ * Mark the blocks used for the inode table
+ */
+ blk = ext2fs_inode_table_loc(fs, i);
+ if (blk)
+ ext2fs_mark_block_bitmap_range2(bmap, blk,
+ fs->inode_blocks_per_group);
+
+ /*
+ * Mark block used for the block bitmap
+ */
+ blk = ext2fs_block_bitmap_loc(fs, i);
+ if (blk && blk < ext2fs_blocks_count(fs->super))
+ ext2fs_mark_block_bitmap2(bmap, blk);
+
+ /*
+ * Mark block used for the inode bitmap
+ */
+ blk = ext2fs_inode_bitmap_loc(fs, i);
+ if (blk && blk < ext2fs_blocks_count(fs->super))
+ ext2fs_mark_block_bitmap2(bmap, blk);
+ }
+ return 0;
+}
+
+static int bitmap_tail_verify(unsigned char *bitmap, int first, int last)
+{
+ int i;
+
+ for (i = first; i <= last; i++)
+ if (bitmap[i] != 0xff)
+ return 0;
+ return 1;
+}
+
+static errcode_t read_bitmaps_range_prepare(ext2_filsys fs, int flags)
+{
+ errcode_t retval;
+ int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
+ int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;
+ char *buf;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ if ((block_nbytes > (int) fs->blocksize) ||
+ (inode_nbytes > (int) fs->blocksize))
+ return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+ fs->write_bitmaps = ext2fs_write_bitmaps;
+
+ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+ if (retval)
+ return retval;
+
+ if (flags & EXT2FS_BITMAPS_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 (flags & EXT2FS_BITMAPS_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;
+ }
+ ext2fs_free_mem(&buf);
+ return retval;
+
+cleanup:
+ if (flags & EXT2FS_BITMAPS_BLOCK) {
+ ext2fs_free_block_bitmap(fs->block_map);
+ fs->block_map = 0;
+ }
+ if (flags & EXT2FS_BITMAPS_INODE) {
+ ext2fs_free_inode_bitmap(fs->inode_map);
+ fs->inode_map = 0;
+ }
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+static errcode_t read_bitmaps_range_start(ext2_filsys fs, int flags,
+ dgrp_t start, dgrp_t end,
+ mutex_t *mutex,
+ int *tail_flags)
+{
+ dgrp_t i;
+ char *block_bitmap = 0, *inode_bitmap = 0;
+ errcode_t retval = 0;
+ int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
+ int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;
+ int csum_flag;
+ unsigned int cnt;
+ blk64_t blk;
+ blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+ blk64_t blk_cnt;
+ ext2_ino_t ino_itr = 1;
+ ext2_ino_t ino_cnt;
+
+ csum_flag = ext2fs_has_group_desc_csum(fs);
+
+ if (flags & EXT2FS_BITMAPS_BLOCK) {
+ retval = io_channel_alloc_buf(fs->io, 0, &block_bitmap);
+ if (retval)
+ goto cleanup;
+ } else {
+ block_nbytes = 0;
+ }
+
+ if (flags & EXT2FS_BITMAPS_INODE) {
+ retval = io_channel_alloc_buf(fs->io, 0, &inode_bitmap);
+ if (retval)
+ goto cleanup;
+ } else {
+ inode_nbytes = 0;
+ }
+
+ /* io should be null */
+ if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+ blk = (ext2fs_le32_to_cpu(fs->image_header->offset_inodemap) / fs->blocksize);
+ ino_cnt = fs->super->s_inodes_count;
+ while (inode_bitmap && ino_cnt > 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 += cnt;
+ ino_cnt -= cnt;
+ }
+ blk = (ext2fs_le32_to_cpu(fs->image_header->offset_blockmap) /
+ fs->blocksize);
+ blk_cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super,
+ fs->group_desc_count);
+ while (block_bitmap && blk_cnt > 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 += cnt;
+ blk_cnt -= cnt;
+ }
+ goto cleanup;
+ }
+
+ blk_itr += ((blk64_t)start * (block_nbytes << 3));
+ ino_itr += ((blk64_t)start * (inode_nbytes << 3));
+ for (i = start; i <= end; 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 >= ext2fs_blocks_count(fs->super)))
+ blk = 0;
+ if (blk) {
+ retval = io_channel_read_blk64(fs->io, blk,
+ 1, block_bitmap);
+ if (retval) {
+ retval = EXT2_ET_BLOCK_BITMAP_READ;
+ goto cleanup;
+ }
+ /* verify block bitmap checksum */
+ if (!(fs->flags &
+ EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_block_bitmap_csum_verify(fs, i,
+ block_bitmap, block_nbytes)) {
+ retval =
+ EXT2_ET_BLOCK_BITMAP_CSUM_INVALID;
+ goto cleanup;
+ }
+ if (!bitmap_tail_verify((unsigned char *) block_bitmap,
+ block_nbytes, fs->blocksize - 1))
+ *tail_flags |= EXT2_FLAG_BBITMAP_TAIL_PROBLEM;
+ } else
+ memset(block_bitmap, 0, block_nbytes);
+ cnt = block_nbytes << 3;
+ unix_pthread_mutex_lock(mutex);
+ retval = ext2fs_set_block_bitmap_range2(fs->block_map,
+ blk_itr, cnt, block_bitmap);
+ unix_pthread_mutex_unlock(mutex);
+ 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 >= ext2fs_blocks_count(fs->super)))
+ blk = 0;
+ if (blk) {
+ retval = io_channel_read_blk64(fs->io, blk,
+ 1, inode_bitmap);
+ if (retval) {
+ retval = EXT2_ET_INODE_BITMAP_READ;
+ goto cleanup;
+ }
+
+ /* verify inode bitmap checksum */
+ if (!(fs->flags &
+ EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_inode_bitmap_csum_verify(fs, i,
+ inode_bitmap, inode_nbytes)) {
+ retval =
+ EXT2_ET_INODE_BITMAP_CSUM_INVALID;
+ goto cleanup;
+ }
+ if (!bitmap_tail_verify((unsigned char *) inode_bitmap,
+ inode_nbytes, fs->blocksize - 1))
+ *tail_flags |= EXT2_FLAG_IBITMAP_TAIL_PROBLEM;
+ } else
+ memset(inode_bitmap, 0, inode_nbytes);
+ cnt = inode_nbytes << 3;
+ unix_pthread_mutex_lock(mutex);
+ retval = ext2fs_set_inode_bitmap_range2(fs->inode_map,
+ ino_itr, cnt, inode_bitmap);
+ unix_pthread_mutex_unlock(mutex);
+ if (retval)
+ goto cleanup;
+ ino_itr += inode_nbytes << 3;
+ }
+ }
+
+cleanup:
+ if (inode_bitmap)
+ ext2fs_free_mem(&inode_bitmap);
+ if (block_bitmap)
+ ext2fs_free_mem(&block_bitmap);
+ return retval;
+}
+
+static errcode_t read_bitmaps_range_end(ext2_filsys fs, int flags,
+ int tail_flags)
+{
+ errcode_t retval;
+
+ /* Mark group blocks for any BLOCK_UNINIT groups */
+ if (flags & EXT2FS_BITMAPS_BLOCK) {
+ retval = mark_uninit_bg_group_blocks(fs);
+ if (retval)
+ return retval;
+ fs->flags &= ~EXT2_FLAG_BBITMAP_TAIL_PROBLEM;
+ }
+ if (flags & EXT2FS_BITMAPS_INODE)
+ fs->flags &= ~EXT2_FLAG_IBITMAP_TAIL_PROBLEM;
+ fs->flags |= tail_flags;
+
+ return 0;
+}
+
+static void read_bitmaps_cleanup_on_error(ext2_filsys fs, int flags)
+{
+ if (flags & EXT2FS_BITMAPS_BLOCK) {
+ ext2fs_free_block_bitmap(fs->block_map);
+ fs->block_map = 0;
+ }
+ if (flags & EXT2FS_BITMAPS_INODE) {
+ ext2fs_free_inode_bitmap(fs->inode_map);
+ fs->inode_map = 0;
+ }
+}
+
+static errcode_t read_bitmaps_range(ext2_filsys fs, int flags,
+ dgrp_t start, dgrp_t end)
+{
+ errcode_t retval;
+ int tail_flags = 0;
+
+ retval = read_bitmaps_range_prepare(fs, flags);
+ if (retval)
+ return retval;
+
+ retval = read_bitmaps_range_start(fs, flags, start, end,
+ NULL, &tail_flags);
+ if (retval == 0)
+ retval = read_bitmaps_range_end(fs, flags, tail_flags);
+ if (retval)
+ read_bitmaps_cleanup_on_error(fs, flags);
+ return retval;
+}
+
+#ifdef HAVE_PTHREAD
+struct read_bitmaps_thread_info {
+ ext2_filsys rbt_fs;
+ int rbt_flags;
+ dgrp_t rbt_grp_start;
+ dgrp_t rbt_grp_end;
+ errcode_t rbt_retval;
+ pthread_mutex_t *rbt_mutex;
+ int rbt_tail_flags;
+};
+
+static void *read_bitmaps_thread(void *data)
+{
+ struct read_bitmaps_thread_info *rbt = data;
+
+ rbt->rbt_retval = read_bitmaps_range_start(rbt->rbt_fs, rbt->rbt_flags,
+ rbt->rbt_grp_start, rbt->rbt_grp_end,
+ rbt->rbt_mutex, &rbt->rbt_tail_flags);
+ return NULL;
+}
+#endif
+
+errcode_t ext2fs_rw_bitmaps(ext2_filsys fs, int flags, int num_threads)
+{
+#ifdef HAVE_PTHREAD
+ pthread_attr_t attr;
+ pthread_t *thread_ids = NULL;
+ struct read_bitmaps_thread_info *thread_infos = NULL;
+ pthread_mutex_t rbt_mutex = PTHREAD_MUTEX_INITIALIZER;
+ errcode_t retval;
+ errcode_t rc;
+ unsigned flexbg_size = 1U << fs->super->s_log_groups_per_flex;
+ dgrp_t average_group;
+ int i, tail_flags = 0;
+#endif
+
+ if (flags & ~EXT2FS_BITMAPS_VALID_FLAGS)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (ext2fs_has_feature_journal_dev(fs->super))
+ return EXT2_ET_EXTERNAL_JOURNAL_NOSUPP;
+
+ if (flags & EXT2FS_BITMAPS_WRITE)
+ return write_bitmaps(fs, flags & EXT2FS_BITMAPS_INODE,
+ flags & EXT2FS_BITMAPS_BLOCK);
+
+#ifdef HAVE_PTHREAD
+ if (((fs->io->flags & CHANNEL_FLAGS_THREADS) == 0) ||
+ (num_threads == 1) || (fs->flags & EXT2_FLAG_IMAGE_FILE))
+ goto fallback;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF)
+ if (num_threads < 0)
+ num_threads = sysconf(_SC_NPROCESSORS_CONF);
+#endif
+ /*
+ * Guess for now; eventually we should probably define
+ * ext2fs_get_num_cpus() and teach it how to get this info on
+ * MacOS, FreeBSD, etc.
+ * ref: https://stackoverflow.com/questions/150355
+ */
+ if (num_threads < 0)
+ num_threads = 4;
+
+ if ((unsigned) num_threads > fs->group_desc_count)
+ num_threads = fs->group_desc_count;
+ average_group = fs->group_desc_count / num_threads;
+ if (ext2fs_has_feature_flex_bg(fs->super)) {
+ average_group = (average_group / flexbg_size) * flexbg_size;
+ }
+ if ((num_threads <= 1) || (average_group == 0))
+ goto fallback;
+
+ io_channel_set_options(fs->io, "cache=off");
+ retval = pthread_attr_init(&attr);
+ if (retval)
+ return retval;
+
+ thread_ids = calloc(sizeof(pthread_t), num_threads);
+ if (!thread_ids)
+ return ENOMEM;
+
+ thread_infos = calloc(sizeof(struct read_bitmaps_thread_info),
+ num_threads);
+ if (!thread_infos)
+ goto out;
+
+ retval = read_bitmaps_range_prepare(fs, flags);
+ if (retval)
+ goto out;
+
+// fprintf(stdout, "Multiple threads triggered to read bitmaps\n");
+ for (i = 0; i < num_threads; i++) {
+ thread_infos[i].rbt_fs = fs;
+ thread_infos[i].rbt_flags = flags;
+ thread_infos[i].rbt_mutex = &rbt_mutex;
+ thread_infos[i].rbt_tail_flags = 0;
+ if (i == 0)
+ thread_infos[i].rbt_grp_start = 0;
+ else
+ thread_infos[i].rbt_grp_start = average_group * i + 1;
+
+ if (i == num_threads - 1)
+ thread_infos[i].rbt_grp_end = fs->group_desc_count - 1;
+ else
+ thread_infos[i].rbt_grp_end = average_group * (i + 1);
+ retval = pthread_create(&thread_ids[i], &attr,
+ &read_bitmaps_thread, &thread_infos[i]);
+ if (retval)
+ break;
+ }
+ for (i = 0; i < num_threads; i++) {
+ if (!thread_ids[i])
+ break;
+ rc = pthread_join(thread_ids[i], NULL);
+ if (rc && !retval)
+ retval = rc;
+ rc = thread_infos[i].rbt_retval;
+ if (rc && !retval)
+ retval = rc;
+ tail_flags |= thread_infos[i].rbt_tail_flags;
+ }
+out:
+ rc = pthread_attr_destroy(&attr);
+ if (rc && !retval)
+ retval = rc;
+ free(thread_infos);
+ free(thread_ids);
+
+ if (retval == 0)
+ retval = read_bitmaps_range_end(fs, flags, tail_flags);
+ if (retval)
+ read_bitmaps_cleanup_on_error(fs, flags);
+ /* XXX should save and restore cache setting */
+ io_channel_set_options(fs->io, "cache=on");
+ return retval;
+fallback:
+#endif /* HAVE_PTHREAD */
+ return read_bitmaps_range(fs, flags, 0, fs->group_desc_count - 1);
+}
+
+errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs)
+{
+ return ext2fs_rw_bitmaps(fs, EXT2FS_BITMAPS_INODE, -1);
+}
+
+errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
+{
+ return ext2fs_rw_bitmaps(fs, EXT2FS_BITMAPS_BLOCK, -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)
+{
+ int flags = 0;
+
+ if (!fs->inode_map)
+ flags |= EXT2FS_BITMAPS_INODE;
+ if (!fs->block_map)
+ flags |= EXT2FS_BITMAPS_BLOCK;
+ if (flags == 0)
+ return 0;
+ return ext2fs_rw_bitmaps(fs, flags, -1);
+}
+
+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/ext2fs/sha256.c b/lib/ext2fs/sha256.c
new file mode 100644
index 0000000..b1506e2
--- /dev/null
+++ b/lib/ext2fs/sha256.c
@@ -0,0 +1,254 @@
+/*
+ * sha256.c --- The sh256 algorithm
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ * (copied from libtomcrypt and then relicensed under GPLv2)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "ext2fs.h"
+
+static const __u32 K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Various logical functions */
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x),(n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#define RORc(x, y) ( ((((__u32)(x)&0xFFFFFFFFUL)>>(__u32)((y)&31)) | ((__u32)(x)<<(__u32)(32-((y)&31)))) & 0xFFFFFFFFUL)
+
+#define RND(a,b,c,d,e,f,g,h,i) \
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+ t1 = Sigma0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+#define STORE64H(x, y) \
+ do { \
+ (y)[0] = (unsigned char)(((x)>>56)&255);\
+ (y)[1] = (unsigned char)(((x)>>48)&255);\
+ (y)[2] = (unsigned char)(((x)>>40)&255);\
+ (y)[3] = (unsigned char)(((x)>>32)&255);\
+ (y)[4] = (unsigned char)(((x)>>24)&255);\
+ (y)[5] = (unsigned char)(((x)>>16)&255);\
+ (y)[6] = (unsigned char)(((x)>>8)&255);\
+ (y)[7] = (unsigned char)((x)&255); } while(0)
+
+#define STORE32H(x, y) \
+ do { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \
+ (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } while(0)
+
+#define LOAD32H(x, y) \
+ do { x = ((__u32)((y)[0] & 255)<<24) | \
+ ((__u32)((y)[1] & 255)<<16) | \
+ ((__u32)((y)[2] & 255)<<8) | \
+ ((__u32)((y)[3] & 255)); } while(0)
+
+struct sha256_state {
+ __u64 length;
+ __u32 state[8], curlen;
+ unsigned char buf[64];
+};
+
+/* This is a highly simplified version from libtomcrypt */
+struct hash_state {
+ struct sha256_state sha256;
+};
+
+static void sha256_compress(struct hash_state * md, const unsigned char *buf)
+{
+ __u32 S[8], W[64], t0, t1;
+ __u32 t;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->sha256.state[i];
+ }
+
+ /* copy the state into 512-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD32H(W[i], buf + (4*i));
+ }
+
+ /* fill W[16..63] */
+ for (i = 16; i < 64; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+ /* Compress */
+ for (i = 0; i < 64; ++i) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
+ t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
+ S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->sha256.state[i] = md->sha256.state[i] + S[i];
+ }
+}
+
+static void sha256_init(struct hash_state * md)
+{
+ md->sha256.curlen = 0;
+ md->sha256.length = 0;
+ md->sha256.state[0] = 0x6A09E667UL;
+ md->sha256.state[1] = 0xBB67AE85UL;
+ md->sha256.state[2] = 0x3C6EF372UL;
+ md->sha256.state[3] = 0xA54FF53AUL;
+ md->sha256.state[4] = 0x510E527FUL;
+ md->sha256.state[5] = 0x9B05688CUL;
+ md->sha256.state[6] = 0x1F83D9ABUL;
+ md->sha256.state[7] = 0x5BE0CD19UL;
+}
+
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#define SHA256_BLOCKSIZE 64
+static void sha256_process(struct hash_state * md, const unsigned char *in, unsigned long inlen)
+{
+ unsigned long n;
+
+ while (inlen > 0) {
+ if (md->sha256.curlen == 0 && inlen >= SHA256_BLOCKSIZE) {
+ sha256_compress(md, in);
+ md->sha256.length += SHA256_BLOCKSIZE * 8;
+ in += SHA256_BLOCKSIZE;
+ inlen -= SHA256_BLOCKSIZE;
+ } else {
+ n = MIN(inlen, (SHA256_BLOCKSIZE - md->sha256.curlen));
+ memcpy(md->sha256.buf + md->sha256.curlen, in, (size_t)n);
+ md->sha256.curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->sha256.curlen == SHA256_BLOCKSIZE) {
+ sha256_compress(md, md->sha256.buf);
+ md->sha256.length += 8*SHA256_BLOCKSIZE;
+ md->sha256.curlen = 0;
+ }
+ }
+ }
+}
+
+
+static void sha256_done(struct hash_state * md, unsigned char *out)
+{
+ int i;
+
+ /* increase the length of the message */
+ md->sha256.length += md->sha256.curlen * 8;
+
+ /* append the '1' bit */
+ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 56 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->sha256.curlen > 56) {
+ while (md->sha256.curlen < 64) {
+ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0;
+ }
+ sha256_compress(md, md->sha256.buf);
+ md->sha256.curlen = 0;
+ }
+
+ /* pad up to 56 bytes of zeroes */
+ while (md->sha256.curlen < 56) {
+ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha256.length, md->sha256.buf+56);
+ sha256_compress(md, md->sha256.buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++) {
+ STORE32H(md->sha256.state[i], out+(4*i));
+ }
+}
+
+void ext2fs_sha256(const unsigned char *in, unsigned long in_size,
+ unsigned char out[EXT2FS_SHA256_LENGTH])
+{
+ struct hash_state md;
+
+ sha256_init(&md);
+ sha256_process(&md, in, in_size);
+ sha256_done(&md, out);
+}
+
+#ifdef UNITTEST
+static const struct {
+ char *msg;
+ unsigned char hash[32];
+} tests[] = {
+ { "",
+ { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+ 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+ 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 }
+ },
+ { "abc",
+ { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }
+ },
+ { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }
+ },
+};
+
+int main(int argc, char **argv)
+{
+ int i;
+ int errors = 0;
+ unsigned char tmp[32];
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ unsigned char *msg = (unsigned char *) tests[i].msg;
+ int len = strlen(tests[i].msg);
+
+ ext2fs_sha256(msg, len, tmp);
+ printf("SHA256 test message %d: ", i);
+ if (memcmp(tmp, tests[i].hash, 32) != 0) {
+ printf("FAILED\n");
+ errors++;
+ } else
+ printf("OK\n");
+ }
+ return errors;
+}
+
+#endif /* UNITTEST */
diff --git a/lib/ext2fs/sha512.c b/lib/ext2fs/sha512.c
new file mode 100644
index 0000000..f246afb
--- /dev/null
+++ b/lib/ext2fs/sha512.c
@@ -0,0 +1,302 @@
+/*
+ * sha512.c --- The sha512 algorithm
+ *
+ * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
+ * (copied from libtomcrypt and then relicensed under GPLv2)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+
+#include "config.h"
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include "ext2fs.h"
+
+/* the K array */
+#define CONST64(n) n
+static const __u64 K[80] = {
+ CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+ CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+ CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+ CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+ CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+ CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+ CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+ CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+ CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+ CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+ CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+ CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+ CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+ CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+ CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+ CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+ CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+ CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+ CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+ CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+ CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+ CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+ CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+ CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+ CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+ CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+ CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+ CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+ CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+ CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+ CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+ CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+ CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+ CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+ CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+ CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+ CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+ CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+ CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+ CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) ROR64c(x, n)
+#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)n))
+#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#define RND(a,b,c,d,e,f,g,h,i)\
+ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];\
+ t1 = Sigma0(a) + Maj(a, b, c);\
+ d += t0;\
+ h = t0 + t1;
+#define STORE64H(x, y) \
+ do { \
+ (y)[0] = (unsigned char)(((x)>>56)&255);\
+ (y)[1] = (unsigned char)(((x)>>48)&255);\
+ (y)[2] = (unsigned char)(((x)>>40)&255);\
+ (y)[3] = (unsigned char)(((x)>>32)&255);\
+ (y)[4] = (unsigned char)(((x)>>24)&255);\
+ (y)[5] = (unsigned char)(((x)>>16)&255);\
+ (y)[6] = (unsigned char)(((x)>>8)&255);\
+ (y)[7] = (unsigned char)((x)&255); } while(0)
+
+#define LOAD64H(x, y)\
+ do {x = \
+ (((__u64)((y)[0] & 255)) << 56) |\
+ (((__u64)((y)[1] & 255)) << 48) |\
+ (((__u64)((y)[2] & 255)) << 40) |\
+ (((__u64)((y)[3] & 255)) << 32) |\
+ (((__u64)((y)[4] & 255)) << 24) |\
+ (((__u64)((y)[5] & 255)) << 16) |\
+ (((__u64)((y)[6] & 255)) << 8) |\
+ (((__u64)((y)[7] & 255)));\
+ } while(0)
+
+#define ROR64c(x, y) \
+ ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((__u64)(y)&CONST64(63))) | \
+ ((x)<<((__u64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF))
+
+struct sha512_state {
+ __u64 length, state[8];
+ unsigned long curlen;
+ unsigned char buf[128];
+};
+
+/* This is a highly simplified version from libtomcrypt */
+struct hash_state {
+ struct sha512_state sha512;
+};
+
+static void sha512_compress(struct hash_state * md, const unsigned char *buf)
+{
+ __u64 S[8], W[80], t0, t1;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->sha512.state[i];
+ }
+
+ /* copy the state into 1024-bits into W[0..15] */
+ for (i = 0; i < 16; i++) {
+ LOAD64H(W[i], buf + (8*i));
+ }
+
+ /* fill W[16..79] */
+ for (i = 16; i < 80; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] +
+ Gamma0(W[i - 15]) + W[i - 16];
+ }
+
+ for (i = 0; i < 80; i += 8) {
+ RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
+ RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
+ RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
+ RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
+ RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
+ RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
+ RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
+ RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->sha512.state[i] = md->sha512.state[i] + S[i];
+ }
+}
+
+static void sha512_init(struct hash_state * md)
+{
+ md->sha512.curlen = 0;
+ md->sha512.length = 0;
+ md->sha512.state[0] = CONST64(0x6a09e667f3bcc908);
+ md->sha512.state[1] = CONST64(0xbb67ae8584caa73b);
+ md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b);
+ md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1);
+ md->sha512.state[4] = CONST64(0x510e527fade682d1);
+ md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f);
+ md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b);
+ md->sha512.state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+static void sha512_done(struct hash_state * md, unsigned char *out)
+{
+ int i;
+
+ /* increase the length of the message */
+ md->sha512.length += md->sha512.curlen * CONST64(8);
+
+ /* append the '1' bit */
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
+
+ /* if the length is currently above 112 bytes we append zeros then
+ * compress. Then we can fall back to padding zeros and length encoding
+ * like normal. */
+ if (md->sha512.curlen > 112) {
+ while (md->sha512.curlen < 128) {
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+ }
+ sha512_compress(md, md->sha512.buf);
+ md->sha512.curlen = 0;
+ }
+
+ /* pad up to 120 bytes of zeroes note: that from 112 to 120 is the 64 MSB
+ * of the length. We assume that you won't hash > 2^64 bits of data. */
+ while (md->sha512.curlen < 120) {
+ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
+ }
+
+ /* store length */
+ STORE64H(md->sha512.length, md->sha512.buf + 120);
+ sha512_compress(md, md->sha512.buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++) {
+ STORE64H(md->sha512.state[i], out+(8 * i));
+ }
+}
+
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#define SHA512_BLOCKSIZE 128
+static void sha512_process(struct hash_state * md,
+ const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+ while (inlen > 0) {
+ if (md->sha512.curlen == 0 && inlen >= SHA512_BLOCKSIZE) {
+ sha512_compress(md, in);
+ md->sha512.length += SHA512_BLOCKSIZE * 8;
+ in += SHA512_BLOCKSIZE;
+ inlen -= SHA512_BLOCKSIZE;
+ } else {
+ n = MIN(inlen, (SHA512_BLOCKSIZE - md->sha512.curlen));
+ memcpy(md->sha512.buf + md->sha512.curlen,
+ in, (size_t)n);
+ md->sha512.curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->sha512.curlen == SHA512_BLOCKSIZE) {
+ sha512_compress(md, md->sha512.buf);
+ md->sha512.length += SHA512_BLOCKSIZE * 8;
+ md->sha512.curlen = 0;
+ }
+ }
+ }
+}
+
+void ext2fs_sha512(const unsigned char *in, unsigned long in_size,
+ unsigned char out[EXT2FS_SHA512_LENGTH])
+{
+ struct hash_state md;
+
+ sha512_init(&md);
+ sha512_process(&md, in, in_size);
+ sha512_done(&md, out);
+}
+
+#ifdef UNITTEST
+static const struct {
+ char *msg;
+ unsigned char hash[64];
+} tests[] = {
+ { "",
+ { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd,
+ 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07,
+ 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc,
+ 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce,
+ 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0,
+ 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f,
+ 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81,
+ 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e }
+ },
+ { "abc",
+ { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba,
+ 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31,
+ 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+ 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a,
+ 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8,
+ 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+ 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e,
+ 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f }
+ },
+ { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
+ { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda,
+ 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f,
+ 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+ 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18,
+ 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4,
+ 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+ 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54,
+ 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 }
+ },
+};
+
+int main(int argc, char **argv)
+{
+ int i;
+ int errors = 0;
+ unsigned char tmp[64];
+
+ for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
+ unsigned char *msg = (unsigned char *) tests[i].msg;
+ int len = strlen(tests[i].msg);
+
+ ext2fs_sha512(msg, len, tmp);
+ printf("SHA512 test message %d: ", i);
+ if (memcmp(tmp, tests[i].hash, 64) != 0) {
+ printf("FAILED\n");
+ errors++;
+ } else
+ printf("OK\n");
+ }
+ return errors;
+}
+
+#endif /* UNITTEST */
diff --git a/lib/ext2fs/sparse_io.c b/lib/ext2fs/sparse_io.c
new file mode 100644
index 0000000..f287e76
--- /dev/null
+++ b/lib/ext2fs/sparse_io.c
@@ -0,0 +1,554 @@
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if !defined(ENABLE_LIBSPARSE)
+static errcode_t sparse_open(const char *name EXT2FS_ATTR((unused)),
+ int flags EXT2FS_ATTR((unused)),
+ io_channel *channel EXT2FS_ATTR((unused)))
+{
+ return EXT2_ET_UNIMPLEMENTED;
+}
+static errcode_t sparse_close(io_channel channel EXT2FS_ATTR((unused)))
+{
+ return EXT2_ET_UNIMPLEMENTED;
+}
+static struct struct_io_manager struct_sparse_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Android sparse I/O Manager",
+ .open = sparse_open,
+ .close = sparse_close,
+};
+static struct struct_io_manager struct_sparsefd_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Android sparse fd I/O Manager",
+ .open = sparse_open,
+ .close = sparse_close,
+};
+#else
+#include <sparse/sparse.h>
+
+struct sparse_map {
+ int fd;
+ char **blocks;
+ int block_size;
+ uint64_t blocks_count;
+ char *file;
+ struct sparse_file *sparse_file;
+ io_channel channel;
+};
+
+struct sparse_io_params {
+ int fd;
+ char *file;
+ uint64_t blocks_count;
+ unsigned int block_size;
+};
+
+static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf);
+
+static void free_sparse_blocks(struct sparse_map *sm)
+{
+ uint64_t i;
+
+ for (i = 0; i < sm->blocks_count; ++i)
+ free(sm->blocks[i]);
+ free(sm->blocks);
+ sm->blocks = NULL;
+}
+
+static int sparse_import_segment(void *priv, const void *data, size_t len,
+ unsigned int block, unsigned int nr_blocks)
+{
+ struct sparse_map *sm = priv;
+
+ /* Ignore chunk headers, only write the data */
+ if (!nr_blocks || len % sm->block_size)
+ return 0;
+
+ return sparse_write_blk(sm->channel, block, nr_blocks, data);
+}
+
+static errcode_t io_manager_import_sparse(struct sparse_io_params *params,
+ struct sparse_map *sm, io_channel io)
+{
+ int fd;
+ errcode_t retval;
+ struct sparse_file *sparse_file;
+
+ if (params->fd < 0) {
+ fd = open(params->file, O_RDONLY);
+ if (fd < 0) {
+ retval = -1;
+ goto err_open;
+ }
+ } else
+ fd = params->fd;
+ sparse_file = sparse_file_import(fd, false, false);
+ if (!sparse_file) {
+ retval = -1;
+ goto err_sparse;
+ }
+
+ sm->block_size = sparse_file_block_size(sparse_file);
+ sm->blocks_count = (sparse_file_len(sparse_file, 0, 0) - 1)
+ / sm->block_size + 1;
+ sm->blocks = calloc(sm->blocks_count, sizeof(char*));
+ if (!sm->blocks) {
+ retval = -1;
+ goto err_alloc;
+ }
+ io->block_size = sm->block_size;
+
+ retval = sparse_file_foreach_chunk(sparse_file, true, false,
+ sparse_import_segment, sm);
+
+ if (retval)
+ free_sparse_blocks(sm);
+err_alloc:
+ sparse_file_destroy(sparse_file);
+err_sparse:
+ close(fd);
+err_open:
+ return retval;
+}
+
+static errcode_t io_manager_configure(struct sparse_io_params *params,
+ int flags, io_channel io)
+{
+ errcode_t retval;
+ uint64_t img_size;
+ struct sparse_map *sm = calloc(1, sizeof(*sm));
+ if (!sm)
+ return EXT2_ET_NO_MEMORY;
+
+ sm->file = params->file;
+ sm->channel = io;
+ io->private_data = sm;
+ retval = io_manager_import_sparse(params, sm, io);
+ if (retval) {
+ if (!params->block_size || !params->blocks_count) {
+ retval = EINVAL;
+ goto err_params;
+ }
+ sm->block_size = params->block_size;
+ sm->blocks_count = params->blocks_count;
+ sm->blocks = calloc(params->blocks_count, sizeof(void*));
+ if (!sm->blocks) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto err_alloc;
+ }
+ }
+ io->block_size = sm->block_size;
+ img_size = (uint64_t)sm->block_size * sm->blocks_count;
+
+ if (flags & IO_FLAG_RW) {
+ sm->sparse_file = sparse_file_new(sm->block_size, img_size);
+ if (!sm->sparse_file) {
+ retval = EXT2_ET_NO_MEMORY;
+ goto err_alloc;
+ }
+ if (params->fd < 0) {
+ sm->fd = open(params->file, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
+ 0644);
+ if (sm->fd < 0) {
+ retval = errno;
+ goto err_open;
+ }
+ } else
+ sm->fd = params->fd;
+ } else {
+ sm->fd = -1;
+ sm->sparse_file = NULL;
+ }
+ return 0;
+
+err_open:
+ sparse_file_destroy(sm->sparse_file);
+err_alloc:
+ free_sparse_blocks(sm);
+err_params:
+ free(sm);
+ return retval;
+}
+
+static errcode_t sparse_open_channel(struct sparse_io_params *sparse_params,
+ int flags, io_channel *channel)
+{
+ errcode_t retval;
+ io_channel io;
+
+ io = calloc(1, sizeof(struct struct_io_channel));
+ io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+ io->block_size = 0;
+ io->refcount = 1;
+
+ retval = io_manager_configure(sparse_params, flags, io);
+ if (retval) {
+ free(io);
+ return retval;
+ }
+
+ *channel = io;
+ return 0;
+}
+
+static errcode_t read_sparse_argv(const char *name, bool is_fd,
+ struct sparse_io_params *sparse_params)
+{
+ int ret;
+ sparse_params->fd = -1;
+ sparse_params->block_size = 0;
+ sparse_params->blocks_count = 0;
+
+ sparse_params->file = malloc(strlen(name) + 1);
+ if (!sparse_params->file) {
+ fprintf(stderr, "failed to alloc %zu\n", strlen(name) + 1);
+ return EXT2_ET_NO_MEMORY;
+ }
+
+ if (is_fd) {
+ ret = sscanf(name, "(%d):%llu:%u", &sparse_params->fd,
+ (unsigned long long *)&sparse_params->blocks_count,
+ &sparse_params->block_size);
+ } else {
+ ret = sscanf(name, "(%[^)])%*[:]%llu%*[:]%u", sparse_params->file,
+ (unsigned long long *)&sparse_params->blocks_count,
+ &sparse_params->block_size);
+ }
+
+ if (ret < 1) {
+ free(sparse_params->file);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static errcode_t sparse_open(const char *name, int flags, io_channel *channel)
+{
+ errcode_t retval;
+ struct sparse_io_params sparse_params;
+
+ retval = read_sparse_argv(name, false, &sparse_params);
+ if (retval)
+ return EXT2_ET_BAD_DEVICE_NAME;
+
+ retval = sparse_open_channel(&sparse_params, flags, channel);
+ if (retval)
+ return retval;
+ (*channel)->manager = sparse_io_manager;
+
+ return retval;
+}
+
+static errcode_t sparsefd_open(const char *name, int flags, io_channel *channel)
+{
+ errcode_t retval;
+ struct sparse_io_params sparse_params;
+
+ retval = read_sparse_argv(name, true, &sparse_params);
+ if (retval)
+ return EXT2_ET_BAD_DEVICE_NAME;
+
+ retval = sparse_open_channel(&sparse_params, flags, channel);
+ if (retval)
+ return retval;
+ (*channel)->manager = sparsefd_io_manager;
+
+ return retval;
+}
+
+static errcode_t sparse_merge_blocks(struct sparse_map *sm, uint64_t start,
+ uint64_t num)
+{
+ char *buf;
+ uint64_t i;
+ unsigned int block_size = sm->block_size;
+ errcode_t retval = 0;
+
+ buf = calloc(num, block_size);
+ if (!buf) {
+ fprintf(stderr, "failed to alloc %llu\n",
+ (unsigned long long)num * block_size);
+ return EXT2_ET_NO_MEMORY;
+ }
+
+ for (i = 0; i < num; i++) {
+ memcpy(buf + i * block_size, sm->blocks[start + i] , block_size);
+ free(sm->blocks[start + i]);
+ sm->blocks[start + i] = NULL;
+ }
+
+ /* free_sparse_blocks will release this buf. */
+ sm->blocks[start] = buf;
+
+ retval = sparse_file_add_data(sm->sparse_file, sm->blocks[start],
+ block_size * num, start);
+
+ return retval;
+}
+
+static errcode_t sparse_close_channel(io_channel channel)
+{
+ uint64_t i;
+ errcode_t retval = 0;
+ struct sparse_map *sm = channel->private_data;
+
+ if (sm->sparse_file) {
+ int64_t chunk_start = (sm->blocks[0] == NULL) ? -1 : 0;
+ for (i = 0; i < sm->blocks_count; ++i) {
+ if (!sm->blocks[i] && chunk_start != -1) {
+ retval = sparse_merge_blocks(sm, chunk_start, i - chunk_start);
+ chunk_start = -1;
+ } else if (sm->blocks[i] && chunk_start == -1) {
+ chunk_start = i;
+ }
+ if (retval)
+ goto ret;
+ }
+ if (chunk_start != -1) {
+ retval = sparse_merge_blocks(sm, chunk_start,
+ sm->blocks_count - chunk_start);
+ if (retval)
+ goto ret;
+ }
+ retval = sparse_file_write(sm->sparse_file, sm->fd,
+ /*gzip*/0, /*sparse*/1, /*crc*/0);
+ }
+
+ret:
+ if (sm->sparse_file)
+ sparse_file_destroy(sm->sparse_file);
+ free_sparse_blocks(sm);
+ free(sm->file);
+ free(sm);
+ free(channel);
+ return retval;
+}
+
+static errcode_t sparse_close(io_channel channel)
+{
+ errcode_t retval;
+ struct sparse_map *sm = channel->private_data;
+ int fd = sm->fd;
+
+ retval = sparse_close_channel(channel);
+ if (fd >= 0)
+ close(fd);
+
+ return retval;
+}
+
+static errcode_t sparse_set_blksize(io_channel channel, int blksize)
+{
+ channel->block_size = blksize;
+ return 0;
+}
+
+static blk64_t block_to_sparse_block(blk64_t block, blk64_t *offset,
+ io_channel channel, struct sparse_map *sm)
+{
+ int ratio;
+ blk64_t ret = block;
+
+ ratio = sm->block_size / channel->block_size;
+ ret /= ratio;
+ *offset = (block % ratio) * channel->block_size;
+
+ return ret;
+}
+
+static errcode_t check_block_size(io_channel channel, struct sparse_map *sm)
+{
+ if (sm->block_size >= channel->block_size)
+ return 0;
+ return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+}
+
+static errcode_t sparse_read_blk64(io_channel channel, blk64_t block,
+ int count, void *buf)
+{
+ int i;
+ char *out = buf;
+ blk64_t offset = 0, cur_block;
+ struct sparse_map *sm = channel->private_data;
+
+ if (check_block_size(channel, sm))
+ return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+
+ if (count < 0) { //partial read
+ count = -count;
+ cur_block = block_to_sparse_block(block, &offset, channel, sm);
+ if (sm->blocks[cur_block])
+ memcpy(out, (sm->blocks[cur_block]) + offset, count);
+ else
+ memset(out, 0, count);
+ } else {
+ for (i = 0; i < count; ++i) {
+ cur_block = block_to_sparse_block(block + i, &offset,
+ channel, sm);
+ if (sm->blocks[cur_block])
+ memcpy(out + (i * channel->block_size),
+ sm->blocks[cur_block] + offset,
+ channel->block_size);
+ else if (sm->blocks)
+ memset(out + (i * channel->block_size), 0,
+ channel->block_size);
+ }
+ }
+ return 0;
+}
+
+static errcode_t sparse_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ return sparse_read_blk64(channel, block, count, buf);
+}
+
+static errcode_t sparse_write_blk64(io_channel channel, blk64_t block,
+ int count, const void *buf)
+{
+ int i;
+ blk64_t offset = 0, cur_block;
+ const char *in = buf;
+ struct sparse_map *sm = channel->private_data;
+
+ if (check_block_size(channel, sm))
+ return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+
+ if (count < 0) { //partial write
+ count = -count;
+ cur_block = block_to_sparse_block(block, &offset, channel,
+ sm);
+ if (!sm->blocks[cur_block]) {
+ sm->blocks[cur_block] = calloc(1, sm->block_size);
+ if (!sm->blocks[cur_block])
+ return EXT2_ET_NO_MEMORY;
+ }
+ memcpy(sm->blocks[cur_block] + offset, in, count);
+ } else {
+ for (i = 0; i < count; ++i) {
+ if (block + i >= sm->blocks_count)
+ return 0;
+ cur_block = block_to_sparse_block(block + i, &offset,
+ channel, sm);
+ if (!sm->blocks[cur_block]) {
+ sm->blocks[cur_block] =
+ calloc(1, sm->block_size);
+ if (!sm->blocks[cur_block])
+ return EXT2_ET_NO_MEMORY;
+ }
+ memcpy(sm->blocks[cur_block] + offset,
+ in + (i * channel->block_size),
+ channel->block_size);
+ }
+ }
+ return 0;
+}
+
+static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ return sparse_write_blk64(channel, block, count, buf);
+}
+
+static errcode_t sparse_discard(io_channel channel __attribute__((unused)),
+ blk64_t blk, unsigned long long count)
+{
+ blk64_t cur_block, offset;
+ struct sparse_map *sm = channel->private_data;
+
+ if (check_block_size(channel, sm))
+ return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+
+ for (unsigned long long i = 0; i < count; ++i) {
+ if (blk + i >= sm->blocks_count)
+ return 0;
+ cur_block = block_to_sparse_block(blk + i, &offset, channel,
+ sm);
+ if (!sm->blocks[cur_block])
+ continue;
+ free(sm->blocks[cur_block]);
+ sm->blocks[cur_block] = NULL;
+ }
+ return 0;
+}
+
+static errcode_t sparse_zeroout(io_channel channel, blk64_t blk,
+ unsigned long long count)
+{
+ return sparse_discard(channel, blk, count);
+}
+
+static errcode_t sparse_flush(io_channel channel __attribute__((unused)))
+{
+ return 0;
+}
+
+static errcode_t sparse_set_option(io_channel channel __attribute__((unused)),
+ const char *option __attribute__((unused)),
+ const char *arg __attribute__((unused)))
+{
+ return 0;
+}
+
+static errcode_t sparse_cache_readahead(
+ io_channel channel __attribute__((unused)),
+ blk64_t blk __attribute__((unused)),
+ unsigned long long count __attribute__((unused)))
+{
+ return 0;
+}
+
+static struct struct_io_manager struct_sparse_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Android sparse I/O Manager",
+ .open = sparse_open,
+ .close = sparse_close,
+ .set_blksize = sparse_set_blksize,
+ .read_blk = sparse_read_blk,
+ .write_blk = sparse_write_blk,
+ .flush = sparse_flush,
+ .write_byte = NULL,
+ .set_option = sparse_set_option,
+ .get_stats = NULL,
+ .read_blk64 = sparse_read_blk64,
+ .write_blk64 = sparse_write_blk64,
+ .discard = sparse_discard,
+ .cache_readahead = sparse_cache_readahead,
+ .zeroout = sparse_zeroout,
+};
+
+static struct struct_io_manager struct_sparsefd_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Android sparse fd I/O Manager",
+ .open = sparsefd_open,
+ .close = sparse_close,
+ .set_blksize = sparse_set_blksize,
+ .read_blk = sparse_read_blk,
+ .write_blk = sparse_write_blk,
+ .flush = sparse_flush,
+ .write_byte = NULL,
+ .set_option = sparse_set_option,
+ .get_stats = NULL,
+ .read_blk64 = sparse_read_blk64,
+ .write_blk64 = sparse_write_blk64,
+ .discard = sparse_discard,
+ .cache_readahead = sparse_cache_readahead,
+ .zeroout = sparse_zeroout,
+};
+
+#endif
+
+io_manager sparse_io_manager = &struct_sparse_manager;
+io_manager sparsefd_io_manager = &struct_sparsefd_manager;
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
new file mode 100644
index 0000000..fe764b9
--- /dev/null
+++ b/lib/ext2fs/swapfs.c
@@ -0,0 +1,491 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+#include <ext2fs/ext2_ext_attr.h>
+
+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_uuid is __u8 and does not need swabbing */
+ /* sb->s_volume_name is char and does not need swabbing */
+ /* sb->s_last_mounted is char and does not need swabbing */
+ sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap);
+ /* sb->s_prealloc_blocks is __u8 and does not need swabbing */
+ /* sb->s_prealloc_dir_blocks is __u8 and does not need swabbing */
+ sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks);
+ /* sb->s_journal_uuid is __u8 and does not need swabbing */
+ 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);
+ for (i = 0; i < 4; i++)
+ sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
+ /* sb->s_def_hash_version is __u8 and does not need swabbing */
+ /* sb->s_jnl_backup_type is __u8 and does not need swabbing */
+ 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);
+ /* if journal backup is for a valid extent-based journal... */
+ if (ext2fs_extent_header_verify(sb->s_jnl_blocks,
+ sizeof(sb->s_jnl_blocks)) == 0) {
+ /* ... swap only the journal i_size and i_size_high,
+ * and the extent data is not swapped on read */
+ i = 15;
+ } else {
+ /* direct/indirect journal: swap it all */
+ i = 0;
+ }
+ for (; i < 17; i++)
+ sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]);
+ 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_raid_stride = ext2fs_swab16(sb->s_raid_stride);
+ sb->s_mmp_update_interval = ext2fs_swab16(sb->s_mmp_update_interval);
+ sb->s_mmp_block = ext2fs_swab64(sb->s_mmp_block);
+ sb->s_raid_stripe_width = ext2fs_swab32(sb->s_raid_stripe_width);
+ /* sb->s_log_groups_per_flex is __u8 and does not need swabbing */
+ /* sb->s_checksum_type is __u8 and does not need swabbing */
+ /* sb->s_encryption_level is __u8 and does not need swabbing */
+ /* sb->s_reserved_pad is __u8 and does not need swabbing */
+ 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_error_count = ext2fs_swab32(sb->s_error_count);
+ sb->s_first_error_time = ext2fs_swab32(sb->s_first_error_time);
+ sb->s_first_error_ino = ext2fs_swab32(sb->s_first_error_ino);
+ sb->s_first_error_block = ext2fs_swab64(sb->s_first_error_block);
+ /* sb->s_first_error_func is __u8 and does not need swabbing */
+ sb->s_last_error_time = ext2fs_swab32(sb->s_last_error_time);
+ sb->s_last_error_ino = ext2fs_swab32(sb->s_last_error_ino);
+ sb->s_last_error_block = ext2fs_swab64(sb->s_last_error_block);
+ /* sb->s_last_error_func is __u8 and does not need swabbing */
+ /* sb->s_mount_opts is __u8 and does not need swabbing */
+ sb->s_usr_quota_inum = ext2fs_swab32(sb->s_usr_quota_inum);
+ sb->s_grp_quota_inum = ext2fs_swab32(sb->s_grp_quota_inum);
+ sb->s_overhead_clusters = ext2fs_swab32(sb->s_overhead_clusters);
+ sb->s_backup_bgs[0] = ext2fs_swab32(sb->s_backup_bgs[0]);
+ sb->s_backup_bgs[1] = ext2fs_swab32(sb->s_backup_bgs[1]);
+ /* sb->s_encrypt_algos is __u8 and does not need swabbing */
+ /* sb->s_encrypt_pw_salt is __u8 and does not need swabbing */
+ sb->s_lpf_ino = ext2fs_swab32(sb->s_lpf_ino);
+ sb->s_prj_quota_inum = ext2fs_swab32(sb->s_prj_quota_inum);
+ sb->s_checksum_seed = ext2fs_swab32(sb->s_checksum_seed);
+ /* s_*_time_hi are __u8 and does not need swabbing */
+ sb->s_encoding = ext2fs_swab16(sb->s_encoding);
+ sb->s_encoding_flags = ext2fs_swab16(sb->s_encoding_flags);
+ sb->s_orphan_file_inum = ext2fs_swab32(sb->s_orphan_file_inum);
+ /* catch when new fields are used from s_reserved */
+ EXT2FS_BUILD_BUG_ON(sizeof(sb->s_reserved) != 94 * sizeof(__le32));
+ sb->s_checksum = ext2fs_swab32(sb->s_checksum);
+}
+
+void ext2fs_swap_group_desc2(ext2_filsys fs, struct ext2_group_desc *gdp)
+{
+ struct ext4_group_desc *gdp4 = (struct ext4_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_exclude_bitmap_lo = ext2fs_swab32(gdp->bg_exclude_bitmap_lo);
+ gdp->bg_block_bitmap_csum_lo =
+ ext2fs_swab16(gdp->bg_block_bitmap_csum_lo);
+ gdp->bg_inode_bitmap_csum_lo =
+ ext2fs_swab16(gdp->bg_inode_bitmap_csum_lo);
+ 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 == NULL || EXT2_DESC_SIZE(fs->super) < EXT2_MIN_DESC_SIZE_64BIT)
+ return;
+
+ /* Swap the 64-bit parts */
+ 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);
+ gdp4->bg_exclude_bitmap_hi = ext2fs_swab16(gdp4->bg_exclude_bitmap_hi);
+ gdp4->bg_block_bitmap_csum_hi =
+ ext2fs_swab16(gdp4->bg_block_bitmap_csum_hi);
+ gdp4->bg_inode_bitmap_csum_hi =
+ ext2fs_swab16(gdp4->bg_inode_bitmap_csum_hi);
+ EXT2FS_BUILD_BUG_ON(sizeof(gdp4->bg_reserved) != sizeof(__u32));
+}
+
+void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
+{
+ 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);
+ to_header->h_checksum = ext2fs_swab32(from_header->h_checksum);
+ for (n = 0; n < 3; 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_inum = ext2fs_swab32(from_entry->e_value_inum);
+ 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 &&
+ (char *)EXT2_EXT_ATTR_NEXT(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, extra_isize, attr_magic;
+ int has_extents = 0, has_inline_data = 0, islnk = 0, fast_symlink = 0;
+ unsigned int inode_size;
+ __u32 *eaf, *eat;
+
+ /*
+ * Note that t and f may point to the same address. That's why
+ * if (hostorder) condition is executed before swab calls and
+ * if (!hostorder) afterwards.
+ */
+ if (hostorder) {
+ islnk = LINUX_S_ISLNK(f->i_mode);
+ fast_symlink = ext2fs_is_fast_symlink(EXT2_INODE(f));
+ has_extents = (f->i_flags & EXT4_EXTENTS_FL) != 0;
+ has_inline_data = (f->i_flags & EXT4_INLINE_DATA_FL) != 0;
+ }
+
+ t->i_mode = ext2fs_swab16(f->i_mode);
+ 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);
+ t->i_blocks = ext2fs_swab32(f->i_blocks);
+ t->i_flags = ext2fs_swab32(f->i_flags);
+ t->i_size_high = ext2fs_swab32(f->i_size_high);
+
+ if (!hostorder) {
+ islnk = LINUX_S_ISLNK(t->i_mode);
+ fast_symlink = ext2fs_is_fast_symlink(EXT2_INODE(t));
+ has_extents = (t->i_flags & EXT4_EXTENTS_FL) != 0;
+ has_inline_data = (t->i_flags & EXT4_INLINE_DATA_FL) != 0;
+ }
+
+ /*
+ * Extent data and inline data are swapped on access, not here
+ */
+ if (!has_extents && !has_inline_data && (!islnk || !fast_symlink)) {
+ 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_checksum_lo =
+ ext2fs_swab16(f->osd2.linux2.l_i_checksum_lo);
+ 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;
+ }
+ if (extra_isize & 3)
+ return; /* Illegal inode extra_isize */
+
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE + extra_isize;
+ if (inode_includes(inode_size, i_checksum_hi))
+ t->i_checksum_hi = ext2fs_swab16(f->i_checksum_hi);
+ if (inode_includes(inode_size, i_ctime_extra))
+ t->i_ctime_extra = ext2fs_swab32(f->i_ctime_extra);
+ if (inode_includes(inode_size, i_mtime_extra))
+ t->i_mtime_extra = ext2fs_swab32(f->i_mtime_extra);
+ if (inode_includes(inode_size, i_atime_extra))
+ t->i_atime_extra = ext2fs_swab32(f->i_atime_extra);
+ if (inode_includes(inode_size, i_crtime))
+ t->i_crtime = ext2fs_swab32(f->i_crtime);
+ if (inode_includes(inode_size, i_crtime_extra))
+ t->i_crtime_extra = ext2fs_swab32(f->i_crtime_extra);
+ if (inode_includes(inode_size, i_version_hi))
+ t->i_version_hi = ext2fs_swab32(f->i_version_hi);
+ if (inode_includes(inode_size, i_projid))
+ t->i_projid = ext2fs_swab32(f->i_projid);
+ /* catch new static fields added after i_projid */
+ EXT2FS_BUILD_BUG_ON(sizeof(struct ext2_inode_large) != 160);
+
+ 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));
+}
+
+void ext2fs_swap_mmp(struct mmp_struct *mmp)
+{
+ mmp->mmp_magic = ext2fs_swab32(mmp->mmp_magic);
+ mmp->mmp_seq = ext2fs_swab32(mmp->mmp_seq);
+ mmp->mmp_time = ext2fs_swab64(mmp->mmp_time);
+ mmp->mmp_check_interval = ext2fs_swab16(mmp->mmp_check_interval);
+ mmp->mmp_checksum = ext2fs_swab32(mmp->mmp_checksum);
+}
+
+errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
+{
+ return ext2fs_dirent_swab_in2(fs, buf, fs->blocksize, flags);
+}
+
+errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf,
+ size_t size, int flags)
+{
+ errcode_t retval;
+ char *p, *end;
+ struct ext2_dir_entry *dirent;
+ unsigned int name_len, rec_len, left;
+
+ p = (char *) buf;
+ end = (char *) buf + size;
+ left = size;
+ while (p < end-8) {
+ dirent = (struct ext2_dir_entry *) p;
+ dirent->inode = ext2fs_swab32(dirent->inode);
+ dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ name_len = dirent->name_len;
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ return retval;
+ if ((rec_len < 8) || (rec_len % 4)) {
+ rec_len = 8;
+ if (!(fs->flags & EXT2_FLAG_IGNORE_SWAP_DIRENT))
+ return EXT2_ET_DIR_CORRUPTED;
+ } else if (((name_len & 0xFF) + 8) > rec_len)
+ if (!(fs->flags & EXT2_FLAG_IGNORE_SWAP_DIRENT))
+ return EXT2_ET_DIR_CORRUPTED;
+ if (rec_len > left)
+ if (!(fs->flags & EXT2_FLAG_IGNORE_SWAP_DIRENT))
+ return EXT2_ET_DIR_CORRUPTED;
+ left -= rec_len;
+ p += rec_len;
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
+{
+ return ext2fs_dirent_swab_out2(fs, buf, fs->blocksize, flags);
+}
+
+errcode_t ext2fs_dirent_swab_out2(ext2_filsys fs, char *buf,
+ size_t size, int flags)
+{
+ errcode_t retval;
+ char *p, *end;
+ unsigned int rec_len;
+ struct ext2_dir_entry *dirent;
+
+ p = buf;
+ end = buf + size;
+ while (p < end) {
+ dirent = (struct ext2_dir_entry *) p;
+ retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
+ if (retval)
+ 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 (rec_len > size)
+ return EXT2_ET_DIR_CORRUPTED;
+ size -= rec_len;
+
+ if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+ dirent->name_len = ext2fs_swab16(dirent->name_len);
+ }
+
+ return 0;
+}
diff --git a/lib/ext2fs/symlink.c b/lib/ext2fs/symlink.c
new file mode 100644
index 0000000..a66fb7e
--- /dev/null
+++ b/lib/ext2fs/symlink.c
@@ -0,0 +1,212 @@
+/*
+ * symlink.c --- make a symlink in the filesystem, based on mkdir.c
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef HAVE_STRNLEN
+/*
+ * Incredibly, libc5 doesn't appear to have strnlen. So we have to
+ * provide our own.
+ */
+static int my_strnlen(const char * s, int count)
+{
+ const char *cp = s;
+
+ while (count-- && *cp)
+ cp++;
+ return cp - s;
+}
+#define strnlen(str, x) my_strnlen((str),(x))
+#endif
+
+errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
+ const char *name, const char *target)
+{
+ errcode_t retval;
+ struct ext2_inode inode;
+ ext2_ino_t scratch_ino;
+ blk64_t blk;
+ int fastlink, inlinelink;
+ unsigned int target_len;
+ char *block_buf = 0;
+ int drop_refcount = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ /*
+ * The Linux kernel doesn't allow for links longer than a block
+ * (counting the NUL terminator)
+ */
+ target_len = strnlen(target, fs->blocksize + 1);
+ if (target_len >= fs->blocksize) {
+ retval = EXT2_ET_INVALID_ARGUMENT;
+ goto cleanup;
+ }
+
+ /*
+ * Allocate a data block for slow links
+ */
+ retval = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (retval)
+ goto cleanup;
+ memset(block_buf, 0, fs->blocksize);
+ strncpy(block_buf, target, fs->blocksize);
+
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ fastlink = (target_len < sizeof(inode.i_block));
+ if (!fastlink) {
+ retval = ext2fs_new_block2(fs, ext2fs_find_inode_goal(fs, ino,
+ &inode,
+ 0),
+ NULL, &blk);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Allocate an inode, if necessary
+ */
+ if (!ino) {
+ retval = ext2fs_new_inode(fs, parent, LINUX_S_IFLNK | 0755,
+ 0, &ino);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Create the inode structure....
+ */
+ inode.i_mode = LINUX_S_IFLNK | 0777;
+ inode.i_uid = inode.i_gid = 0;
+ inode.i_links_count = 1;
+ ext2fs_inode_size_set(fs, &inode, target_len);
+ /* The time fields are set by ext2fs_write_new_inode() */
+
+ inlinelink = !fastlink && ext2fs_has_feature_inline_data(fs->super);
+ if (fastlink) {
+ /* Fast symlinks, target stored in inode */
+ strcpy((char *)&inode.i_block, target);
+ } else if (inlinelink) {
+ /* Try inserting an inline data symlink */
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_inline_data_set(fs, ino, &inode, block_buf,
+ target_len);
+ if (retval) {
+ inode.i_flags &= ~EXT4_INLINE_DATA_FL;
+ inlinelink = 0;
+ goto need_block;
+ }
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+ } else {
+need_block:
+ /* Slow symlinks, target stored in the first block */
+ ext2fs_iblk_set(fs, &inode, 1);
+ if (ext2fs_has_feature_extents(fs->super)) {
+ /*
+ * The extent bmap is setup after the inode and block
+ * have been written out below.
+ */
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ }
+ }
+
+ /*
+ * Write out the inode and inode data block. The inode generation
+ * number is assigned by write_new_inode, which means that the
+ * operations using ino must come after it.
+ */
+ if (inlinelink)
+ retval = ext2fs_write_inode(fs, ino, &inode);
+ else
+ retval = ext2fs_write_new_inode(fs, ino, &inode);
+ if (retval)
+ goto cleanup;
+
+ if (!fastlink && !inlinelink) {
+ retval = ext2fs_bmap2(fs, ino, &inode, NULL, BMAP_SET, 0, NULL,
+ &blk);
+ if (retval)
+ goto cleanup;
+
+ retval = io_channel_write_blk64(fs->io, blk, 1, block_buf);
+ if (retval)
+ goto cleanup;
+ }
+
+ /*
+ * Update accounting....
+ */
+ if (!fastlink && !inlinelink)
+ ext2fs_block_alloc_stats2(fs, blk, +1);
+ ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
+ drop_refcount = 1;
+
+ /*
+ * Link the symlink into the filesystem hierarchy
+ */
+ if (name) {
+ retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
+ &scratch_ino);
+ if (!retval) {
+ retval = EXT2_ET_FILE_EXISTS;
+ goto cleanup;
+ }
+ if (retval != EXT2_ET_FILE_NOT_FOUND)
+ goto cleanup;
+ retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_SYMLINK);
+ if (retval)
+ goto cleanup;
+ }
+ drop_refcount = 0;
+
+cleanup:
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+ if (drop_refcount) {
+ if (!fastlink && !inlinelink)
+ ext2fs_block_alloc_stats2(fs, blk, -1);
+ ext2fs_inode_alloc_stats2(fs, ino, -1, 0);
+ }
+ return retval;
+}
+
+/*
+ * Test whether an inode is a fast symlink.
+ *
+ * A fast symlink has its symlink data stored in inode->i_block.
+ */
+int ext2fs_is_fast_symlink(struct ext2_inode *inode)
+{
+ return LINUX_S_ISLNK(inode->i_mode) && EXT2_I_SIZE(inode) &&
+ EXT2_I_SIZE(inode) < sizeof(inode->i_block);
+}
diff --git a/lib/ext2fs/tdb.c b/lib/ext2fs/tdb.c
new file mode 100644
index 0000000..b07b291
--- /dev/null
+++ b/lib/ext2fs/tdb.c
@@ -0,0 +1,4174 @@
+/*
+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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef CONFIG_STAND_ALONE
+#define HAVE_MMAP
+#define HAVE_STRDUP
+#define HAVE_SYS_MMAN_H
+#define HAVE_UTIME_H
+#define HAVE_UTIME
+#endif
+#ifndef __FreeBSD__
+#define _XOPEN_SOURCE 600
+#endif
+
+#include "config.h"
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#ifdef __GNUC__
+#define EXT2FS_ATTR(x) __attribute__(x)
+#else
+#define EXT2FS_ATTR(x)
+#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;
+ 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"
+
+static TDB_DATA tdb_null;
+
+#ifndef u32
+#define u32 unsigned
+#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;
+ tdb_len_t real_map_size; /* how much space has been mapped */
+};
+
+
+/*
+ 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);
+int tdb_lock_nonblock(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;
+ select(0, NULL, NULL, NULL, &tv);
+ }
+ 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 &&
+ ((u32)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; i<tdb->num_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 &&
+ ((u32)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; i<tdb->num_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 == (u32)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 != (u32)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 variant */
+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 variant */
+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 < (off_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->real_map_size);
+ if (ret != 0)
+ return ret;
+ tdb->real_map_size = 0;
+ }
+#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->real_map_size = 0;
+ tdb->map_ptr = NULL;
+ TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n",
+ tdb->map_size, strerror(errno)));
+ }
+ tdb->real_map_size = tdb->map_size;
+ } 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 strategy 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 EXT2FS_ATTR((unused)))
+{
+ 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 EXT2FS_ATTR((unused)),
+ tdb_off_t offset EXT2FS_ATTR((unused)),
+ int rw_type EXT2FS_ATTR((unused)),
+ int lck_type EXT2FS_ATTR((unused)),
+ int probe EXT2FS_ATTR((unused)),
+ size_t len EXT2FS_ATTR((unused)))
+{
+ 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;i<tdb->num_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;
+ }
+#if defined(HAVE_MSYNC) && defined(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;
+ free(data);
+ 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
+ necessary. */
+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 that's 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" <Shlomi@exanet.com>. */
+ 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 initialization, 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; i < (int)tdb->header.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 */
+
+/*
+ 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.
+ */
+ if (tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum) == -1)
+ return;
+ seqnum++;
+ (void) 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 EXT2FS_ATTR((unused)))
+{
+ 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;
+
+ if (tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum) == -1)
+ return 0;
+ 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 from a hash algorithm suggested by Rogier Wolff */
+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 = 0, i=0; i < key->dsize; i++)
+ value = value * 256 + key->dptr[i] + (value >> 24) * 241;
+
+ return value;
+}
+
+
+/* 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 EXT2FS_ATTR((unused)),
+ enum tdb_debug_level level EXT2FS_ATTR((unused)),
+ const char *fmt EXT2FS_ATTR((unused)), ...)
+{
+}
+
+
+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 = sysconf(_SC_PAGESIZE);
+ 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)
+ || memcmp(tdb->header.magic_food, TDB_MAGIC_FOOD,
+ sizeof(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 logging 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;
+}
+
+/**
+ * Flush a database file from the page cache.
+ **/
+int tdb_flush(struct tdb_context *tdb)
+{
+ if (tdb->fd != -1)
+ return fsync(tdb->fd);
+ return 0;
+}
diff --git a/lib/ext2fs/tdb.h b/lib/ext2fs/tdb.h
new file mode 100644
index 0000000..de7aa33
--- /dev/null
+++ b/lib/ext2fs/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
+#define tdb_flush ext2fs_tdb_flush
+
+/* 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);
+int tdb_flush(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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* tdb.h */
diff --git a/lib/ext2fs/tdb/build-tdb b/lib/ext2fs/tdb/build-tdb
new file mode 100755
index 0000000..1cc18f7
--- /dev/null
+++ b/lib/ext2fs/tdb/build-tdb
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# This file creates a stand-alone TDB based on a set of sources from
+# Samba
+
+#BASE_DIR=/usr/projects/samba/samba-4.0.0tp4/source/lib/tdb
+BASE_DIR=/usr/projects/samba/tdb
+
+rm -rf .pc
+
+FILES="error.c lock.c io.c transaction.c freelist.c \
+ freelistcheck.c traverse.c dump.c tdb.c open.c"
+
+(cd $BASE_DIR/common; svn info ) > .svninfo
+echo "/*" > tdb.c
+grep ^URL .svninfo >> tdb.c
+grep "^Last Changed Rev" .svninfo | sed -e 's/Last Changed //' >> tdb.c
+grep "^Last Changed Date" .svninfo >> tdb.c
+echo "*/" >> tdb.c
+
+cat $BASE_DIR/common/tdb_private.h >> tdb.c
+for i in $FILES; do
+ if [ `tail -n 1 tdb.c | wc -c` -gt 1 ]; then
+ printf "\n" >> tdb.c
+ fi
+ echo "/* file: $i */" >> tdb.c
+ sed -e '1,/#include "tdb_private.h"/d' < $BASE_DIR/common/$i >> tdb.c
+done
+
+cp $BASE_DIR/include/tdb.h .
+cp $BASE_DIR/tools/tdbtool.c .
+
+quilt push -a
+
diff --git a/lib/ext2fs/tdb/patches/copyright b/lib/ext2fs/tdb/patches/copyright
new file mode 100644
index 0000000..d9d1d84
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/copyright
@@ -0,0 +1,20 @@
+Index: tdbsa/tdb.c
+===================================================================
+--- tdbsa.orig/tdb.c
++++ tdbsa/tdb.c
+@@ -4,11 +4,11 @@ Rev: 23371
+ Last Changed Date: 2007-06-06 20:14:06 -0400 (Wed, 06 Jun 2007)
+ */
+ /*
+- Unix SMB/CIFS implementation.
++ trivial database library - standalone version
+
+- trivial database library - private includes
+-
+- Copyright (C) Andrew Tridgell 2005
++ 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
diff --git a/lib/ext2fs/tdb/patches/ext2tdb-rename b/lib/ext2fs/tdb/patches/ext2tdb-rename
new file mode 100644
index 0000000..15bf085
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/ext2tdb-rename
@@ -0,0 +1,65 @@
+Index: tdb/tdb.h
+===================================================================
+--- tdb.orig/tdb.h
++++ tdb/tdb.h
+@@ -76,6 +76,60 @@ typedef struct TDB_DATA {
+ #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;
+
diff --git a/lib/ext2fs/tdb/patches/replace-includes b/lib/ext2fs/tdb/patches/replace-includes
new file mode 100644
index 0000000..f4181c1
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/replace-includes
@@ -0,0 +1,92 @@
+Index: tdb/tdb.c
+===================================================================
+--- tdb.orig/tdb.c
++++ tdb/tdb.c
+@@ -29,11 +29,82 @@ Last Changed Date: 2007-06-22 13:36:10 -
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+-#include "replace.h"
+-#include "system/filesys.h"
+-#include "system/time.h"
+-#include "system/shmem.h"
+-#include "system/select.h"
++#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 500
++
++#include <unistd.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <stddef.h>
++#include <errno.h>
++#include <string.h>
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++#include <sys/time.h>
++#include <sys/types.h>
++#include <time.h>
++#ifdef HAVE_UTIME_H
++#include <utime.h>
++#endif
++#include <sys/stat.h>
++#include <sys/file.h>
++#include <fcntl.h>
++
++#ifdef HAVE_SYS_MMAN_H
++#include <sys/mman.h>
++#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
diff --git a/lib/ext2fs/tdb/patches/series b/lib/ext2fs/tdb/patches/series
new file mode 100644
index 0000000..722b921
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/series
@@ -0,0 +1,6 @@
+copyright
+replace-includes
+static-prototypes
+static-functions
+tdbtool-includes
+ext2tdb-rename
diff --git a/lib/ext2fs/tdb/patches/static-functions b/lib/ext2fs/tdb/patches/static-functions
new file mode 100644
index 0000000..ab0fbef
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/static-functions
@@ -0,0 +1,13 @@
+Index: tdbsa/tdb.c
+===================================================================
+--- tdbsa.orig/tdb.c
++++ tdbsa/tdb.c
+@@ -2254,7 +2254,7 @@ int tdb_transaction_recover(struct tdb_c
+ /* file: freelist.c */
+
+ /* read a freelist record and check for simple errors */
+-int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec)
++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;
diff --git a/lib/ext2fs/tdb/patches/static-prototypes b/lib/ext2fs/tdb/patches/static-prototypes
new file mode 100644
index 0000000..cf8af61
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/static-prototypes
@@ -0,0 +1,72 @@
+Index: tdbsa/tdb.c
+===================================================================
+--- tdbsa.orig/tdb.c
++++ tdbsa/tdb.c
+@@ -251,39 +251,39 @@ struct tdb_context {
+ /*
+ internal prototypes
+ */
+-int tdb_munmap(struct tdb_context *tdb);
+-void tdb_mmap(struct tdb_context *tdb);
+-int tdb_lock(struct tdb_context *tdb, int list, int ltype);
+-int tdb_unlock(struct tdb_context *tdb, int list, int ltype);
+-int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len);
+-int tdb_transaction_lock(struct tdb_context *tdb, int ltype);
+-int tdb_transaction_unlock(struct tdb_context *tdb);
+-int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len);
+-int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off);
+-int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off);
+-int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
+-int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
+-void *tdb_convert(void *buf, u32 size);
+-int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec);
+-tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec);
+-int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
+-int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d);
+-int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off);
+-int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off);
+-int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec);
+-int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec);
+-int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec);
+-unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len);
+-int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
++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);
+-tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype,
++static tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype,
+ struct list_struct *rec);
+-void tdb_io_init(struct tdb_context *tdb);
+-int tdb_expand(struct tdb_context *tdb, tdb_off_t size);
+-int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off,
++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);
+
+
diff --git a/lib/ext2fs/tdb/patches/tdbtool-includes b/lib/ext2fs/tdb/patches/tdbtool-includes
new file mode 100644
index 0000000..c076c79
--- /dev/null
+++ b/lib/ext2fs/tdb/patches/tdbtool-includes
@@ -0,0 +1,30 @@
+Index: tdbsa/tdbtool.c
+===================================================================
+--- tdbsa.orig/tdbtool.c
++++ tdbsa/tdbtool.c
+@@ -21,10 +21,21 @@
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+-#include "replace.h"
+-#include "system/locale.h"
+-#include "system/time.h"
+-#include "system/filesys.h"
++#include <errno.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <string.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <sys/time.h>
++#include <ctype.h>
++#include <signal.h>
++#include <stdarg.h>
++
+ #include "tdb.h"
+
+ static int do_command(void);
diff --git a/lib/ext2fs/tdbtool.c b/lib/ext2fs/tdbtool.c
new file mode 100644
index 0000000..eeac0c8
--- /dev/null
+++ b/lib/ext2fs/tdbtool.c
@@ -0,0 +1,621 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba database functions
+ Copyright (C) Andrew Tridgell 1999-2000
+ Copyright (C) Paul `Rusty' Russell 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Andrew Esh 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdarg.h>
+
+#include "tdb.h"
+
+static int do_command(void);
+const char *cmdname;
+char *arg1, *arg2;
+size_t arg1len, arg2len;
+int bIterate = 0;
+char *line;
+TDB_DATA iterate_kbuf;
+char cmdline[1024];
+
+enum commands {
+ CMD_CREATE_TDB,
+ CMD_OPEN_TDB,
+ CMD_ERASE,
+ CMD_DUMP,
+ CMD_INSERT,
+ CMD_MOVE,
+ CMD_STORE,
+ CMD_SHOW,
+ CMD_KEYS,
+ CMD_HEXKEYS,
+ CMD_DELETE,
+ CMD_LIST_HASH_FREE,
+ CMD_LIST_FREE,
+ CMD_INFO,
+ CMD_FIRST,
+ CMD_NEXT,
+ CMD_SYSTEM,
+ CMD_QUIT,
+ CMD_HELP
+};
+
+typedef struct {
+ const char *name;
+ enum commands cmd;
+} COMMAND_TABLE;
+
+COMMAND_TABLE cmd_table[] = {
+ {"create", CMD_CREATE_TDB},
+ {"open", CMD_OPEN_TDB},
+ {"erase", CMD_ERASE},
+ {"dump", CMD_DUMP},
+ {"insert", CMD_INSERT},
+ {"move", CMD_MOVE},
+ {"store", CMD_STORE},
+ {"show", CMD_SHOW},
+ {"keys", CMD_KEYS},
+ {"hexkeys", CMD_HEXKEYS},
+ {"delete", CMD_DELETE},
+ {"list", CMD_LIST_HASH_FREE},
+ {"free", CMD_LIST_FREE},
+ {"info", CMD_INFO},
+ {"first", CMD_FIRST},
+ {"1", CMD_FIRST},
+ {"next", CMD_NEXT},
+ {"n", CMD_NEXT},
+ {"quit", CMD_QUIT},
+ {"q", CMD_QUIT},
+ {"!", CMD_SYSTEM},
+ {NULL, CMD_HELP}
+};
+
+/* a tdb tool for manipulating a tdb database */
+
+static TDB_CONTEXT *tdb;
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state);
+
+static void print_asc(const char *buf,int len)
+{
+ int i;
+
+ /* We're probably printing ASCII strings so don't try to display
+ the trailing NULL character. */
+
+ if (buf[len - 1] == 0)
+ len--;
+
+ for (i=0;i<len;i++)
+ printf("%c",isprint(buf[i])?buf[i]:'.');
+}
+
+static void print_data(const char *buf,int len)
+{
+ int i=0;
+ if (len<=0) return;
+ printf("[%03X] ",i);
+ for (i=0;i<len;) {
+ printf("%02X ",(int)buf[i]);
+ i++;
+ if (i%8 == 0) printf(" ");
+ if (i%16 == 0) {
+ print_asc(&buf[i-16],8); printf(" ");
+ print_asc(&buf[i-8],8); printf("\n");
+ if (i<len) printf("[%03X] ",i);
+ }
+ }
+ if (i%16) {
+ int n;
+
+ n = 16 - (i%16);
+ printf(" ");
+ if (n>8) printf(" ");
+ while (n--) printf(" ");
+
+ n = i%16;
+ if (n > 8) n = 8;
+ print_asc(&buf[i-(i%16)],n); printf(" ");
+ n = (i%16) - n;
+ if (n>0) print_asc(&buf[i-n],n);
+ printf("\n");
+ }
+}
+
+static void help(void)
+{
+ printf("\n"
+"tdbtool: \n"
+" create dbname : create a database\n"
+" open dbname : open an existing database\n"
+" erase : erase the database\n"
+" dump : dump the database as strings\n"
+" keys : dump the database keys as strings\n"
+" hexkeys : dump the database keys as hex values\n"
+" info : print summary info about the database\n"
+" insert key data : insert a record\n"
+" move key file : move a record to a destination tdb\n"
+" store key data : store a record (replace)\n"
+" show key : show a record by key\n"
+" delete key : delete a record by key\n"
+" list : print the database hash table and freelist\n"
+" free : print the database freelist\n"
+" ! command : execute system command\n"
+" 1 | first : print the first record\n"
+" n | next : print the next record\n"
+" q | quit : terminate\n"
+" \\n : repeat 'next' command\n"
+"\n");
+}
+
+static void terror(const char *why)
+{
+ printf("%s\n", why);
+}
+
+static void create_tdb(const char *tdbname)
+{
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (!tdb) {
+ printf("Could not create %s: %s\n", tdbname, strerror(errno));
+ }
+}
+
+static void open_tdb(const char *tdbname)
+{
+ if (tdb) tdb_close(tdb);
+ tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
+ if (!tdb) {
+ printf("Could not open %s: %s\n", tdbname, strerror(errno));
+ }
+}
+
+static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
+{
+ TDB_DATA key, dbuf;
+
+ if ((keyname == NULL) || (keylen == 0)) {
+ terror("need key");
+ return;
+ }
+
+ key.dptr = (unsigned char *)keyname;
+ key.dsize = keylen;
+ dbuf.dptr = (unsigned char *)data;
+ dbuf.dsize = datalen;
+
+ if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) {
+ terror("insert failed");
+ }
+}
+
+static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen)
+{
+ TDB_DATA key, dbuf;
+
+ if ((keyname == NULL) || (keylen == 0)) {
+ terror("need key");
+ return;
+ }
+
+ if ((data == NULL) || (datalen == 0)) {
+ terror("need data");
+ return;
+ }
+
+ key.dptr = (unsigned char *)keyname;
+ key.dsize = keylen;
+ dbuf.dptr = (unsigned char *)data;
+ dbuf.dsize = datalen;
+
+ printf("Storing key:\n");
+ print_rec(tdb, key, dbuf, NULL);
+
+ if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) {
+ terror("store failed");
+ }
+}
+
+static void show_tdb(char *keyname, size_t keylen)
+{
+ TDB_DATA key, dbuf;
+
+ if ((keyname == NULL) || (keylen == 0)) {
+ terror("need key");
+ return;
+ }
+
+ key.dptr = (unsigned char *)keyname;
+ key.dsize = keylen;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ terror("fetch failed");
+ return;
+ }
+
+ print_rec(tdb, key, dbuf, NULL);
+
+ free( dbuf.dptr );
+
+ return;
+}
+
+static void delete_tdb(char *keyname, size_t keylen)
+{
+ TDB_DATA key;
+
+ if ((keyname == NULL) || (keylen == 0)) {
+ terror("need key");
+ return;
+ }
+
+ key.dptr = (unsigned char *)keyname;
+ key.dsize = keylen;
+
+ if (tdb_delete(tdb, key) != 0) {
+ terror("delete failed");
+ }
+}
+
+static void move_rec(char *keyname, size_t keylen, char* tdbname)
+{
+ TDB_DATA key, dbuf;
+ TDB_CONTEXT *dst_tdb;
+
+ if ((keyname == NULL) || (keylen == 0)) {
+ terror("need key");
+ return;
+ }
+
+ if ( !tdbname ) {
+ terror("need destination tdb name");
+ return;
+ }
+
+ key.dptr = (unsigned char *)keyname;
+ key.dsize = keylen;
+
+ dbuf = tdb_fetch(tdb, key);
+ if (!dbuf.dptr) {
+ terror("fetch failed");
+ return;
+ }
+
+ print_rec(tdb, key, dbuf, NULL);
+
+ dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600);
+ if ( !dst_tdb ) {
+ terror("unable to open destination tdb");
+ return;
+ }
+
+ if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) {
+ terror("failed to move record");
+ }
+ else
+ printf("record moved\n");
+
+ tdb_close( dst_tdb );
+
+ return;
+}
+
+static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ printf("\nkey %d bytes\n", (int)key.dsize);
+ print_asc((const char *)key.dptr, key.dsize);
+ printf("\ndata %d bytes\n", (int)dbuf.dsize);
+ print_data((const char *)dbuf.dptr, dbuf.dsize);
+ return 0;
+}
+
+static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ printf("key %d bytes: ", (int)key.dsize);
+ print_asc((const char *)key.dptr, key.dsize);
+ printf("\n");
+ return 0;
+}
+
+static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ printf("key %d bytes\n", (int)key.dsize);
+ print_data((const char *)key.dptr, key.dsize);
+ printf("\n");
+ return 0;
+}
+
+static int total_bytes;
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
+{
+ total_bytes += dbuf.dsize;
+ return 0;
+}
+
+static void info_tdb(void)
+{
+ int count;
+ total_bytes = 0;
+ if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1)
+ printf("Error = %s\n", tdb_errorstr(tdb));
+ else
+ printf("%d records totalling %d bytes\n", count, total_bytes);
+}
+
+static char *tdb_getline(const char *prompt)
+{
+ static char thisline[1024];
+ char *p;
+ fputs(prompt, stdout);
+ thisline[0] = 0;
+ p = fgets(thisline, sizeof(thisline)-1, stdin);
+ if (p) p = strchr(p, '\n');
+ if (p) *p = 0;
+ return p?thisline:NULL;
+}
+
+static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf,
+ void *state)
+{
+ return tdb_delete(the_tdb, key);
+}
+
+static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_firstkey(the_tdb);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr) terror("fetch failed");
+ else {
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+ }
+}
+
+static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey)
+{
+ TDB_DATA dbuf;
+ *pkey = tdb_nextkey(the_tdb, *pkey);
+
+ dbuf = tdb_fetch(the_tdb, *pkey);
+ if (!dbuf.dptr)
+ terror("fetch failed");
+ else
+ print_rec(the_tdb, *pkey, dbuf, NULL);
+}
+
+static int do_command(void)
+{
+ COMMAND_TABLE *ctp = cmd_table;
+ enum commands mycmd = CMD_HELP;
+ int cmd_len;
+
+ if (cmdname && strlen(cmdname) == 0) {
+ mycmd = CMD_NEXT;
+ } else {
+ while (ctp->name) {
+ cmd_len = strlen(ctp->name);
+ if (strncmp(ctp->name,cmdname,cmd_len) == 0) {
+ mycmd = ctp->cmd;
+ break;
+ }
+ ctp++;
+ }
+ }
+
+ switch (mycmd) {
+ case CMD_CREATE_TDB:
+ bIterate = 0;
+ create_tdb(arg1);
+ return 0;
+ case CMD_OPEN_TDB:
+ bIterate = 0;
+ open_tdb(arg1);
+ return 0;
+ case CMD_SYSTEM:
+ /* Shell command */
+ system(arg1);
+ return 0;
+ case CMD_QUIT:
+ return 1;
+ default:
+ /* all the rest require a open database */
+ if (!tdb) {
+ bIterate = 0;
+ terror("database not open");
+ help();
+ return 0;
+ }
+ switch (mycmd) {
+ case CMD_ERASE:
+ bIterate = 0;
+ tdb_traverse(tdb, do_delete_fn, NULL);
+ return 0;
+ case CMD_DUMP:
+ bIterate = 0;
+ tdb_traverse(tdb, print_rec, NULL);
+ return 0;
+ case CMD_INSERT:
+ bIterate = 0;
+ insert_tdb(arg1, arg1len,arg2,arg2len);
+ return 0;
+ case CMD_MOVE:
+ bIterate = 0;
+ move_rec(arg1,arg1len,arg2);
+ return 0;
+ case CMD_STORE:
+ bIterate = 0;
+ store_tdb(arg1,arg1len,arg2,arg2len);
+ return 0;
+ case CMD_SHOW:
+ bIterate = 0;
+ show_tdb(arg1, arg1len);
+ return 0;
+ case CMD_KEYS:
+ tdb_traverse(tdb, print_key, NULL);
+ return 0;
+ case CMD_HEXKEYS:
+ tdb_traverse(tdb, print_hexkey, NULL);
+ return 0;
+ case CMD_DELETE:
+ bIterate = 0;
+ delete_tdb(arg1,arg1len);
+ return 0;
+ case CMD_LIST_HASH_FREE:
+ tdb_dump_all(tdb);
+ return 0;
+ case CMD_LIST_FREE:
+ tdb_printfreelist(tdb);
+ return 0;
+ case CMD_INFO:
+ info_tdb();
+ return 0;
+ case CMD_FIRST:
+ bIterate = 1;
+ first_record(tdb, &iterate_kbuf);
+ return 0;
+ case CMD_NEXT:
+ if (bIterate)
+ next_record(tdb, &iterate_kbuf);
+ return 0;
+ case CMD_HELP:
+ help();
+ return 0;
+ case CMD_CREATE_TDB:
+ case CMD_OPEN_TDB:
+ case CMD_SYSTEM:
+ case CMD_QUIT:
+ /*
+ * unhandled commands. cases included here to avoid compiler
+ * warnings.
+ */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static char *convert_string(char *instring, size_t *sizep)
+{
+ size_t length = 0;
+ char *outp, *inp;
+ char temp[3];
+
+
+ outp = inp = instring;
+
+ while (*inp) {
+ if (*inp == '\\') {
+ inp++;
+ if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
+ temp[0] = *inp++;
+ temp[1] = '\0';
+ if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) {
+ temp[1] = *inp++;
+ temp[2] = '\0';
+ }
+ *outp++ = (char)strtol((const char *)temp,NULL,16);
+ } else {
+ *outp++ = *inp++;
+ }
+ } else {
+ *outp++ = *inp++;
+ }
+ length++;
+ }
+ *sizep = length;
+ return instring;
+}
+
+int main(int argc, char *argv[])
+{
+ cmdname = "";
+ arg1 = NULL;
+ arg1len = 0;
+ arg2 = NULL;
+ arg2len = 0;
+
+ if (argv[1]) {
+ cmdname = "open";
+ arg1 = argv[1];
+ do_command();
+ cmdname = "";
+ arg1 = NULL;
+ }
+
+ switch (argc) {
+ case 1:
+ case 2:
+ /* Interactive mode */
+ while ((cmdname = tdb_getline("tdb> "))) {
+ arg2 = arg1 = NULL;
+ if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) {
+ arg1++;
+ arg2 = arg1;
+ while (*arg2) {
+ if (*arg2 == ' ') {
+ *arg2++ = '\0';
+ break;
+ }
+ if ((*arg2++ == '\\') && (*arg2 == ' ')) {
+ arg2++;
+ }
+ }
+ }
+ if (arg1) arg1 = convert_string(arg1,&arg1len);
+ if (arg2) arg2 = convert_string(arg2,&arg2len);
+ if (do_command()) break;
+ }
+ break;
+ case 5:
+ arg2 = convert_string(argv[4],&arg2len);
+ case 4:
+ arg1 = convert_string(argv[3],&arg1len);
+ case 3:
+ cmdname = argv[2];
+ default:
+ do_command();
+ break;
+ }
+
+ if (tdb) tdb_close(tdb);
+
+ return 0;
+}
diff --git a/lib/ext2fs/test_io.c b/lib/ext2fs/test_io.c
new file mode 100644
index 0000000..6843edb
--- /dev/null
+++ b/lib/ext2fs/test_io.c
@@ -0,0 +1,557 @@
+/*
+ * test_io.c --- This is the Test I/O interface.
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+
+#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 test_private_data {
+ int magic;
+ io_channel real;
+ int flags;
+ FILE *outfile;
+ unsigned long block;
+ int read_abort_count, write_abort_count;
+ void (*read_blk)(unsigned long block, int count, errcode_t err);
+ void (*write_blk)(unsigned long block, int count, errcode_t err);
+ void (*set_blksize)(int blksize, errcode_t err);
+ void (*write_byte)(unsigned long block, int count, errcode_t err);
+ void (*read_blk64)(unsigned long long block, int count, errcode_t err);
+ void (*write_blk64)(unsigned long long block, int count, errcode_t err);
+};
+
+/*
+ * These global variable can be set by the test program as
+ * necessary *before* calling test_open
+ */
+io_manager test_io_backing_manager = 0;
+void (*test_io_cb_read_blk)
+ (unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_write_blk)
+ (unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_read_blk64)
+ (unsigned long long block, int count, errcode_t err) = 0;
+void (*test_io_cb_write_blk64)
+ (unsigned long long block, int count, errcode_t err) = 0;
+void (*test_io_cb_set_blksize)
+ (int blksize, errcode_t err) = 0;
+void (*test_io_cb_write_byte)
+ (unsigned long block, int count, errcode_t err) = 0;
+
+/*
+ * Test flags
+ */
+#define TEST_FLAG_READ 0x01
+#define TEST_FLAG_WRITE 0x02
+#define TEST_FLAG_SET_BLKSIZE 0x04
+#define TEST_FLAG_FLUSH 0x08
+#define TEST_FLAG_DUMP 0x10
+#define TEST_FLAG_SET_OPTION 0x20
+#define TEST_FLAG_DISCARD 0x40
+#define TEST_FLAG_READAHEAD 0x80
+#define TEST_FLAG_ZEROOUT 0x100
+
+static void test_dump_block(io_channel channel,
+ struct test_private_data *data,
+ unsigned long block, const void *buf)
+{
+ const unsigned char *cp;
+ FILE *f = data->outfile;
+ int i;
+ unsigned long cksum = 0;
+
+ for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+ cksum += *cp;
+ }
+ fprintf(f, "Contents of block %lu, checksum %08lu: \n", block, cksum);
+ for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+ if ((i % 16) == 0)
+ fprintf(f, "%04x: ", i);
+ fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
+ }
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t test_flush(io_channel channel)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *)channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_flush(data->real);
+
+ if (data->flags & TEST_FLAG_FLUSH)
+ fprintf(data->outfile, "Test_io: flush() returned %s\n",
+ retval ? error_message(retval) : "OK");
+
+ return retval;
+}
+
+static void test_abort(io_channel channel, unsigned long block)
+{
+ struct test_private_data *data;
+ FILE *f;
+
+ data = (struct test_private_data *) channel->private_data;
+ f = data->outfile;
+ test_flush(channel);
+
+ fprintf(f, "Aborting due to I/O to block %lu\n", block);
+ fflush(f);
+ abort();
+}
+
+static char *safe_getenv(const char *arg)
+{
+#if !defined(_WIN32)
+ if ((getuid() != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#endif
+#if HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel)
+{
+ io_channel io = NULL;
+ struct test_private_data *data = NULL;
+ errcode_t retval;
+ char *value;
+
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+ 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;
+ retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
+ if (retval)
+ goto cleanup;
+ io->manager = test_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;
+ io->flags = 0;
+
+ memset(data, 0, sizeof(struct test_private_data));
+ data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
+ if (test_io_backing_manager) {
+ retval = test_io_backing_manager->open(name, flags,
+ &data->real);
+ if (retval)
+ goto cleanup;
+ } else {
+ data->real = 0;
+ }
+ data->read_blk = test_io_cb_read_blk;
+ data->write_blk = test_io_cb_write_blk;
+ data->set_blksize = test_io_cb_set_blksize;
+ data->write_byte = test_io_cb_write_byte;
+ data->read_blk64 = test_io_cb_read_blk64;
+ data->write_blk64 = test_io_cb_write_blk64;
+
+ data->outfile = NULL;
+ if ((value = safe_getenv("TEST_IO_LOGFILE")) != NULL)
+ data->outfile = fopen(value, "w");
+ if (!data->outfile)
+ data->outfile = stderr;
+
+ data->flags = 0;
+ if ((value = safe_getenv("TEST_IO_FLAGS")) != NULL)
+ data->flags = strtoul(value, NULL, 0);
+
+ data->block = 0;
+ if ((value = safe_getenv("TEST_IO_BLOCK")) != NULL)
+ data->block = strtoul(value, NULL, 0);
+
+ data->read_abort_count = 0;
+ if ((value = safe_getenv("TEST_IO_READ_ABORT")) != NULL)
+ data->read_abort_count = strtoul(value, NULL, 0);
+
+ data->write_abort_count = 0;
+ if ((value = safe_getenv("TEST_IO_WRITE_ABORT")) != NULL)
+ data->write_abort_count = strtoul(value, NULL, 0);
+
+ if (data->real) {
+ io->align = data->real->align;
+ if (data->real->flags & CHANNEL_FLAGS_THREADS)
+ io->flags |= CHANNEL_FLAGS_THREADS;
+ }
+
+ *channel = io;
+ return 0;
+
+cleanup:
+ if (io && io->name)
+ ext2fs_free_mem(&io->name);
+ if (io)
+ ext2fs_free_mem(&io);
+ if (data)
+ ext2fs_free_mem(&data);
+ return retval;
+}
+
+static errcode_t test_close(io_channel channel)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+
+ if (data->real)
+ retval = io_channel_close(data->real);
+
+ if (data->outfile && data->outfile != stderr)
+ fclose(data->outfile);
+
+ ext2fs_free_mem(&channel->private_data);
+ if (channel->name)
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+ return retval;
+}
+
+static errcode_t test_set_blksize(io_channel channel, int blksize)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real) {
+ retval = io_channel_set_blksize(data->real, blksize);
+ channel->align = data->real->align;
+ }
+ if (data->set_blksize)
+ data->set_blksize(blksize, retval);
+ if (data->flags & TEST_FLAG_SET_BLKSIZE)
+ fprintf(data->outfile,
+ "Test_io: set_blksize(%d) returned %s\n",
+ blksize, retval ? error_message(retval) : "OK");
+ channel->block_size = blksize;
+ return retval;
+}
+
+
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_read_blk(data->real, block, count, buf);
+ if (data->read_blk)
+ data->read_blk(block, count, retval);
+ if (data->flags & TEST_FLAG_READ)
+ fprintf(data->outfile,
+ "Test_io: read_blk(%lu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ if (data->block && data->block == block) {
+ if (data->flags & TEST_FLAG_DUMP)
+ test_dump_block(channel, data, block, buf);
+ if (--data->read_abort_count == 0)
+ test_abort(channel, block);
+ }
+ return retval;
+}
+
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_write_blk(data->real, block, count, buf);
+ if (data->write_blk)
+ data->write_blk(block, count, retval);
+ if (data->flags & TEST_FLAG_WRITE)
+ fprintf(data->outfile,
+ "Test_io: write_blk(%lu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ if (data->block && data->block == block) {
+ if (data->flags & TEST_FLAG_DUMP)
+ test_dump_block(channel, data, block, buf);
+ if (--data->write_abort_count == 0)
+ test_abort(channel, block);
+ }
+ return retval;
+}
+
+static errcode_t test_read_blk64(io_channel channel, unsigned long long block,
+ int count, void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_read_blk64(data->real, block, count, buf);
+ if (data->read_blk64)
+ data->read_blk64(block, count, retval);
+ if (data->flags & TEST_FLAG_READ)
+ fprintf(data->outfile,
+ "Test_io: read_blk64(%llu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ if (data->block && data->block == block) {
+ if (data->flags & TEST_FLAG_DUMP)
+ test_dump_block(channel, data, block, buf);
+ if (--data->read_abort_count == 0)
+ test_abort(channel, block);
+ }
+ return retval;
+}
+
+static errcode_t test_write_blk64(io_channel channel, unsigned long long block,
+ int count, const void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_write_blk64(data->real, block, count, buf);
+ if (data->write_blk64)
+ data->write_blk64(block, count, retval);
+ if (data->flags & TEST_FLAG_WRITE)
+ fprintf(data->outfile,
+ "Test_io: write_blk64(%llu, %d) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ if (data->block && data->block == block) {
+ if (data->flags & TEST_FLAG_DUMP)
+ test_dump_block(channel, data, block, buf);
+ if (--data->write_abort_count == 0)
+ test_abort(channel, block);
+ }
+ return retval;
+}
+
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+ int count, const void *buf)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real && data->real->manager->write_byte)
+ retval = io_channel_write_byte(data->real, offset, count, buf);
+ if (data->write_byte)
+ data->write_byte(offset, count, retval);
+ if (data->flags & TEST_FLAG_WRITE)
+ fprintf(data->outfile,
+ "Test_io: write_byte(%lu, %d) returned %s\n",
+ offset, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+static errcode_t test_set_option(io_channel channel, const char *option,
+ const char *arg)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+
+ if (data->flags & TEST_FLAG_SET_OPTION)
+ fprintf(data->outfile, "Test_io: set_option(%s, %s) ",
+ option, arg);
+ if (data->real && data->real->manager->set_option) {
+ retval = (data->real->manager->set_option)(data->real,
+ option, arg);
+ if (data->flags & TEST_FLAG_SET_OPTION)
+ fprintf(data->outfile, "returned %s\n",
+ retval ? error_message(retval) : "OK");
+ } else {
+ if (data->flags & TEST_FLAG_SET_OPTION)
+ fprintf(data->outfile, "not implemented\n");
+ }
+ return retval;
+}
+
+static errcode_t test_get_stats(io_channel channel, io_stats *stats)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real && data->real->manager->get_stats) {
+ retval = (data->real->manager->get_stats)(data->real, stats);
+ }
+ return retval;
+}
+
+static errcode_t test_discard(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_discard(data->real, block, count);
+ if (data->flags & TEST_FLAG_DISCARD)
+ fprintf(data->outfile,
+ "Test_io: discard(%llu, %llu) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+static errcode_t test_cache_readahead(io_channel channel,
+ unsigned long long block,
+ unsigned long long count)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_cache_readahead(data->real, block, count);
+ if (data->flags & TEST_FLAG_READAHEAD)
+ fprintf(data->outfile,
+ "Test_io: readahead(%llu, %llu) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+static errcode_t test_zeroout(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct test_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct test_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_zeroout(data->real, block, count);
+ if (data->flags & TEST_FLAG_ZEROOUT)
+ fprintf(data->outfile,
+ "Test_io: zeroout(%llu, %llu) returned %s\n",
+ block, count, retval ? error_message(retval) : "OK");
+ return retval;
+}
+
+static struct struct_io_manager struct_test_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Test I/O Manager",
+ .open = test_open,
+ .close = test_close,
+ .set_blksize = test_set_blksize,
+ .read_blk = test_read_blk,
+ .write_blk = test_write_blk,
+ .flush = test_flush,
+ .write_byte = test_write_byte,
+ .set_option = test_set_option,
+ .get_stats = test_get_stats,
+ .read_blk64 = test_read_blk64,
+ .write_blk64 = test_write_blk64,
+ .discard = test_discard,
+ .cache_readahead = test_cache_readahead,
+ .zeroout = test_zeroout,
+};
+
+io_manager test_io_manager = &struct_test_manager;
diff --git a/lib/ext2fs/tst_badblocks.c b/lib/ext2fs/tst_badblocks.c
new file mode 100644
index 0000000..b6e766a
--- /dev/null
+++ b/lib/ext2fs/tst_badblocks.c
@@ -0,0 +1,369 @@
+/*
+ * This testing program makes sure the badblocks implementation works.
+ *
+ * Copyright (C) 1996 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#define ADD_BLK 0x0001
+#define DEL_BLK 0x0002
+
+blk_t test1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 };
+blk_t test2[] = { 11, 10, 9, 8, 7, 6, 5, 4, 3, 3, 2, 1, 0 };
+blk_t test3[] = { 3, 1, 4, 5, 9, 2, 7, 10, 5, 6, 10, 8, 0 };
+blk_t test4[] = { 20, 50, 12, 17, 13, 2, 66, 23, 56, 0 };
+blk_t test4a[] = {
+ 20, 1,
+ 50, 1,
+ 3, 0,
+ 17, 1,
+ 18, 0,
+ 16, 0,
+ 11, 0,
+ 12, 1,
+ 13, 1,
+ 14, 0,
+ 80, 0,
+ 45, 0,
+ 66, 1,
+ 0 };
+blk_t test5[] = { 31, 20, 17, 51, 23, 1, 56, 57, 0 };
+blk_t test5a[] = {
+ 50, ADD_BLK,
+ 51, DEL_BLK,
+ 57, DEL_BLK,
+ 66, ADD_BLK,
+ 31, DEL_BLK,
+ 12, ADD_BLK,
+ 2, ADD_BLK,
+ 13, ADD_BLK,
+ 1, DEL_BLK,
+ 0
+ };
+
+
+static int test_fail = 0;
+static int test_expected_fail = 0;
+
+static errcode_t create_test_list(blk_t *vec, badblocks_list *ret)
+{
+ errcode_t retval;
+ badblocks_list bb;
+ int i;
+
+ retval = ext2fs_badblocks_list_create(&bb, 5);
+ if (retval) {
+ com_err("create_test_list", retval, "while creating list");
+ return retval;
+ }
+ for (i=0; vec[i]; i++) {
+ retval = ext2fs_badblocks_list_add(bb, vec[i]);
+ if (retval) {
+ com_err("create_test_list", retval,
+ "while adding test vector %d", i);
+ ext2fs_badblocks_list_free(bb);
+ return retval;
+ }
+ }
+ *ret = bb;
+ return 0;
+}
+
+static void print_list(badblocks_list bb, int verify)
+{
+ errcode_t retval;
+ badblocks_iterate iter;
+ blk_t blk;
+ int i, ok;
+
+ retval = ext2fs_badblocks_list_iterate_begin(bb, &iter);
+ if (retval) {
+ com_err("print_list", retval, "while setting up iterator");
+ return;
+ }
+ ok = i = 1;
+ while (ext2fs_badblocks_list_iterate(iter, &blk)) {
+ printf("%u ", blk);
+ if (i++ != blk)
+ ok = 0;
+ }
+ ext2fs_badblocks_list_iterate_end(iter);
+ if (verify) {
+ if (ok)
+ printf("--- OK");
+ else {
+ printf("--- NOT OK");
+ test_fail++;
+ }
+ }
+}
+
+static void validate_test_seq(badblocks_list bb, blk_t *vec)
+{
+ int i, match, ok;
+
+ for (i = 0; vec[i]; i += 2) {
+ match = ext2fs_badblocks_list_test(bb, vec[i]);
+ if (match == vec[i+1])
+ ok = 1;
+ else {
+ ok = 0;
+ test_fail++;
+ }
+ printf("\tblock %u is %s --- %s\n", vec[i],
+ match ? "present" : "absent",
+ ok ? "OK" : "NOT OK");
+ }
+}
+
+static void do_test_seq(badblocks_list bb, blk_t *vec)
+{
+ int i, match;
+
+ for (i = 0; vec[i]; i += 2) {
+ switch (vec[i+1]) {
+ case ADD_BLK:
+ ext2fs_badblocks_list_add(bb, vec[i]);
+ match = ext2fs_badblocks_list_test(bb, vec[i]);
+ printf("Adding block %u --- now %s\n", vec[i],
+ match ? "present" : "absent");
+ if (!match) {
+ printf("FAILURE!\n");
+ test_fail++;
+ }
+ break;
+ case DEL_BLK:
+ ext2fs_badblocks_list_del(bb, vec[i]);
+ match = ext2fs_badblocks_list_test(bb, vec[i]);
+ printf("Removing block %u --- now %s\n", vec[i],
+ ext2fs_badblocks_list_test(bb, vec[i]) ?
+ "present" : "absent");
+ if (match) {
+ printf("FAILURE!\n");
+ test_fail++;
+ }
+ break;
+ }
+ }
+}
+
+
+int file_test(badblocks_list bb)
+{
+ badblocks_list new_bb = 0;
+ errcode_t retval;
+ FILE *f;
+
+ f = tmpfile();
+ if (!f) {
+ fprintf(stderr, "Error opening temp file: %s\n",
+ error_message(errno));
+ return 1;
+ }
+ retval = ext2fs_write_bb_FILE(bb, 0, f);
+ if (retval) {
+ com_err("file_test", retval, "while writing bad blocks");
+ return 1;
+ }
+
+ rewind(f);
+ retval = ext2fs_read_bb_FILE2(0, f, &new_bb, 0, 0);
+ if (retval) {
+ com_err("file_test", retval, "while reading bad blocks");
+ return 1;
+ }
+ fclose(f);
+
+ if (ext2fs_badblocks_equal(bb, new_bb)) {
+ printf("Block bitmap matched after reading and writing.\n");
+ } else {
+ printf("Block bitmap NOT matched.\n");
+ test_fail++;
+ }
+ ext2fs_badblocks_list_free(new_bb);
+ return 0;
+}
+
+static void invalid_proc(ext2_filsys fs, blk_t blk)
+{
+ if (blk == 34500) {
+ printf("Expected invalid block\n");
+ test_expected_fail++;
+ } else {
+ printf("Invalid block #: %u\n", blk);
+ test_fail++;
+ }
+}
+
+void file_test_invalid(badblocks_list bb)
+{
+ badblocks_list new_bb = 0;
+ errcode_t retval;
+ ext2_filsys fs;
+ FILE *f;
+
+ fs = malloc(sizeof(struct struct_ext2_filsys));
+ memset(fs, 0, sizeof(struct struct_ext2_filsys));
+ fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+ fs->super = malloc(SUPERBLOCK_SIZE);
+ memset(fs->super, 0, SUPERBLOCK_SIZE);
+ fs->super->s_first_data_block = 1;
+ ext2fs_blocks_count_set(fs->super, 100);
+
+ f = tmpfile();
+ if (!f) {
+ fprintf(stderr, "Error opening temp file: %s\n",
+ error_message(errno));
+ test_fail++;
+ goto out;
+ }
+ retval = ext2fs_write_bb_FILE(bb, 0, f);
+ if (retval) {
+ com_err("file_test", retval, "while writing bad blocks");
+ test_fail++;
+ goto out;
+ }
+ fprintf(f, "34500\n");
+
+ rewind(f);
+ test_expected_fail = 0;
+ retval = ext2fs_read_bb_FILE(fs, f, &new_bb, invalid_proc);
+ if (retval) {
+ com_err("file_test", retval, "while reading bad blocks");
+ test_fail++;
+ goto out;
+ }
+ fclose(f);
+ if (!test_expected_fail) {
+ printf("Expected test failure didn't happen!\n");
+ test_fail++;
+ }
+
+
+ if (ext2fs_badblocks_equal(bb, new_bb)) {
+ printf("Block bitmap matched after reading and writing.\n");
+ } else {
+ printf("Block bitmap NOT matched.\n");
+ test_fail++;
+ }
+ ext2fs_badblocks_list_free(new_bb);
+out:
+ free(fs->super);
+ free(fs);
+}
+
+int main(int argc, char **argv)
+{
+ badblocks_list bb1, bb2, bb3, bb4, bb5;
+ int equal;
+ errcode_t retval;
+
+ add_error_table(&et_ext2_error_table);
+
+ bb1 = bb2 = bb3 = bb4 = bb5 = 0;
+
+ printf("test1: ");
+ retval = create_test_list(test1, &bb1);
+ if (retval == 0)
+ print_list(bb1, 1);
+ printf("\n");
+
+ printf("test2: ");
+ retval = create_test_list(test2, &bb2);
+ if (retval == 0)
+ print_list(bb2, 1);
+ printf("\n");
+
+ printf("test3: ");
+ retval = create_test_list(test3, &bb3);
+ if (retval == 0)
+ print_list(bb3, 1);
+ printf("\n");
+
+ printf("test4: ");
+ retval = create_test_list(test4, &bb4);
+ if (retval == 0) {
+ print_list(bb4, 0);
+ printf("\n");
+ validate_test_seq(bb4, test4a);
+ }
+ printf("\n");
+
+ printf("test5: ");
+ retval = create_test_list(test5, &bb5);
+ if (retval == 0) {
+ print_list(bb5, 0);
+ printf("\n");
+ do_test_seq(bb5, test5a);
+ printf("After test5 sequence: ");
+ print_list(bb5, 0);
+ printf("\n");
+ }
+ printf("\n");
+
+ if (bb1 && bb2 && bb3 && bb4 && bb5) {
+ printf("Comparison tests:\n");
+ equal = ext2fs_badblocks_equal(bb1, bb2);
+ printf("bb1 and bb2 are %sequal.\n", equal ? "" : "NOT ");
+ if (equal)
+ test_fail++;
+
+ equal = ext2fs_badblocks_equal(bb1, bb3);
+ printf("bb1 and bb3 are %sequal.\n", equal ? "" : "NOT ");
+ if (!equal)
+ test_fail++;
+
+ equal = ext2fs_badblocks_equal(bb1, bb4);
+ printf("bb1 and bb4 are %sequal.\n", equal ? "" : "NOT ");
+ if (equal)
+ test_fail++;
+
+ equal = ext2fs_badblocks_equal(bb4, bb5);
+ printf("bb4 and bb5 are %sequal.\n", equal ? "" : "NOT ");
+ if (!equal)
+ test_fail++;
+ printf("\n");
+ }
+
+ file_test(bb4);
+
+ file_test_invalid(bb4);
+
+ if (test_fail == 0)
+ printf("ext2fs library badblocks tests checks out OK!\n");
+
+ if (bb1)
+ ext2fs_badblocks_list_free(bb1);
+ if (bb2)
+ ext2fs_badblocks_list_free(bb2);
+ if (bb3)
+ ext2fs_badblocks_list_free(bb3);
+ if (bb4)
+ ext2fs_badblocks_list_free(bb4);
+ if (bb5)
+ ext2fs_badblocks_list_free(bb5);
+
+ return test_fail;
+
+}
diff --git a/lib/ext2fs/tst_bitmaps.c b/lib/ext2fs/tst_bitmaps.c
new file mode 100644
index 0000000..cb3c70d
--- /dev/null
+++ b/lib/ext2fs/tst_bitmaps.c
@@ -0,0 +1,732 @@
+/*
+ * tst_bitmaps.c
+ *
+ * Copyright (C) 2011 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 "config.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "ss/ss.h"
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+extern ss_request_table tst_bitmaps_cmds;
+
+static char subsystem_name[] = "tst_bitmaps";
+static char version[] = "1.0";
+
+ext2_filsys test_fs;
+int exit_status = 0;
+
+static int source_file(const char *cmd_file, int sci_idx)
+{
+ FILE *f;
+ char buf[256];
+ char *cp;
+ int retval;
+ int noecho;
+
+ if (strcmp(cmd_file, "-") == 0)
+ f = stdin;
+ else {
+ f = fopen(cmd_file, "r");
+ if (!f) {
+ perror(cmd_file);
+ exit(1);
+ }
+ }
+ fflush(stdout);
+ fflush(stderr);
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+ while (!feof(f)) {
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ break;
+ if (buf[0] == '#')
+ continue;
+ noecho = 0;
+ if (buf[0] == '-') {
+ noecho = 1;
+ buf[0] = ' ';
+ }
+ cp = strchr(buf, '\n');
+ if (cp)
+ *cp = 0;
+ cp = strchr(buf, '\r');
+ if (cp)
+ *cp = 0;
+ if (!noecho)
+ printf("%s: %s\n", subsystem_name, buf);
+ retval = ss_execute_line(sci_idx, buf);
+ if (retval) {
+ ss_perror(sci_idx, retval, buf);
+ exit_status++;
+ }
+ }
+ return exit_status;
+}
+
+
+/*
+ * This function resets the libc getopt() function, which keeps
+ * internal state. Bad design! Stupid libc API designers! No
+ * biscuit!
+ *
+ * BSD-derived getopt() functions require that optind be reset to 1 in
+ * order to reset getopt() state. This used to be generally accepted
+ * way of resetting getopt(). However, glibc's getopt()
+ * has additional getopt() state beyond optind, and requires that
+ * optind be set zero to reset its state. So the unfortunate state of
+ * affairs is that BSD-derived versions of getopt() misbehave if
+ * optind is set to 0 in order to reset getopt(), and glibc's getopt()
+ * will core dump if optind is set 1 in order to reset getopt().
+ *
+ * More modern versions of BSD require that optreset be set to 1 in
+ * order to reset getopt(). Sigh. Standards, anyone?
+ *
+ * We hide the hair here.
+ */
+void reset_getopt(void)
+{
+#if defined(__GLIBC__) || defined(__linux__)
+ optind = 0;
+#else
+ optind = 1;
+#endif
+#ifdef HAVE_OPTRESET
+ optreset = 1; /* Makes BSD getopt happy */
+#endif
+}
+
+/*
+ * This function will convert a string to an unsigned long, printing
+ * an error message if it fails, and returning success or failure in err.
+ */
+unsigned long parse_ulong(const char *str, const char *cmd,
+ const char *descr, int *err)
+{
+ char *tmp;
+ unsigned long ret;
+
+ ret = strtoul(str, &tmp, 0);
+ if (*tmp == 0) {
+ if (err)
+ *err = 0;
+ return ret;
+ }
+ com_err(cmd, 0, "Bad %s - %s", descr, str);
+ if (err)
+ *err = 1;
+ else
+ exit(1);
+ return 0;
+}
+
+
+int check_fs_open(char *name)
+{
+ if (!test_fs) {
+ com_err(name, 0, "Filesystem not open");
+ return 1;
+ }
+ return 0;
+}
+
+static void setup_filesystem(const char *name,
+ unsigned int blocks, unsigned int inodes,
+ unsigned int type, int flags)
+{
+ struct ext2_super_block param;
+ errcode_t retval;
+
+ memset(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, blocks);
+ param.s_inodes_count = inodes;
+
+ retval = ext2fs_initialize("test fs", flags, &param,
+ test_io_manager, &test_fs);
+
+ if (retval) {
+ com_err(name, retval, "while initializing filesystem");
+ return;
+ }
+ test_fs->default_bitmap_type = type;
+ ext2fs_free_block_bitmap(test_fs->block_map);
+ test_fs->block_map = 0;
+ ext2fs_free_inode_bitmap(test_fs->inode_map);
+ test_fs->inode_map = 0;
+ retval = ext2fs_allocate_block_bitmap(test_fs, "block bitmap",
+ &test_fs->block_map);
+ if (retval) {
+ com_err(name, retval, "while allocating block bitmap");
+ goto errout;
+ }
+ retval = ext2fs_allocate_inode_bitmap(test_fs, "inode bitmap",
+ &test_fs->inode_map);
+ if (retval) {
+ com_err(name, retval, "while allocating inode bitmap");
+ goto errout;
+ }
+ return;
+
+errout:
+ ext2fs_close_free(&test_fs);
+}
+
+void setup_cmd(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ int c, err;
+ unsigned int blocks = 128;
+ unsigned int inodes = 0;
+ unsigned int type = EXT2FS_BMAP64_BITARRAY;
+ int flags = EXT2_FLAG_64BITS;
+
+ if (test_fs)
+ ext2fs_close_free(&test_fs);
+
+ reset_getopt();
+ while ((c = getopt(argc, argv, "b:i:lt:")) != EOF) {
+ switch (c) {
+ case 'b':
+ blocks = parse_ulong(optarg, argv[0],
+ "number of blocks", &err);
+ if (err)
+ return;
+ break;
+ case 'i':
+ inodes = parse_ulong(optarg, argv[0],
+ "number of blocks", &err);
+ if (err)
+ return;
+ break;
+ case 'l': /* Legacy bitmaps */
+ flags = 0;
+ break;
+ case 't':
+ type = parse_ulong(optarg, argv[0],
+ "bitmap backend type", &err);
+ if (err)
+ return;
+ break;
+ default:
+ fprintf(stderr, "%s: usage: setup [-b blocks] "
+ "[-i inodes] [-t type]\n", argv[0]);
+ return;
+ }
+ }
+ setup_filesystem(argv[0], blocks, inodes, type, flags);
+}
+
+void close_cmd(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ if (check_fs_open(argv[0]))
+ return;
+
+ ext2fs_close_free(&test_fs);
+}
+
+
+void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
+{
+ unsigned char *buf;
+ errcode_t retval;
+ int i, len = (num - start + 7) / 8;
+
+ buf = malloc(len);
+ if (!buf) {
+ com_err("dump_bitmap", 0, "couldn't allocate buffer");
+ return;
+ }
+ memset(buf, 0, len);
+ retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
+ if (retval) {
+ com_err("dump_bitmap", retval,
+ "while calling ext2fs_generic_bmap_range");
+ free(buf);
+ return;
+ }
+ for (i=0; i < len; i++)
+ printf("%02x", buf[i]);
+ printf("\n");
+ printf("bits set: %u\n", ext2fs_bitcount(buf, len));
+ free(buf);
+}
+
+void dump_inode_bitmap_cmd(int argc, char **argv,
+ int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ if (check_fs_open(argv[0]))
+ return;
+
+ printf("inode bitmap: ");
+ dump_bitmap(test_fs->inode_map, 1, test_fs->super->s_inodes_count);
+}
+
+void dump_block_bitmap_cmd(int argc, char **argv,
+ int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ if (check_fs_open(argv[0]))
+ return;
+
+ printf("block bitmap: ");
+ dump_bitmap(test_fs->block_map, test_fs->super->s_first_data_block,
+ test_fs->super->s_blocks_count);
+}
+
+void do_setb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int block, num;
+ int err;
+ int test_result, op_result;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 2 && argc != 3) {
+ com_err(argv[0], 0, "Usage: setb <block> [num]");
+ return;
+ }
+
+ block = parse_ulong(argv[1], argv[0], "block", &err);
+ if (err)
+ return;
+
+ if (argc == 3) {
+ num = parse_ulong(argv[2], argv[0], "num", &err);
+ if (err)
+ return;
+
+ ext2fs_mark_block_bitmap_range2(test_fs->block_map,
+ block, num);
+ printf("Marking blocks %u to %u\n", block, block + num - 1);
+ return;
+ }
+
+ test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block);
+ op_result = ext2fs_mark_block_bitmap2(test_fs->block_map, block);
+ printf("Setting block %u, was %s before\n", block, op_result ?
+ "set" : "clear");
+ if (!test_result != !op_result)
+ com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
+ test_result, op_result);
+}
+
+void do_clearb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int block, num;
+ int err;
+ int test_result, op_result;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 2 && argc != 3) {
+ com_err(argv[0], 0, "Usage: clearb <block> [num]");
+ return;
+ }
+
+ block = parse_ulong(argv[1], argv[0], "block", &err);
+ if (err)
+ return;
+
+ if (argc == 3) {
+ num = parse_ulong(argv[2], argv[0], "num", &err);
+ if (err)
+ return;
+
+ ext2fs_unmark_block_bitmap_range2(test_fs->block_map,
+ block, num);
+ printf("Clearing blocks %u to %u\n", block, block + num - 1);
+ return;
+ }
+
+ test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block);
+ op_result = ext2fs_unmark_block_bitmap2(test_fs->block_map, block);
+ printf("Clearing block %u, was %s before\n", block, op_result ?
+ "set" : "clear");
+ if (!test_result != !op_result)
+ com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
+ test_result, op_result);
+}
+
+void do_testb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int block, num;
+ int err;
+ int test_result;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 2 && argc != 3) {
+ com_err(argv[0], 0, "Usage: testb <block> [num]");
+ return;
+ }
+
+ block = parse_ulong(argv[1], argv[0], "block", &err);
+ if (err)
+ return;
+
+ if (argc == 3) {
+ num = parse_ulong(argv[2], argv[0], "num", &err);
+ if (err)
+ return;
+
+ test_result =
+ ext2fs_test_block_bitmap_range2(test_fs->block_map,
+ block, num);
+ printf("Blocks %u to %u are %sall clear.\n",
+ block, block + num - 1, test_result ? "" : "NOT ");
+ return;
+ }
+
+ test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block);
+ printf("Block %u is %s\n", block, test_result ? "set" : "clear");
+}
+
+void do_ffzb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ blk64_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffzb <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_zero_block_bitmap2(test_fs->block_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_zero_block_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First unmarked block is %llu\n", (unsigned long long) out);
+}
+
+void do_ffsb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ blk64_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffsb <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_set_block_bitmap2(test_fs->block_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_set_block_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First marked block is %llu\n", (unsigned long long) out);
+}
+
+
+void do_zerob(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ if (check_fs_open(argv[0]))
+ return;
+
+ printf("Clearing block bitmap.\n");
+ ext2fs_clear_block_bitmap(test_fs->block_map);
+}
+
+void do_seti(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int inode;
+ int err;
+ int test_result, op_result;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 2) {
+ com_err(argv[0], 0, "Usage: seti <inode>");
+ return;
+ }
+
+ inode = parse_ulong(argv[1], argv[0], "inode", &err);
+ if (err)
+ return;
+
+ test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
+ op_result = ext2fs_mark_inode_bitmap2(test_fs->inode_map, inode);
+ printf("Setting inode %u, was %s before\n", inode, op_result ?
+ "set" : "clear");
+ if (!test_result != !op_result) {
+ com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
+ test_result, op_result);
+ exit_status++;
+ }
+}
+
+void do_cleari(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int inode;
+ int err;
+ int test_result, op_result;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 2) {
+ com_err(argv[0], 0, "Usage: clearb <inode>");
+ return;
+ }
+
+ inode = parse_ulong(argv[1], argv[0], "inode", &err);
+ if (err)
+ return;
+
+ test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
+ op_result = ext2fs_unmark_inode_bitmap2(test_fs->inode_map, inode);
+ printf("Clearing inode %u, was %s before\n", inode, op_result ?
+ "set" : "clear");
+ if (!test_result != !op_result) {
+ com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
+ test_result, op_result);
+ exit_status++;
+ }
+}
+
+void do_testi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int inode;
+ int err;
+ int test_result;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 2) {
+ com_err(argv[0], 0, "Usage: testb <inode>");
+ return;
+ }
+
+ inode = parse_ulong(argv[1], argv[0], "inode", &err);
+ if (err)
+ return;
+
+ test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
+ printf("Inode %u is %s\n", inode, test_result ? "set" : "clear");
+}
+
+void do_ffzi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ ext2_ino_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffzi <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_zero_inode_bitmap2(test_fs->inode_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_zero_inode_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First unmarked inode is %u\n", out);
+}
+
+void do_ffsi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ unsigned int start, end;
+ int err;
+ errcode_t retval;
+ ext2_ino_t out;
+
+ if (check_fs_open(argv[0]))
+ return;
+
+ if (argc != 3 && argc != 3) {
+ com_err(argv[0], 0, "Usage: ffsi <start> <end>");
+ return;
+ }
+
+ start = parse_ulong(argv[1], argv[0], "start", &err);
+ if (err)
+ return;
+
+ end = parse_ulong(argv[2], argv[0], "end", &err);
+ if (err)
+ return;
+
+ retval = ext2fs_find_first_set_inode_bitmap2(test_fs->inode_map,
+ start, end, &out);
+ if (retval) {
+ printf("ext2fs_find_first_set_inode_bitmap2() returned %s\n",
+ error_message(retval));
+ return;
+ }
+ printf("First marked inode is %u\n", out);
+}
+
+void do_zeroi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ if (check_fs_open(argv[0]))
+ return;
+
+ printf("Clearing inode bitmap.\n");
+ ext2fs_clear_inode_bitmap(test_fs->inode_map);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int blocks = 128;
+ unsigned int inodes = 0;
+ unsigned int type = EXT2FS_BMAP64_BITARRAY;
+ int c, err, code;
+ char *request = (char *)NULL;
+ char *cmd_file = 0;
+ int sci_idx;
+ int flags = EXT2_FLAG_64BITS;
+
+ add_error_table(&et_ss_error_table);
+ add_error_table(&et_ext2_error_table);
+ while ((c = getopt (argc, argv, "b:i:lt:R:f:")) != EOF) {
+ switch (c) {
+ case 'b':
+ blocks = parse_ulong(optarg, argv[0],
+ "number of blocks", &err);
+ if (err)
+ exit(1);
+ break;
+ case 'i':
+ inodes = parse_ulong(optarg, argv[0],
+ "number of blocks", &err);
+ if (err)
+ exit(1);
+ break;
+ case 'l': /* Legacy bitmaps */
+ flags = 0;
+ break;
+ case 't':
+ type = parse_ulong(optarg, argv[0],
+ "bitmap backend type", &err);
+ if (err)
+ exit(1);
+ break;
+ case 'R':
+ request = optarg;
+ break;
+ case 'f':
+ cmd_file = optarg;
+ break;
+ default:
+ com_err(argv[0], 0, "Usage: %s [-R request] "
+ "[-f cmd_file]", subsystem_name);
+ exit(1);
+ }
+ }
+
+ sci_idx = ss_create_invocation(subsystem_name, version,
+ (char *)NULL, &tst_bitmaps_cmds, &code);
+ if (code) {
+ ss_perror(sci_idx, code, "creating invocation");
+ exit(1);
+ }
+
+ (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code);
+ if (code) {
+ ss_perror(sci_idx, code, "adding standard requests");
+ exit (1);
+ }
+
+ printf("%s %s. Type '?' for a list of commands.\n\n",
+ subsystem_name, version);
+
+ setup_filesystem(argv[0], blocks, inodes, type, flags);
+
+ if (request) {
+ code = ss_execute_line(sci_idx, request);
+ if (code) {
+ ss_perror(sci_idx, code, request);
+ exit_status++;
+ }
+ } else if (cmd_file) {
+ exit_status = source_file(cmd_file, sci_idx);
+ } else {
+ ss_listen(sci_idx);
+ }
+
+ exit(exit_status);
+}
+
diff --git a/lib/ext2fs/tst_bitmaps_cmd.ct b/lib/ext2fs/tst_bitmaps_cmd.ct
new file mode 100644
index 0000000..13b7fa7
--- /dev/null
+++ b/lib/ext2fs/tst_bitmaps_cmd.ct
@@ -0,0 +1,51 @@
+command_table tst_bitmaps_cmds;
+
+request setup_cmd, "Setup file system",
+ setup;
+
+request close_cmd, "Close file system",
+ close;
+
+request dump_inode_bitmap_cmd, "Dump the inode bitmap",
+ dump_inode_bitmap, dump_ib;
+
+request dump_block_bitmap_cmd, "Dump the block bitmap",
+ dump_block_bitmap, dump_bb;
+
+request do_setb, "Set block",
+ set_block, setb;
+
+request do_clearb, "Clear block",
+ clear_block, clearb;
+
+request do_testb, "Test block",
+ test_block, testb;
+
+request do_ffzb, "Find first zero block",
+ find_first_zero_block, ffzb;
+
+request do_ffsb, "Find first set block",
+ find_first_set_block, ffsb;
+
+request do_zerob, "Clear block bitmap",
+ clear_block_bitmap, zerob;
+
+request do_seti, "Set inode",
+ set_inode, seti;
+
+request do_cleari, "Clear inode",
+ clear_inode, cleari;
+
+request do_testi, "Test inode",
+ test_inode, testi;
+
+request do_ffzi, "Find first zero inode",
+ find_first_zero_inode, ffzi;
+
+request do_ffsi, "Find first set inode",
+ find_first_set_inode, ffsi;
+
+request do_zeroi, "Clear inode bitmap",
+ clear_inode_bitmap, zeroi;
+
+end;
diff --git a/lib/ext2fs/tst_bitmaps_cmds b/lib/ext2fs/tst_bitmaps_cmds
new file mode 100644
index 0000000..dc116b1
--- /dev/null
+++ b/lib/ext2fs/tst_bitmaps_cmds
@@ -0,0 +1,150 @@
+setb 12 0
+setb 12
+setb 12
+clearb 12
+clearb 12
+setb 12
+setb 14
+setb 16
+testb 13
+testb 15
+testb 12
+testb 14
+setb 13
+setb 15
+testb 12
+testb 11
+testb 15
+testb 16
+dump_bb
+ffzb 11 16
+ffzb 12 16
+ffzb 12 20
+ffsb 0 127
+ffsb 1 128
+ffsb 1 127
+ffsb 1 10
+ffsb 1 11
+ffsb 12 12
+ffsb 13 12
+ffsb 12 15
+clearb 13
+ffzb 12 20
+ffsb 13 18
+setb 13
+clearb 12 7
+testb 12 7
+setb 15
+testb 12 7
+clearb 15
+testb 12 7
+setb 12 0
+setb 12 7
+dump_bb
+seti 2
+seti 5
+seti 4
+seti 3
+seti 4
+seti 5
+testi 6
+testi 1
+dump_ib
+ffzi 1 6
+ffzi 2 5
+ffzi 2 6
+ffsi 0 31
+ffsi 1 33
+ffsi 1 32
+ffsi 2 32
+ffsi 6 32
+cleari 4
+ffzi 2 6
+ffsi 4 32
+ffsi 5 32
+zeroi
+testi 5
+seti 5
+seti 5
+cleari 5
+cleari 5
+testi 17
+testi 6
+testi 4
+clearb 7 12
+dump_bb
+setb 1
+dump_bb
+setb 2
+dump_bb
+setb 3
+dump_bb
+setb 4
+dump_bb
+setb 5
+dump_bb
+setb 6
+dump_bb
+setb 7
+dump_bb
+setb 8
+dump_bb
+setb 10
+setb 12
+setb 14
+setb 17
+setb 19
+setb 24
+setb 26
+setb 27
+setb 30
+setb 31
+setb 32
+setb 35
+setb 39
+setb 40
+setb 44
+setb 46
+setb 47
+setb 49
+setb 51
+setb 52
+clearb 2
+clearb 3
+clearb 7
+dump_bb
+ffsb 14 127
+ffsb 15 127
+ffsb 36 127
+ffsb 32 127
+ffsb 52 127
+ffsb 53 127
+ffsb 46 127
+ffsb 45 127
+ffsb 41 127
+ffsb 20 127
+ffsb 1 127
+ffsb 2 127
+ffsb 3 127
+ffsb 4 127
+ffsb 5 127
+ffsb 6 127
+ffsb 7 127
+ffsb 8 127
+ffzb 1 127
+ffzb 2 127
+ffzb 3 127
+ffzb 4 127
+ffzb 5 127
+ffzb 6 127
+ffzb 7 127
+ffzb 8 127
+ffzb 45 127
+ffzb 46 127
+ffzb 47 127
+ffzb 48 127
+ffzb 49 127
+ffzb 50 127
+ffzb 51 127
+quit
+
diff --git a/lib/ext2fs/tst_bitmaps_exp b/lib/ext2fs/tst_bitmaps_exp
new file mode 100644
index 0000000..9cfea13
--- /dev/null
+++ b/lib/ext2fs/tst_bitmaps_exp
@@ -0,0 +1,313 @@
+tst_bitmaps 1.0. Type '?' for a list of commands.
+
+tst_bitmaps: setb 12 0
+Marking blocks 12 to 11
+tst_bitmaps: setb 12
+Setting block 12, was clear before
+tst_bitmaps: setb 12
+Setting block 12, was set before
+tst_bitmaps: clearb 12
+Clearing block 12, was set before
+tst_bitmaps: clearb 12
+Clearing block 12, was clear before
+tst_bitmaps: setb 12
+Setting block 12, was clear before
+tst_bitmaps: setb 14
+Setting block 14, was clear before
+tst_bitmaps: setb 16
+Setting block 16, was clear before
+tst_bitmaps: testb 13
+Block 13 is clear
+tst_bitmaps: testb 15
+Block 15 is clear
+tst_bitmaps: testb 12
+Block 12 is set
+tst_bitmaps: testb 14
+Block 14 is set
+tst_bitmaps: setb 13
+Setting block 13, was clear before
+tst_bitmaps: setb 15
+Setting block 15, was clear before
+tst_bitmaps: testb 12
+Block 12 is set
+tst_bitmaps: testb 11
+Block 11 is clear
+tst_bitmaps: testb 15
+Block 15 is set
+tst_bitmaps: testb 16
+Block 16 is set
+tst_bitmaps: dump_bb
+block bitmap: 00f80000000000000000000000000000
+bits set: 5
+tst_bitmaps: ffzb 11 16
+First unmarked block is 11
+tst_bitmaps: ffzb 12 16
+ext2fs_find_first_zero_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffzb 12 20
+First unmarked block is 17
+tst_bitmaps: ffsb 0 127
+ext2fs_find_first_set_block_bitmap2() returned Invalid argument
+tst_bitmaps: ffsb 1 128
+ext2fs_find_first_set_block_bitmap2() returned Invalid argument
+tst_bitmaps: ffsb 1 127
+First marked block is 12
+tst_bitmaps: ffsb 1 10
+ext2fs_find_first_set_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffsb 1 11
+ext2fs_find_first_set_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffsb 12 12
+First marked block is 12
+tst_bitmaps: ffsb 13 12
+ext2fs_find_first_set_block_bitmap2() returned Invalid argument
+tst_bitmaps: ffsb 12 15
+First marked block is 12
+tst_bitmaps: clearb 13
+Clearing block 13, was set before
+tst_bitmaps: ffzb 12 20
+First unmarked block is 13
+tst_bitmaps: ffsb 13 18
+First marked block is 14
+tst_bitmaps: setb 13
+Setting block 13, was clear before
+tst_bitmaps: clearb 12 7
+Clearing blocks 12 to 18
+tst_bitmaps: testb 12 7
+Blocks 12 to 18 are all clear.
+tst_bitmaps: setb 15
+Setting block 15, was clear before
+tst_bitmaps: testb 12 7
+Blocks 12 to 18 are NOT all clear.
+tst_bitmaps: clearb 15
+Clearing block 15, was set before
+tst_bitmaps: testb 12 7
+Blocks 12 to 18 are all clear.
+tst_bitmaps: setb 12 0
+Marking blocks 12 to 11
+tst_bitmaps: setb 12 7
+Marking blocks 12 to 18
+tst_bitmaps: dump_bb
+block bitmap: 00f80300000000000000000000000000
+bits set: 7
+tst_bitmaps: seti 2
+Setting inode 2, was clear before
+tst_bitmaps: seti 5
+Setting inode 5, was clear before
+tst_bitmaps: seti 4
+Setting inode 4, was clear before
+tst_bitmaps: seti 3
+Setting inode 3, was clear before
+tst_bitmaps: seti 4
+Setting inode 4, was set before
+tst_bitmaps: seti 5
+Setting inode 5, was set before
+tst_bitmaps: testi 6
+Inode 6 is clear
+tst_bitmaps: testi 1
+Inode 1 is clear
+tst_bitmaps: dump_ib
+inode bitmap: 1e000000
+bits set: 4
+tst_bitmaps: ffzi 1 6
+First unmarked inode is 1
+tst_bitmaps: ffzi 2 5
+ext2fs_find_first_zero_inode_bitmap2() returned No such file or directory
+tst_bitmaps: ffzi 2 6
+First unmarked inode is 6
+tst_bitmaps: ffsi 0 31
+ext2fs_find_first_set_inode_bitmap2() returned Invalid argument
+tst_bitmaps: ffsi 1 33
+ext2fs_find_first_set_inode_bitmap2() returned Invalid argument
+tst_bitmaps: ffsi 1 32
+First marked inode is 2
+tst_bitmaps: ffsi 2 32
+First marked inode is 2
+tst_bitmaps: ffsi 6 32
+ext2fs_find_first_set_inode_bitmap2() returned No such file or directory
+tst_bitmaps: cleari 4
+Clearing inode 4, was set before
+tst_bitmaps: ffzi 2 6
+First unmarked inode is 4
+tst_bitmaps: ffsi 4 32
+First marked inode is 5
+tst_bitmaps: ffsi 5 32
+First marked inode is 5
+tst_bitmaps: zeroi
+Clearing inode bitmap.
+tst_bitmaps: testi 5
+Inode 5 is clear
+tst_bitmaps: seti 5
+Setting inode 5, was clear before
+tst_bitmaps: seti 5
+Setting inode 5, was set before
+tst_bitmaps: cleari 5
+Clearing inode 5, was set before
+tst_bitmaps: cleari 5
+Clearing inode 5, was clear before
+tst_bitmaps: testi 17
+Inode 17 is clear
+tst_bitmaps: testi 6
+Inode 6 is clear
+tst_bitmaps: testi 4
+Inode 4 is clear
+tst_bitmaps: clearb 7 12
+Clearing blocks 7 to 18
+tst_bitmaps: dump_bb
+block bitmap: 00000000000000000000000000000000
+bits set: 0
+tst_bitmaps: setb 1
+Setting block 1, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 01000000000000000000000000000000
+bits set: 1
+tst_bitmaps: setb 2
+Setting block 2, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 03000000000000000000000000000000
+bits set: 2
+tst_bitmaps: setb 3
+Setting block 3, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 07000000000000000000000000000000
+bits set: 3
+tst_bitmaps: setb 4
+Setting block 4, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 0f000000000000000000000000000000
+bits set: 4
+tst_bitmaps: setb 5
+Setting block 5, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 1f000000000000000000000000000000
+bits set: 5
+tst_bitmaps: setb 6
+Setting block 6, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 3f000000000000000000000000000000
+bits set: 6
+tst_bitmaps: setb 7
+Setting block 7, was clear before
+tst_bitmaps: dump_bb
+block bitmap: 7f000000000000000000000000000000
+bits set: 7
+tst_bitmaps: setb 8
+Setting block 8, was clear before
+tst_bitmaps: dump_bb
+block bitmap: ff000000000000000000000000000000
+bits set: 8
+tst_bitmaps: setb 10
+Setting block 10, was clear before
+tst_bitmaps: setb 12
+Setting block 12, was clear before
+tst_bitmaps: setb 14
+Setting block 14, was clear before
+tst_bitmaps: setb 17
+Setting block 17, was clear before
+tst_bitmaps: setb 19
+Setting block 19, was clear before
+tst_bitmaps: setb 24
+Setting block 24, was clear before
+tst_bitmaps: setb 26
+Setting block 26, was clear before
+tst_bitmaps: setb 27
+Setting block 27, was clear before
+tst_bitmaps: setb 30
+Setting block 30, was clear before
+tst_bitmaps: setb 31
+Setting block 31, was clear before
+tst_bitmaps: setb 32
+Setting block 32, was clear before
+tst_bitmaps: setb 35
+Setting block 35, was clear before
+tst_bitmaps: setb 39
+Setting block 39, was clear before
+tst_bitmaps: setb 40
+Setting block 40, was clear before
+tst_bitmaps: setb 44
+Setting block 44, was clear before
+tst_bitmaps: setb 46
+Setting block 46, was clear before
+tst_bitmaps: setb 47
+Setting block 47, was clear before
+tst_bitmaps: setb 49
+Setting block 49, was clear before
+tst_bitmaps: setb 51
+Setting block 51, was clear before
+tst_bitmaps: setb 52
+Setting block 52, was clear before
+tst_bitmaps: clearb 2
+Clearing block 2, was set before
+tst_bitmaps: clearb 3
+Clearing block 3, was set before
+tst_bitmaps: clearb 7
+Clearing block 7, was set before
+tst_bitmaps: dump_bb
+block bitmap: b92a85e6c4680d000000000000000000
+bits set: 25
+tst_bitmaps: ffsb 14 127
+First marked block is 14
+tst_bitmaps: ffsb 15 127
+First marked block is 17
+tst_bitmaps: ffsb 36 127
+First marked block is 39
+tst_bitmaps: ffsb 32 127
+First marked block is 32
+tst_bitmaps: ffsb 52 127
+First marked block is 52
+tst_bitmaps: ffsb 53 127
+ext2fs_find_first_set_block_bitmap2() returned No such file or directory
+tst_bitmaps: ffsb 46 127
+First marked block is 46
+tst_bitmaps: ffsb 45 127
+First marked block is 46
+tst_bitmaps: ffsb 41 127
+First marked block is 44
+tst_bitmaps: ffsb 20 127
+First marked block is 24
+tst_bitmaps: ffsb 1 127
+First marked block is 1
+tst_bitmaps: ffsb 2 127
+First marked block is 4
+tst_bitmaps: ffsb 3 127
+First marked block is 4
+tst_bitmaps: ffsb 4 127
+First marked block is 4
+tst_bitmaps: ffsb 5 127
+First marked block is 5
+tst_bitmaps: ffsb 6 127
+First marked block is 6
+tst_bitmaps: ffsb 7 127
+First marked block is 8
+tst_bitmaps: ffsb 8 127
+First marked block is 8
+tst_bitmaps: ffzb 1 127
+First unmarked block is 2
+tst_bitmaps: ffzb 2 127
+First unmarked block is 2
+tst_bitmaps: ffzb 3 127
+First unmarked block is 3
+tst_bitmaps: ffzb 4 127
+First unmarked block is 7
+tst_bitmaps: ffzb 5 127
+First unmarked block is 7
+tst_bitmaps: ffzb 6 127
+First unmarked block is 7
+tst_bitmaps: ffzb 7 127
+First unmarked block is 7
+tst_bitmaps: ffzb 8 127
+First unmarked block is 9
+tst_bitmaps: ffzb 45 127
+First unmarked block is 45
+tst_bitmaps: ffzb 46 127
+First unmarked block is 48
+tst_bitmaps: ffzb 47 127
+First unmarked block is 48
+tst_bitmaps: ffzb 48 127
+First unmarked block is 48
+tst_bitmaps: ffzb 49 127
+First unmarked block is 50
+tst_bitmaps: ffzb 50 127
+First unmarked block is 50
+tst_bitmaps: ffzb 51 127
+First unmarked block is 53
+tst_bitmaps: quit
+tst_bitmaps:
diff --git a/lib/ext2fs/tst_bitops.c b/lib/ext2fs/tst_bitops.c
new file mode 100644
index 0000000..adef12d
--- /dev/null
+++ b/lib/ext2fs/tst_bitops.c
@@ -0,0 +1,288 @@
+/*
+ * This testing program makes sure the bitops functions work
+ *
+ * Copyright (C) 2001 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+unsigned char bitarray[] = {
+ 0x80, 0xF0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x10, 0x20, 0x00, 0x00
+ };
+
+int bits_list[] = {
+ 7, 12, 13, 14,15, 22, 30, 68, 77, -1,
+};
+
+#define BIG_TEST_BIT (((unsigned) 1 << 31) + 42)
+
+
+int main(int argc, char **argv)
+{
+ int i, j, size;
+ unsigned char testarray[12];
+ unsigned char *bigarray;
+
+ size = sizeof(bitarray)*8;
+#if 0
+ i = ext2fs_find_first_bit_set(bitarray, size);
+ while (i < size) {
+ printf("Bit set: %d\n", i);
+ i = ext2fs_find_next_bit_set(bitarray, size, i+1);
+ }
+#endif
+
+ /* Test test_bit */
+ for (i=0,j=0; i < size; i++) {
+ if (ext2fs_test_bit(i, bitarray)) {
+ if (bits_list[j] == i) {
+ j++;
+ } else {
+ printf("Bit %d set, not expected\n", i);
+ exit(1);
+ }
+ } else {
+ if (bits_list[j] == i) {
+ printf("Expected bit %d to be clear.\n", i);
+ exit(1);
+ }
+ }
+ }
+ printf("ext2fs_test_bit appears to be correct\n");
+
+ /* Test ext2fs_set_bit */
+ memset(testarray, 0, sizeof(testarray));
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_set_bit(bits_list[i], testarray);
+ }
+ if (memcmp(testarray, bitarray, sizeof(testarray)) == 0) {
+ printf("ext2fs_set_bit test succeeded.\n");
+ } else {
+ printf("ext2fs_set_bit test failed.\n");
+ for (i=0; i < sizeof(testarray); i++) {
+ printf("%02x ", testarray[i]);
+ }
+ printf("\n");
+ exit(1);
+ }
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_clear_bit(bits_list[i], testarray);
+ }
+ for (i=0; i < sizeof(testarray); i++) {
+ if (testarray[i]) {
+ printf("ext2fs_clear_bit failed, "
+ "testarray[%d] is %d\n", i, testarray[i]);
+ exit(1);
+ }
+ }
+ printf("ext2fs_clear_bit test succeed.\n");
+
+
+ /* Do bigarray test */
+ bigarray = malloc(1 << 29);
+ if (!bigarray) {
+ fprintf(stderr, "Failed to allocate scratch memory!\n");
+ exit(0);
+ }
+
+ bigarray[BIG_TEST_BIT >> 3] = 0;
+
+ ext2fs_set_bit(BIG_TEST_BIT, bigarray);
+ printf("big bit number (%u) test: %d, expected %d\n", BIG_TEST_BIT,
+ bigarray[BIG_TEST_BIT >> 3], (1 << (BIG_TEST_BIT & 7)));
+ if (bigarray[BIG_TEST_BIT >> 3] != (1 << (BIG_TEST_BIT & 7)))
+ exit(1);
+
+ ext2fs_clear_bit(BIG_TEST_BIT, bigarray);
+
+ printf("big bit number (%u) test: %d, expected 0\n", BIG_TEST_BIT,
+ bigarray[BIG_TEST_BIT >> 3]);
+ if (bigarray[BIG_TEST_BIT >> 3] != 0)
+ exit(1);
+
+ printf("ext2fs_set_bit big_test successful\n");
+
+
+ /* Now test ext2fs_fast_set_bit */
+ memset(testarray, 0, sizeof(testarray));
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_fast_set_bit(bits_list[i], testarray);
+ }
+ if (memcmp(testarray, bitarray, sizeof(testarray)) == 0) {
+ printf("ext2fs_fast_set_bit test succeeded.\n");
+ } else {
+ printf("ext2fs_fast_set_bit test failed.\n");
+ for (i=0; i < sizeof(testarray); i++) {
+ printf("%02x ", testarray[i]);
+ }
+ printf("\n");
+ exit(1);
+ }
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_clear_bit(bits_list[i], testarray);
+ }
+ for (i=0; i < sizeof(testarray); i++) {
+ if (testarray[i]) {
+ printf("ext2fs_clear_bit failed, "
+ "testarray[%d] is %d\n", i, testarray[i]);
+ exit(1);
+ }
+ }
+ printf("ext2fs_clear_bit test succeed.\n");
+
+
+ bigarray[BIG_TEST_BIT >> 3] = 0;
+
+ ext2fs_fast_set_bit(BIG_TEST_BIT, bigarray);
+ printf("big bit number (%u) test: %d, expected %d\n", BIG_TEST_BIT,
+ bigarray[BIG_TEST_BIT >> 3], (1 << (BIG_TEST_BIT & 7)));
+ if (bigarray[BIG_TEST_BIT >> 3] != (1 << (BIG_TEST_BIT & 7)))
+ exit(1);
+
+ ext2fs_fast_clear_bit(BIG_TEST_BIT, bigarray);
+
+ printf("big bit number (%u) test: %d, expected 0\n", BIG_TEST_BIT,
+ bigarray[BIG_TEST_BIT >> 3]);
+ if (bigarray[BIG_TEST_BIT >> 3] != 0)
+ exit(1);
+
+ printf("ext2fs_fast_set_bit big_test successful\n");
+
+ /* Repeat foregoing tests for 64-bit bitops */
+
+ /* Test test_bit */
+ for (i=0,j=0; i < size; i++) {
+ if (ext2fs_test_bit64(i, bitarray)) {
+ if (bits_list[j] == i) {
+ j++;
+ } else {
+ printf("64-bit: Bit %d set, not expected\n",
+ i);
+ exit(1);
+ }
+ } else {
+ if (bits_list[j] == i) {
+ printf("64-bit: "
+ "Expected bit %d to be clear.\n", i);
+ exit(1);
+ }
+ }
+ }
+ printf("64-bit: ext2fs_test_bit appears to be correct\n");
+
+ /* Test ext2fs_set_bit */
+ memset(testarray, 0, sizeof(testarray));
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_set_bit64(bits_list[i], testarray);
+ }
+ if (memcmp(testarray, bitarray, sizeof(testarray)) == 0) {
+ printf("64-bit: ext2fs_set_bit test succeeded.\n");
+ } else {
+ printf("64-bit: ext2fs_set_bit test failed.\n");
+ for (i=0; i < sizeof(testarray); i++) {
+ printf("%02x ", testarray[i]);
+ }
+ printf("\n");
+ exit(1);
+ }
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_clear_bit64(bits_list[i], testarray);
+ }
+ for (i=0; i < sizeof(testarray); i++) {
+ if (testarray[i]) {
+ printf("64-bit: ext2fs_clear_bit failed, "
+ "testarray[%d] is %d\n", i, testarray[i]);
+ exit(1);
+ }
+ }
+ printf("64-bit: ext2fs_clear_bit test succeed.\n");
+
+ /* Do bigarray test */
+ bigarray[BIG_TEST_BIT >> 3] = 0;
+
+ ext2fs_set_bit64(BIG_TEST_BIT, bigarray);
+ printf("64-bit: big bit number (%u) test: %d, expected %d\n",
+ BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3],
+ (1 << (BIG_TEST_BIT & 7)));
+ if (bigarray[BIG_TEST_BIT >> 3] != (1 << (BIG_TEST_BIT & 7)))
+ exit(1);
+
+ ext2fs_clear_bit64(BIG_TEST_BIT, bigarray);
+
+ printf("64-bit: big bit number (%u) test: %d, expected 0\n",
+ BIG_TEST_BIT,
+ bigarray[BIG_TEST_BIT >> 3]);
+ if (bigarray[BIG_TEST_BIT >> 3] != 0)
+ exit(1);
+
+ printf("64-bit: ext2fs_set_bit big_test successful\n");
+
+ /* Now test ext2fs_fast_set_bit */
+ memset(testarray, 0, sizeof(testarray));
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_fast_set_bit64(bits_list[i], testarray);
+ }
+ if (memcmp(testarray, bitarray, sizeof(testarray)) == 0) {
+ printf("64-bit: ext2fs_fast_set_bit test succeeded.\n");
+ } else {
+ printf("64-bit: ext2fs_fast_set_bit test failed.\n");
+ for (i=0; i < sizeof(testarray); i++) {
+ printf("%02x ", testarray[i]);
+ }
+ printf("\n");
+ exit(1);
+ }
+ for (i=0; bits_list[i] > 0; i++) {
+ ext2fs_clear_bit64(bits_list[i], testarray);
+ }
+ for (i=0; i < sizeof(testarray); i++) {
+ if (testarray[i]) {
+ printf("64-bit: ext2fs_clear_bit failed, "
+ "testarray[%d] is %d\n", i, testarray[i]);
+ exit(1);
+ }
+ }
+ printf("64-bit: ext2fs_clear_bit test succeed.\n");
+
+ bigarray[BIG_TEST_BIT >> 3] = 0;
+
+ ext2fs_fast_set_bit64(BIG_TEST_BIT, bigarray);
+ printf("64-bit: big bit number (%u) test: %d, expected %d\n",
+ BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3],
+ (1 << (BIG_TEST_BIT & 7)));
+ if (bigarray[BIG_TEST_BIT >> 3] != (1 << (BIG_TEST_BIT & 7)))
+ exit(1);
+
+ ext2fs_fast_clear_bit64(BIG_TEST_BIT, bigarray);
+
+ printf("64-bit: big bit number (%u) test: %d, expected 0\n",
+ BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3]);
+ if (bigarray[BIG_TEST_BIT >> 3] != 0)
+ exit(1);
+
+ printf("64-bit: ext2fs_fast_set_bit big_test successful\n");
+ free(bigarray);
+ exit(0);
+}
diff --git a/lib/ext2fs/tst_byteswap.c b/lib/ext2fs/tst_byteswap.c
new file mode 100644
index 0000000..c500cae
--- /dev/null
+++ b/lib/ext2fs/tst_byteswap.c
@@ -0,0 +1,93 @@
+/*
+ * This testing program makes sure the byteswap functions work
+ *
+ * Copyright (C) 2000 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+__u16 test1[] = {
+ 0x0001, 0x0100,
+ 0x1234, 0x3412,
+ 0xff00, 0x00ff,
+ 0x4000, 0x0040,
+ 0xfeff, 0xfffe,
+ 0x0000, 0x0000
+ };
+
+__u32 test2[] = {
+ 0x00000001, 0x01000000,
+ 0x80000000, 0x00000080,
+ 0x12345678, 0x78563412,
+ 0xffff0000, 0x0000ffff,
+ 0x00ff0000, 0x0000ff00,
+ 0xff000000, 0x000000ff,
+ 0x00000000, 0x00000000
+ };
+
+int main(int argc, char **argv)
+{
+ int i;
+ int errors = 0;
+
+ printf("Testing ext2fs_swab16\n");
+ i=0;
+ do {
+ printf("swab16(0x%04x) = 0x%04x\n", test1[i],
+ ext2fs_swab16(test1[i]));
+ if (ext2fs_swab16(test1[i]) != test1[i+1]) {
+ printf("Error!!! %04x != %04x\n",
+ ext2fs_swab16(test1[i]), test1[i+1]);
+ errors++;
+ }
+ if (ext2fs_swab16(test1[i+1]) != test1[i]) {
+ printf("Error!!! %04x != %04x\n",
+ ext2fs_swab16(test1[i+1]), test1[i]);
+ errors++;
+ }
+ i += 2;
+ } while (test1[i] != 0);
+
+ printf("Testing ext2fs_swab32\n");
+ i = 0;
+ do {
+ printf("swab32(0x%08x) = 0x%08x\n", test2[i],
+ ext2fs_swab32(test2[i]));
+ if (ext2fs_swab32(test2[i]) != test2[i+1]) {
+ printf("Error!!! %04x != %04x\n",
+ ext2fs_swab32(test2[i]), test2[i+1]);
+ errors++;
+ }
+ if (ext2fs_swab32(test2[i+1]) != test2[i]) {
+ printf("Error!!! %04x != %04x\n",
+ ext2fs_swab32(test2[i+1]), test2[i]);
+ errors++;
+ }
+ i += 2;
+ } while (test2[i] != 0);
+
+ if (!errors)
+ printf("No errors found in the byteswap implementation!\n");
+
+ return errors;
+}
diff --git a/lib/ext2fs/tst_cmds.ct b/lib/ext2fs/tst_cmds.ct
new file mode 100644
index 0000000..45a8594
--- /dev/null
+++ b/lib/ext2fs/tst_cmds.ct
@@ -0,0 +1,6 @@
+command_table libext2fs_cmds;
+
+request do_block_iterate, "block_iterate",
+ block_iterate;
+
+end;
diff --git a/lib/ext2fs/tst_fs_struct.c b/lib/ext2fs/tst_fs_struct.c
new file mode 100644
index 0000000..6f44df1
--- /dev/null
+++ b/lib/ext2fs/tst_fs_struct.c
@@ -0,0 +1,81 @@
+/*
+ * This testing program checks the offset of the ext2_filsys structure
+ *
+ * Copyright (C) 2007 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "ext2fs.h"
+
+struct struct_ext2_filsys fs;
+
+#ifndef offsetof
+#define offsetof(type, member) __builtin_offsetof (type, member)
+#endif
+#define check_field(x) cur_offset = do_field(#x, sizeof(fs.x), \
+ offsetof(struct struct_ext2_filsys, x), \
+ cur_offset)
+
+static int do_field(const char *field, size_t size, int offset, int cur_offset)
+{
+ if (offset != cur_offset) {
+ printf("\t(padding %d bytes?)\n", offset - cur_offset);
+ }
+ printf("%8d %-30s %3u\n", offset, field, (unsigned) size);
+ return offset + size;
+}
+
+int main(int argc, char **argv)
+{
+#if (__GNUC__ >= 4)
+ int cur_offset = 0;
+
+ printf("%8s %-30s %3s\n", "offset", "field", "size");
+ check_field(magic);
+ check_field(io);
+ check_field(flags);
+ check_field(device_name);
+ check_field(super);
+ check_field(blocksize);
+ check_field(fragsize);
+ check_field(group_desc_count);
+ check_field(desc_blocks);
+ check_field(group_desc);
+ check_field(inode_blocks_per_group);
+ check_field(inode_map);
+ check_field(block_map);
+ check_field(get_blocks);
+ check_field(check_directory);
+ check_field(write_bitmaps);
+ check_field(read_inode);
+ check_field(write_inode);
+ check_field(badblocks);
+ check_field(dblist);
+ check_field(stride);
+ check_field(orig_super);
+ check_field(image_header);
+ check_field(umask);
+ check_field(now);
+ check_field(cluster_ratio_bits);
+ check_field(reserved);
+ check_field(priv_data);
+ check_field(icache);
+ check_field(image_io);
+ check_field(get_alloc_block);
+ check_field(block_alloc_stats);
+ check_field(mmp_buf);
+ check_field(mmp_cmp);
+ check_field(mmp_fd);
+ check_field(mmp_last_written);
+ printf("Ending offset is %d\n\n", cur_offset);
+#endif
+ exit(0);
+}
diff --git a/lib/ext2fs/tst_getsectsize.c b/lib/ext2fs/tst_getsectsize.c
new file mode 100644
index 0000000..d616965
--- /dev/null
+++ b/lib/ext2fs/tst_getsectsize.c
@@ -0,0 +1,63 @@
+/*
+ * tst_getsize.c --- this function tests the getsize function
+ *
+ * Copyright (C) 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int main(int argc, char **argv)
+{
+ int lsectsize, psectsize;
+ int retval;
+ int fd;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n", argv[0]);
+ exit(1);
+ }
+
+ retval = ext2fs_get_device_sectsize(argv[1], &lsectsize);
+ if (retval) {
+ com_err(argv[0], retval,
+ "while calling ext2fs_get_device_sectsize");
+ exit(1);
+ }
+ retval = ext2fs_get_device_phys_sectsize(argv[1], &psectsize);
+ if (retval) {
+ com_err(argv[0], retval,
+ "while calling ext2fs_get_device_phys_sectsize");
+ exit(1);
+ }
+ printf("Device %s has logical/physical sector size of %d/%d.\n",
+ argv[1], lsectsize, psectsize);
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+ printf("The device's DIO alignment is %d\n",
+ ext2fs_get_dio_alignment(fd));
+ close(fd);
+ exit(0);
+}
diff --git a/lib/ext2fs/tst_getsize.c b/lib/ext2fs/tst_getsize.c
new file mode 100644
index 0000000..ba869dc
--- /dev/null
+++ b/lib/ext2fs/tst_getsize.c
@@ -0,0 +1,47 @@
+/*
+ * tst_getsize.c --- this function tests the getsize function
+ *
+ * Copyright (C) 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int main(int argc, const char *argv[])
+{
+ errcode_t retval;
+ blk64_t blocks;
+
+ if (argc < 2) {
+ fprintf(stderr, "%s device\n", argv[0]);
+ exit(1);
+ }
+ add_error_table(&et_ext2_error_table);
+ retval = ext2fs_get_device_size2(argv[1], 1024, &blocks);
+ if (retval) {
+ com_err(argv[0], retval, "while getting device size");
+ exit(1);
+ }
+ printf("%s is device has %llu blocks.\n", argv[1],
+ (unsigned long long) blocks);
+ return 0;
+}
diff --git a/lib/ext2fs/tst_inode_size.c b/lib/ext2fs/tst_inode_size.c
new file mode 100644
index 0000000..cc5d165
--- /dev/null
+++ b/lib/ext2fs/tst_inode_size.c
@@ -0,0 +1,89 @@
+/*
+ * This testing program makes sure the ext2_inode structure is 1024 bytes long
+ *
+ * Copyright (C) 2007 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 "config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "ext2_fs.h"
+
+struct ext2_inode_large inode;
+
+#ifndef offsetof
+#define offsetof(type, member) __builtin_offsetof(type, member)
+#endif
+
+#define check_field(x, s) cur_offset = do_field(#x, s, sizeof(inode.x), \
+ offsetof(struct ext2_inode_large, x), \
+ cur_offset)
+
+static int do_field(const char *field, unsigned size, unsigned cur_size,
+ unsigned offset, unsigned cur_offset)
+{
+ if (size != cur_size) {
+ printf("error: %s size %u should be %u\n",
+ field, cur_size, size);
+ exit(1);
+ }
+ if (offset != cur_offset) {
+ printf("error: %s offset %u should be %u\n",
+ field, cur_offset, offset);
+ exit(1);
+ }
+ printf("%8d %-30s %3u\n", offset, field, (unsigned) size);
+ return offset + size;
+}
+
+int main(int argc, char **argv)
+{
+#if (__GNUC__ >= 4)
+ int cur_offset = 0;
+
+ printf("%8s %-30s %3s\n", "offset", "field", "size");
+ check_field(i_mode, 2);
+ check_field(i_uid, 2);
+ check_field(i_size, 4);
+ check_field(i_atime, 4);
+ check_field(i_ctime, 4);
+ check_field(i_mtime, 4);
+ check_field(i_dtime, 4);
+ check_field(i_gid, 2);
+ check_field(i_links_count, 2);
+ check_field(i_blocks, 4);
+ check_field(i_flags, 4);
+ check_field(osd1.linux1.l_i_version, 4);
+ check_field(i_block, 15 * 4);
+ check_field(i_generation, 4);
+ check_field(i_file_acl, 4);
+ check_field(i_size_high, 4);
+ check_field(i_faddr, 4);
+ check_field(osd2.linux2.l_i_blocks_hi, 2);
+ check_field(osd2.linux2.l_i_file_acl_high, 2);
+ check_field(osd2.linux2.l_i_uid_high, 2);
+ check_field(osd2.linux2.l_i_gid_high, 2);
+ check_field(osd2.linux2.l_i_checksum_lo, 2);
+ check_field(osd2.linux2.l_i_reserved, 2);
+ do_field("Small inode end", 0, 0, cur_offset, 128);
+ check_field(i_extra_isize, 2);
+ check_field(i_checksum_hi, 2);
+ check_field(i_ctime_extra, 4);
+ check_field(i_mtime_extra, 4);
+ check_field(i_atime_extra, 4);
+ check_field(i_crtime, 4);
+ check_field(i_crtime_extra, 4);
+ check_field(i_version_hi, 4);
+ check_field(i_projid, 4);
+ /* This size will change as new fields are added */
+ do_field("Large inode end", 0, 0, cur_offset, sizeof(inode));
+#endif
+ return 0;
+}
diff --git a/lib/ext2fs/tst_iscan.c b/lib/ext2fs/tst_iscan.c
new file mode 100644
index 0000000..76aaa9a
--- /dev/null
+++ b/lib/ext2fs/tst_iscan.c
@@ -0,0 +1,227 @@
+/*
+ * tst_inode.c --- this function tests the inode scan function
+ *
+ * Copyright (C) 1996 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+blk64_t test_vec[] = { 8, 12, 24, 34, 43, 44, 100, 0 };
+
+ext2_filsys test_fs;
+ext2fs_block_bitmap bad_block_map, touched_map;
+ext2fs_inode_bitmap bad_inode_map;
+badblocks_list test_badblocks;
+
+int first_no_comma = 1;
+int failed = 0;
+
+static void iscan_test_read_blk64(unsigned long long block, int count, errcode_t err)
+{
+ int i;
+
+ if (first_no_comma)
+ first_no_comma = 0;
+ else
+ printf(", ");
+
+ if (count > 1)
+ printf("%llu-%llu", block, block+count-1);
+ else
+ printf("%llu", block);
+
+ for (i=0; i < count; i++, block++) {
+ if (ext2fs_test_block_bitmap2(touched_map, block)) {
+ printf("\nDuplicate block?!? --- %llu\n", block);
+ failed++;
+ first_no_comma = 1;
+ }
+ ext2fs_mark_block_bitmap2(touched_map, block);
+ }
+}
+
+static void iscan_test_read_blk(unsigned long block, int count, errcode_t err)
+{
+ iscan_test_read_blk64(block, count, err);
+}
+
+/*
+ * Setup the variables for doing the inode scan test.
+ */
+static void setup(void)
+{
+ errcode_t retval;
+ int i;
+ struct ext2_super_block param;
+
+ initialize_ext2_error_table();
+
+ memset(&param, 0, sizeof(param));
+ ext2fs_blocks_count_set(&param, 12000);
+
+
+ test_io_cb_read_blk = iscan_test_read_blk;
+ test_io_cb_read_blk64 = iscan_test_read_blk64;
+
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
+ 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);
+ }
+ retval = ext2fs_allocate_block_bitmap(test_fs, "bad block map",
+ &bad_block_map);
+ if (retval) {
+ com_err("setup", retval,
+ "While allocating bad_block bitmap");
+ exit(1);
+ }
+ retval = ext2fs_allocate_block_bitmap(test_fs, "touched map",
+ &touched_map);
+ if (retval) {
+ com_err("setup", retval,
+ "While allocating touched block bitmap");
+ exit(1);
+ }
+ retval = ext2fs_allocate_inode_bitmap(test_fs, "bad inode map",
+ &bad_inode_map);
+ if (retval) {
+ com_err("setup", retval,
+ "While allocating bad inode bitmap");
+ exit(1);
+ }
+
+ retval = ext2fs_badblocks_list_create(&test_badblocks, 5);
+ if (retval) {
+ com_err("setup", retval, "while creating badblocks list");
+ exit(1);
+ }
+ for (i=0; test_vec[i]; i++) {
+ retval = ext2fs_badblocks_list_add(test_badblocks, test_vec[i]);
+ if (retval) {
+ com_err("setup", retval,
+ "while adding test vector %d", i);
+ exit(1);
+ }
+ ext2fs_mark_block_bitmap2(bad_block_map, test_vec[i]);
+ }
+ test_fs->badblocks = test_badblocks;
+}
+
+/*
+ * Iterate using inode_scan
+ */
+static void iterate(void)
+{
+ struct ext2_inode inode;
+ ext2_inode_scan scan;
+ errcode_t retval;
+ ext2_ino_t ino;
+
+ retval = ext2fs_open_inode_scan(test_fs, 8, &scan);
+ if (retval) {
+ com_err("iterate", retval, "While opening inode scan");
+ exit(1);
+ }
+ printf("Reading blocks: ");
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval) {
+ com_err("iterate", retval, "while reading first inode");
+ exit(1);
+ }
+ while (ino) {
+ retval = ext2fs_get_next_inode(scan, &ino, &inode);
+ if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
+ ext2fs_mark_inode_bitmap2(bad_inode_map, ino);
+ continue;
+ }
+ if (retval) {
+ com_err("iterate", retval,
+ "while getting next inode");
+ exit(1);
+ }
+ }
+ printf("\n");
+ ext2fs_close_inode_scan(scan);
+}
+
+/*
+ * Verify the touched map
+ */
+static void check_map(void)
+{
+ int i, j, first=1;
+ blk64_t blk;
+
+ for (i=0; test_vec[i]; i++) {
+ if (ext2fs_test_block_bitmap2(touched_map, test_vec[i])) {
+ printf("Bad block was touched --- %llu\n",
+ (unsigned long long) test_vec[i]);
+ failed++;
+ first_no_comma = 1;
+ }
+ ext2fs_mark_block_bitmap2(touched_map, test_vec[i]);
+ }
+ for (i = 0; i < test_fs->group_desc_count; i++) {
+ for (j=0, blk = ext2fs_inode_table_loc(test_fs, i);
+ j < test_fs->inode_blocks_per_group;
+ j++, blk++) {
+ if (!ext2fs_test_block_bitmap2(touched_map, blk) &&
+ !ext2fs_test_block_bitmap2(bad_block_map, blk)) {
+ printf("Missing block --- %llu\n",
+ (unsigned long long) blk);
+ failed++;
+ }
+ }
+ }
+ printf("Bad inodes: ");
+ for (i=1; i <= test_fs->super->s_inodes_count; i++) {
+ if (ext2fs_test_inode_bitmap2(bad_inode_map, i)) {
+ if (first)
+ first = 0;
+ else
+ printf(", ");
+ printf("%u", i);
+ }
+ }
+ printf("\n");
+}
+
+
+int main(int argc, char **argv)
+{
+ setup();
+ iterate();
+ check_map();
+ if (!failed)
+ printf("Inode scan tested OK!\n");
+ return failed;
+}
+
diff --git a/lib/ext2fs/tst_libext2fs.c b/lib/ext2fs/tst_libext2fs.c
new file mode 100644
index 0000000..4c86464
--- /dev/null
+++ b/lib/ext2fs/tst_libext2fs.c
@@ -0,0 +1,72 @@
+/*
+ * tst_libext2fs.c
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+#include "ss/ss.h"
+#include "debugfs.h"
+
+/*
+ * Hook in new commands into debugfs
+ * Override debugfs's prompt
+ */
+const char *debug_prog_name = "tst_libext2fs";
+extern ss_request_table libext2fs_cmds;
+ss_request_table *extra_cmds = &libext2fs_cmds;
+
+static int print_blocks_proc(ext2_filsys fs EXT2FS_ATTR((unused)),
+ blk64_t *blocknr, e2_blkcnt_t blockcnt,
+ blk64_t ref_block, int ref_offset,
+ void *private EXT2FS_ATTR((unused)))
+{
+ printf("%6lld %8llu (%d %llu)\n", (long long) blockcnt,
+ (unsigned long long) *blocknr, ref_offset,
+ (unsigned long long) ref_block);
+ return 0;
+}
+
+
+void do_block_iterate(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)),
+ void *infop EXT2FS_ATTR((unused)))
+{
+ const char *usage = "block_iterate <file> <flags";
+ ext2_ino_t ino;
+ int err = 0;
+ int flags = 0;
+
+ if (common_args_process(argc, argv, 2, 3, argv[0], usage, 0))
+ return;
+
+ ino = string_to_inode(argv[1]);
+ if (!ino)
+ return;
+
+ if (argc > 2) {
+ flags = parse_ulong(argv[2], argv[0], "flags", &err);
+ if (err)
+ return;
+ }
+ flags |= BLOCK_FLAG_READ_ONLY;
+
+ ext2fs_block_iterate3(current_fs, ino, flags, NULL,
+ print_blocks_proc, NULL);
+ putc('\n', stdout);
+}
diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c
new file mode 100644
index 0000000..ad452de
--- /dev/null
+++ b/lib/ext2fs/tst_super_size.c
@@ -0,0 +1,161 @@
+/*
+ * This testing program makes sure superblock size is 1024 bytes long
+ *
+ * Copyright (C) 2007 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 "config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "ext2_fs.h"
+
+#define sb_struct ext2_super_block
+#define sb_struct_name "ext2_super_block"
+
+struct sb_struct sb;
+
+#ifndef offsetof
+#define offsetof(type, member) __builtin_offsetof (type, member)
+#endif
+
+#define check_field(x, s) cur_offset = do_field(#x, s, sizeof(sb.x), \
+ offsetof(struct sb_struct, x), \
+ cur_offset)
+
+static int do_field(const char *field, unsigned size, unsigned cur_size,
+ unsigned offset, unsigned cur_offset)
+{
+ if (size != cur_size) {
+ printf("error: %s size %u should be %u\n",
+ field, cur_size, size);
+ exit(1);
+ }
+ if (offset != cur_offset) {
+ printf("error: %s offset %u should be %u\n",
+ field, cur_offset, offset);
+ exit(1);
+ }
+ printf("%8d %-30s %3u\n", offset, field, size);
+ return offset + size;
+}
+
+int main(int argc, char **argv)
+{
+#if (__GNUC__ >= 4)
+ int cur_offset = 0;
+
+ printf("%8s %-30s %3s\n", "offset", "field", "size");
+ check_field(s_inodes_count, 4);
+ check_field(s_blocks_count, 4);
+ check_field(s_r_blocks_count, 4);
+ check_field(s_free_blocks_count, 4);
+ check_field(s_free_inodes_count, 4);
+ check_field(s_first_data_block, 4);
+ check_field(s_log_block_size, 4);
+ check_field(s_log_cluster_size, 4);
+ check_field(s_blocks_per_group, 4);
+ check_field(s_clusters_per_group, 4);
+ check_field(s_inodes_per_group, 4);
+ check_field(s_mtime, 4);
+ check_field(s_wtime, 4);
+ check_field(s_mnt_count, 2);
+ check_field(s_max_mnt_count, 2);
+ check_field(s_magic, 2);
+ check_field(s_state, 2);
+ check_field(s_errors, 2);
+ check_field(s_minor_rev_level, 2);
+ check_field(s_lastcheck, 4);
+ check_field(s_checkinterval, 4);
+ check_field(s_creator_os, 4);
+ check_field(s_rev_level, 4);
+ check_field(s_def_resuid, 2);
+ check_field(s_def_resgid, 2);
+ check_field(s_first_ino, 4);
+ check_field(s_inode_size, 2);
+ check_field(s_block_group_nr, 2);
+ check_field(s_feature_compat, 4);
+ check_field(s_feature_incompat, 4);
+ check_field(s_feature_ro_compat, 4);
+ check_field(s_uuid, 16);
+ check_field(s_volume_name, 16);
+ check_field(s_last_mounted, 64);
+ check_field(s_algorithm_usage_bitmap, 4);
+ check_field(s_prealloc_blocks, 1);
+ check_field(s_prealloc_dir_blocks, 1);
+ check_field(s_reserved_gdt_blocks, 2);
+ check_field(s_journal_uuid, 16);
+ check_field(s_journal_inum, 4);
+ check_field(s_journal_dev, 4);
+ check_field(s_last_orphan, 4);
+ check_field(s_hash_seed, 4 * 4);
+ check_field(s_def_hash_version, 1);
+ check_field(s_jnl_backup_type, 1);
+ check_field(s_desc_size, 2);
+ check_field(s_default_mount_opts, 4);
+ check_field(s_first_meta_bg, 4);
+ check_field(s_mkfs_time, 4);
+ check_field(s_jnl_blocks, 17 * 4);
+ check_field(s_blocks_count_hi, 4);
+ check_field(s_r_blocks_count_hi, 4);
+ check_field(s_free_blocks_hi, 4);
+ check_field(s_min_extra_isize, 2);
+ check_field(s_want_extra_isize, 2);
+ check_field(s_flags, 4);
+ check_field(s_raid_stride, 2);
+ check_field(s_mmp_update_interval, 2);
+ check_field(s_mmp_block, 8);
+ check_field(s_raid_stripe_width, 4);
+ check_field(s_log_groups_per_flex, 1);
+ check_field(s_checksum_type, 1);
+ check_field(s_encryption_level, 1);
+ check_field(s_reserved_pad, 1);
+ check_field(s_kbytes_written, 8);
+ check_field(s_snapshot_inum, 4);
+ check_field(s_snapshot_id, 4);
+ check_field(s_snapshot_r_blocks_count, 8);
+ check_field(s_snapshot_list, 4);
+ check_field(s_error_count, 4);
+ check_field(s_first_error_time, 4);
+ check_field(s_first_error_ino, 4);
+ check_field(s_first_error_block, 8);
+ check_field(s_first_error_func, 32);
+ check_field(s_first_error_line, 4);
+ check_field(s_last_error_time, 4);
+ check_field(s_last_error_ino, 4);
+ check_field(s_last_error_line, 4);
+ check_field(s_last_error_block, 8);
+ check_field(s_last_error_func, 32);
+ check_field(s_mount_opts, 64);
+ check_field(s_usr_quota_inum, 4);
+ check_field(s_grp_quota_inum, 4);
+ check_field(s_overhead_clusters, 4);
+ check_field(s_backup_bgs, 8);
+ check_field(s_encrypt_algos, 4);
+ check_field(s_encrypt_pw_salt, 16);
+ check_field(s_lpf_ino, 4);
+ check_field(s_prj_quota_inum, 4);
+ check_field(s_checksum_seed, 4);
+ check_field(s_wtime_hi, 1);
+ check_field(s_mtime_hi, 1);
+ check_field(s_mkfs_time_hi, 1);
+ check_field(s_lastcheck_hi, 1);
+ check_field(s_first_error_time_hi, 1);
+ check_field(s_last_error_time_hi, 1);
+ check_field(s_first_error_errcode, 1);
+ check_field(s_last_error_errcode, 1);
+ check_field(s_encoding, 2);
+ check_field(s_encoding_flags, 2);
+ check_field(s_orphan_file_inum, 4);
+ check_field(s_reserved, 94 * 4);
+ check_field(s_checksum, 4);
+ do_field("Superblock end", 0, 0, cur_offset, 1024);
+#endif
+ return 0;
+}
diff --git a/lib/ext2fs/tst_types.c b/lib/ext2fs/tst_types.c
new file mode 100644
index 0000000..3e41128
--- /dev/null
+++ b/lib/ext2fs/tst_types.c
@@ -0,0 +1,64 @@
+/*
+ * This testing program makes sure the ext2_types header file
+ *
+ * Copyright (C) 2006 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 "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ext2fs/ext2_types.h"
+
+int main(int argc, char **argv)
+{
+ if (sizeof(__u8) != 1) {
+ printf("Sizeof(__u8) is %d should be 1\n",
+ (int)sizeof(__u8));
+ exit(1);
+ }
+ if (sizeof(__s8) != 1) {
+ printf("Sizeof(_s8) is %d should be 1\n",
+ (int)sizeof(__s8));
+ exit(1);
+ }
+ if (sizeof(__u16) != 2) {
+ printf("Sizeof(__u16) is %d should be 2\n",
+ (int)sizeof(__u16));
+ exit(1);
+ }
+ if (sizeof(__s16) != 2) {
+ printf("Sizeof(__s16) is %d should be 2\n",
+ (int)sizeof(__s16));
+ exit(1);
+ }
+ if (sizeof(__u32) != 4) {
+ printf("Sizeof(__u32) is %d should be 4\n",
+ (int)sizeof(__u32));
+ exit(1);
+ }
+ if (sizeof(__s32) != 4) {
+ printf("Sizeof(__s32) is %d should be 4\n",
+ (int)sizeof(__s32));
+ exit(1);
+ }
+ if (sizeof(__u64) != 8) {
+ printf("Sizeof(__u64) is %d should be 8\n",
+ (int)sizeof(__u64));
+ exit(1);
+ }
+ if (sizeof(__s64) != 8) {
+ printf("Sizeof(__s64) is %d should be 8\n",
+ (int)sizeof(__s64));
+ exit(1);
+ }
+ printf("The ext2_types.h types are correct.\n");
+ exit(0);
+}
+
diff --git a/lib/ext2fs/undo_io.c b/lib/ext2fs/undo_io.c
new file mode 100644
index 0000000..f4a6d52
--- /dev/null
+++ b/lib/ext2fs/undo_io.c
@@ -0,0 +1,1108 @@
+/*
+ * undo_io.c --- This is the undo io manager that copies the old data that
+ * copies the old data being overwritten into a tdb database
+ *
+ * Copyright IBM Corporation, 2007
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#include <limits.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+#ifdef __GNUC__
+#define ATTR(x) __attribute__(x)
+#else
+#define ATTR(x)
+#endif
+
+#undef DEBUG
+
+#ifdef DEBUG
+# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
+#else
+# define dbg_printf(f, a...)
+#endif
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+/*
+ * Undo file format: The file is cut up into undo_header.block_size blocks.
+ * The first block contains the header.
+ * The second block contains the superblock.
+ * There is then a repeating series of blocks as follows:
+ * A key block, which contains undo_keys to map the following data blocks.
+ * Data blocks
+ * (Note that there are pointers to the first key block and the sb, so this
+ * order isn't strictly necessary.)
+ */
+#define E2UNDO_MAGIC "E2UNDO02"
+#define KEYBLOCK_MAGIC 0xCADECADE
+
+#define E2UNDO_STATE_FINISHED 0x1 /* undo file is complete */
+
+#define E2UNDO_MIN_BLOCK_SIZE 1024 /* undo blocks are no less than 1KB */
+#define E2UNDO_MAX_BLOCK_SIZE 1048576 /* undo blocks are no more than 1MB */
+
+struct undo_header {
+ char magic[8]; /* "E2UNDO02" */
+ __le64 num_keys; /* how many keys? */
+ __le64 super_offset; /* where in the file is the superblock copy? */
+ __le64 key_offset; /* where do the key/data block chunks start? */
+ __le32 block_size; /* block size of the undo file */
+ __le32 fs_block_size; /* block size of the target device */
+ __le32 sb_crc; /* crc32c of the superblock */
+ __le32 state; /* e2undo state flags */
+ __le32 f_compat; /* compatible features */
+ __le32 f_incompat; /* incompatible features (none so far) */
+ __le32 f_rocompat; /* ro compatible features (none so far) */
+ __le32 pad32; /* padding for fs_offset */
+ __le64 fs_offset; /* filesystem offset */
+ __u8 padding[436]; /* padding */
+ __le32 header_crc; /* crc32c of this header (but not this field) */
+};
+
+#define E2UNDO_MAX_EXTENT_BLOCKS 512 /* max extent size, in blocks */
+
+struct undo_key {
+ __le64 fsblk; /* where in the fs does the block go */
+ __le32 blk_crc; /* crc32c of the block */
+ __le32 size; /* how many bytes in this block? */
+};
+
+struct undo_key_block {
+ __le32 magic; /* KEYBLOCK_MAGIC number */
+ __le32 crc; /* block checksum */
+ __le64 reserved; /* zero */
+
+#if __STDC_VERSION__ >= 199901L
+ struct undo_key keys[]; /* keys, which come immediately after */
+#else
+ struct undo_key keys[0]; /* keys, which come immediately after */
+#endif
+};
+
+struct undo_private_data {
+ int magic;
+
+ /* the undo file io channel */
+ io_channel undo_file;
+ blk64_t undo_blk_num; /* next free block */
+ blk64_t key_blk_num; /* current key block location */
+ blk64_t super_blk_num; /* superblock location */
+ blk64_t first_key_blk; /* first key block location */
+ struct undo_key_block *keyb;
+ size_t num_keys, keys_in_block;
+
+ /* The backing io channel */
+ io_channel real;
+
+ unsigned long long tdb_data_size;
+ int tdb_written;
+
+ /* to support offset in unix I/O manager */
+ ext2_loff_t offset;
+
+ ext2fs_block_bitmap written_block_map;
+ struct struct_ext2_filsys fake_fs;
+ char *tdb_file;
+ struct undo_header hdr;
+};
+#define KEYS_PER_BLOCK(d) (((d)->tdb_data_size / sizeof(struct undo_key)) - 1)
+
+#define E2UNDO_FEATURE_COMPAT_FS_OFFSET 0x1 /* the filesystem offset */
+
+static inline void e2undo_set_feature_fs_offset(struct undo_header *header) {
+ header->f_compat |= ext2fs_le32_to_cpu(E2UNDO_FEATURE_COMPAT_FS_OFFSET);
+}
+
+static inline void e2undo_clear_feature_fs_offset(struct undo_header *header) {
+ header->f_compat &= ~ext2fs_le32_to_cpu(E2UNDO_FEATURE_COMPAT_FS_OFFSET);
+}
+
+static io_manager undo_io_backing_manager;
+static char *tdb_file;
+static int actual_size;
+
+errcode_t set_undo_io_backing_manager(io_manager manager)
+{
+ /*
+ * We may want to do some validation later
+ */
+ undo_io_backing_manager = manager;
+ return 0;
+}
+
+errcode_t set_undo_io_backup_file(char *file_name)
+{
+ tdb_file = strdup(file_name);
+
+ if (tdb_file == NULL) {
+ return EXT2_ET_NO_MEMORY;
+ }
+
+ return 0;
+}
+
+static errcode_t write_undo_indexes(struct undo_private_data *data, int flush)
+{
+ errcode_t retval;
+ struct ext2_super_block super;
+ io_channel channel;
+ int block_size;
+ __u32 sb_crc, hdr_crc;
+
+ /* Spit out a key block, if there's any data */
+ if (data->keys_in_block) {
+ data->keyb->magic = ext2fs_cpu_to_le32(KEYBLOCK_MAGIC);
+ data->keyb->crc = 0;
+ data->keyb->crc = ext2fs_cpu_to_le32(
+ ext2fs_crc32c_le(~0,
+ (unsigned char *)data->keyb,
+ data->tdb_data_size));
+ dbg_printf("Writing keyblock to blk %llu\n", data->key_blk_num);
+ retval = io_channel_write_blk64(data->undo_file,
+ data->key_blk_num,
+ 1, data->keyb);
+ if (retval)
+ return retval;
+ /* Move on to the next key block if it's full. */
+ if (data->keys_in_block == KEYS_PER_BLOCK(data)) {
+ memset(data->keyb, 0, data->tdb_data_size);
+ data->keys_in_block = 0;
+ data->key_blk_num = data->undo_blk_num;
+ data->undo_blk_num++;
+ }
+ }
+
+ /* Prepare superblock for write */
+ channel = data->real;
+ block_size = channel->block_size;
+
+ io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
+ retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
+ if (retval)
+ goto err_out;
+ sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)&super, SUPERBLOCK_SIZE);
+ super.s_magic = ~super.s_magic;
+
+ /* Write the undo header to disk. */
+ memcpy(data->hdr.magic, E2UNDO_MAGIC, sizeof(data->hdr.magic));
+ data->hdr.num_keys = ext2fs_cpu_to_le64(data->num_keys);
+ data->hdr.super_offset = ext2fs_cpu_to_le64(data->super_blk_num);
+ data->hdr.key_offset = ext2fs_cpu_to_le64(data->first_key_blk);
+ data->hdr.fs_block_size = ext2fs_cpu_to_le32(block_size);
+ data->hdr.sb_crc = ext2fs_cpu_to_le32(sb_crc);
+ data->hdr.fs_offset = ext2fs_cpu_to_le64(data->offset);
+ if (data->offset)
+ e2undo_set_feature_fs_offset(&data->hdr);
+ else
+ e2undo_clear_feature_fs_offset(&data->hdr);
+ hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&data->hdr,
+ sizeof(data->hdr) -
+ sizeof(data->hdr.header_crc));
+ data->hdr.header_crc = ext2fs_cpu_to_le32(hdr_crc);
+ retval = io_channel_write_blk64(data->undo_file, 0,
+ -(int)sizeof(data->hdr),
+ &data->hdr);
+ if (retval)
+ goto err_out;
+
+ /*
+ * Record the entire superblock (in FS byte order) so that we can't
+ * apply e2undo files to the wrong FS or out of order.
+ */
+ dbg_printf("Writing superblock to block %llu\n", data->super_blk_num);
+ retval = io_channel_write_blk64(data->undo_file, data->super_blk_num,
+ -SUPERBLOCK_SIZE, &super);
+ if (retval)
+ goto err_out;
+
+ if (flush)
+ retval = io_channel_flush(data->undo_file);
+err_out:
+ io_channel_set_blksize(channel, block_size);
+ return retval;
+}
+
+static errcode_t undo_setup_tdb(struct undo_private_data *data)
+{
+ int i;
+ errcode_t retval;
+
+ if (data->tdb_written == 1)
+ return 0;
+
+ data->tdb_written = 1;
+
+ /* Make a bitmap to track what we've written */
+ memset(&data->fake_fs, 0, sizeof(data->fake_fs));
+ data->fake_fs.blocksize = data->tdb_data_size;
+ retval = ext2fs_alloc_generic_bmap(&data->fake_fs,
+ EXT2_ET_MAGIC_BLOCK_BITMAP64,
+ EXT2FS_BMAP64_RBTREE,
+ 0, ~1ULL, ~1ULL,
+ "undo block map", &data->written_block_map);
+ if (retval)
+ return retval;
+
+ /* Allocate key block */
+ retval = ext2fs_get_mem(data->tdb_data_size, &data->keyb);
+ if (retval)
+ return retval;
+ data->key_blk_num = data->first_key_blk;
+
+ /* Record block size */
+ dbg_printf("Undo block size %llu\n", data->tdb_data_size);
+ dbg_printf("Keys per block %llu\n", KEYS_PER_BLOCK(data));
+ data->hdr.block_size = ext2fs_cpu_to_le32(data->tdb_data_size);
+ io_channel_set_blksize(data->undo_file, data->tdb_data_size);
+
+ /* Ensure that we have space for header blocks */
+ for (i = 0; i <= 2; i++) {
+ retval = io_channel_read_blk64(data->undo_file, i, 1,
+ data->keyb);
+ if (retval)
+ memset(data->keyb, 0, data->tdb_data_size);
+ retval = io_channel_write_blk64(data->undo_file, i, 1,
+ data->keyb);
+ if (retval)
+ return retval;
+ retval = io_channel_flush(data->undo_file);
+ if (retval)
+ return retval;
+ }
+ memset(data->keyb, 0, data->tdb_data_size);
+ return 0;
+}
+
+static errcode_t undo_write_tdb(io_channel channel,
+ unsigned long long block, int count)
+
+{
+ int size, sz;
+ unsigned long long block_num, backing_blk_num;
+ errcode_t retval = 0;
+ ext2_loff_t offset;
+ struct undo_private_data *data;
+ unsigned char *read_ptr;
+ unsigned long long end_block;
+ unsigned long long data_size;
+ struct undo_key *key;
+ __u32 blk_crc;
+
+ data = (struct undo_private_data *) channel->private_data;
+
+ if (data->undo_file == NULL) {
+ /*
+ * Transaction database not initialized
+ */
+ return 0;
+ }
+
+ if (count == 1)
+ size = channel->block_size;
+ else {
+ if (count < 0)
+ size = -count;
+ else
+ size = count * channel->block_size;
+ }
+
+ retval = undo_setup_tdb(data);
+ if (retval)
+ return retval;
+ /*
+ * Data is stored in tdb database as blocks of tdb_data_size size
+ * This helps in efficient lookup further.
+ *
+ * We divide the disk to blocks of tdb_data_size.
+ */
+ offset = (block * channel->block_size) + data->offset ;
+ block_num = offset / data->tdb_data_size;
+ end_block = (offset + size - 1) / data->tdb_data_size;
+
+ while (block_num <= end_block) {
+ __u32 keysz;
+
+ /*
+ * Check if we have the record already
+ */
+ if (ext2fs_test_block_bitmap2(data->written_block_map,
+ block_num)) {
+ /* Try the next block */
+ block_num++;
+ continue;
+ }
+ ext2fs_mark_block_bitmap2(data->written_block_map, block_num);
+
+ /*
+ * Read one block using the backing I/O manager
+ * The backing I/O manager block size may be
+ * different from the tdb_data_size.
+ * Also we need to recalculate the block number with respect
+ * to the backing I/O manager.
+ */
+ offset = block_num * data->tdb_data_size +
+ (data->offset % data->tdb_data_size);
+ backing_blk_num = (offset - data->offset) / channel->block_size;
+
+ retval = ext2fs_get_mem(data->tdb_data_size, &read_ptr);
+ if (retval) {
+ return retval;
+ }
+
+ memset(read_ptr, 0, data->tdb_data_size);
+ actual_size = 0;
+ if ((data->tdb_data_size % channel->block_size) == 0)
+ sz = data->tdb_data_size / channel->block_size;
+ else
+ sz = -data->tdb_data_size;
+ retval = io_channel_read_blk64(data->real, backing_blk_num,
+ sz, read_ptr);
+ if (retval) {
+ if (retval != EXT2_ET_SHORT_READ) {
+ free(read_ptr);
+ return retval;
+ }
+ /*
+ * short read so update the record size
+ * accordingly
+ */
+ data_size = actual_size;
+ } else {
+ data_size = data->tdb_data_size;
+ }
+ if (data_size == 0) {
+ free(read_ptr);
+ block_num++;
+ continue;
+ }
+ dbg_printf("Read %llu bytes from FS block %llu (blk=%llu cnt=%llu)\n",
+ data_size, backing_blk_num, block, data->tdb_data_size);
+ if ((data_size % data->undo_file->block_size) == 0)
+ sz = data_size / data->undo_file->block_size;
+ else
+ sz = -data_size;;
+ /* extend this key? */
+ if (data->keys_in_block) {
+ key = data->keyb->keys + data->keys_in_block - 1;
+ keysz = ext2fs_le32_to_cpu(key->size);
+ } else {
+ key = NULL;
+ keysz = 0;
+ }
+ if (key != NULL &&
+ (ext2fs_le64_to_cpu(key->fsblk) * channel->block_size +
+ channel->block_size - 1 +
+ keysz) / channel->block_size == backing_blk_num &&
+ E2UNDO_MAX_EXTENT_BLOCKS * data->tdb_data_size >
+ keysz + data_size) {
+ blk_crc = ext2fs_le32_to_cpu(key->blk_crc);
+ blk_crc = ext2fs_crc32c_le(blk_crc, read_ptr, data_size);
+ key->blk_crc = ext2fs_cpu_to_le32(blk_crc);
+ key->size = ext2fs_cpu_to_le32(keysz + data_size);
+ } else {
+ data->num_keys++;
+ key = data->keyb->keys + data->keys_in_block;
+ data->keys_in_block++;
+ key->fsblk = ext2fs_cpu_to_le64(backing_blk_num);
+ blk_crc = ext2fs_crc32c_le(~0, read_ptr, data_size);
+ key->blk_crc = ext2fs_cpu_to_le32(blk_crc);
+ key->size = ext2fs_cpu_to_le32(data_size);
+ }
+ dbg_printf("Writing block %llu to offset %llu size %d key %zu\n",
+ block_num,
+ data->undo_blk_num,
+ sz, data->num_keys - 1);
+ retval = io_channel_write_blk64(data->undo_file,
+ data->undo_blk_num, sz, read_ptr);
+ if (retval) {
+ free(read_ptr);
+ return retval;
+ }
+ data->undo_blk_num++;
+ free(read_ptr);
+
+ /* Write out the key block */
+ retval = write_undo_indexes(data, 0);
+ if (retval)
+ return retval;
+
+ /* Next block */
+ block_num++;
+ }
+
+ return retval;
+}
+
+static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
+ unsigned long block ATTR((unused)),
+ int count ATTR((unused)),
+ void *data ATTR((unused)),
+ size_t size ATTR((unused)),
+ int actual,
+ errcode_t error ATTR((unused)))
+{
+ actual_size = actual;
+ return error;
+}
+
+static void undo_err_handler_init(io_channel channel)
+{
+ channel->read_error = undo_io_read_error;
+}
+
+static int check_filesystem(struct undo_header *hdr, io_channel undo_file,
+ unsigned int blocksize, blk64_t super_block,
+ io_channel channel)
+{
+ struct ext2_super_block super, *sb;
+ char *buf;
+ __u32 sb_crc;
+ errcode_t retval;
+
+ io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
+ retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
+ if (retval)
+ return retval;
+
+ /*
+ * Compare the FS and the undo file superblock so that we don't
+ * append to something that doesn't match this FS.
+ */
+ retval = ext2fs_get_mem(blocksize, &buf);
+ if (retval)
+ return retval;
+ retval = io_channel_read_blk64(undo_file, super_block,
+ -SUPERBLOCK_SIZE, buf);
+ if (retval)
+ goto out;
+ sb = (struct ext2_super_block *)buf;
+ sb->s_magic = ~sb->s_magic;
+ if (memcmp(&super, buf, sizeof(super))) {
+ retval = -1;
+ goto out;
+ }
+ sb_crc = ext2fs_crc32c_le(~0, (unsigned char *)buf, SUPERBLOCK_SIZE);
+ if (ext2fs_le32_to_cpu(hdr->sb_crc) != sb_crc) {
+ retval = -1;
+ goto out;
+ }
+
+out:
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+/*
+ * Try to re-open the undo file, so that we can resume where we left off.
+ * That way, the user can pass the same undo file to various programs as
+ * part of an FS upgrade instead of having to create multiple files and
+ * then apply them in correct order.
+ */
+static errcode_t try_reopen_undo_file(int undo_fd,
+ struct undo_private_data *data)
+{
+ struct undo_header hdr;
+ struct undo_key *dkey;
+ ext2fs_struct_stat statbuf;
+ unsigned int blocksize, fs_blocksize;
+ blk64_t super_block, lblk;
+ size_t num_keys, keys_per_block, i;
+ __u32 hdr_crc, key_crc;
+ errcode_t retval;
+
+ /* Zero size already? */
+ retval = ext2fs_fstat(undo_fd, &statbuf);
+ if (retval)
+ goto bad_file;
+ if (statbuf.st_size == 0)
+ goto out;
+
+ /* check the file header */
+ retval = io_channel_read_blk64(data->undo_file, 0, -(int)sizeof(hdr),
+ &hdr);
+ if (retval)
+ goto bad_file;
+
+ if (memcmp(hdr.magic, E2UNDO_MAGIC,
+ sizeof(hdr.magic)))
+ goto bad_file;
+ hdr_crc = ext2fs_crc32c_le(~0, (unsigned char *)&hdr,
+ sizeof(struct undo_header) -
+ sizeof(__u32));
+ if (ext2fs_le32_to_cpu(hdr.header_crc) != hdr_crc)
+ goto bad_file;
+ blocksize = ext2fs_le32_to_cpu(hdr.block_size);
+ fs_blocksize = ext2fs_le32_to_cpu(hdr.fs_block_size);
+ if (blocksize > E2UNDO_MAX_BLOCK_SIZE ||
+ blocksize < E2UNDO_MIN_BLOCK_SIZE ||
+ !blocksize || !fs_blocksize)
+ goto bad_file;
+ super_block = ext2fs_le64_to_cpu(hdr.super_offset);
+ num_keys = ext2fs_le64_to_cpu(hdr.num_keys);
+ io_channel_set_blksize(data->undo_file, blocksize);
+ /*
+ * Do not compare hdr.f_compat with the available compatible
+ * features set, because a "missing" compatible feature should
+ * not cause any problems.
+ */
+ if (hdr.f_incompat || hdr.f_rocompat)
+ goto bad_file;
+
+ /* Superblock matches this FS? */
+ if (check_filesystem(&hdr, data->undo_file, blocksize, super_block,
+ data->real) != 0) {
+ retval = EXT2_ET_UNDO_FILE_WRONG;
+ goto out;
+ }
+
+ /* Try to set ourselves up */
+ data->tdb_data_size = blocksize;
+ retval = undo_setup_tdb(data);
+ if (retval)
+ goto bad_file;
+ data->num_keys = num_keys;
+ data->super_blk_num = super_block;
+ data->first_key_blk = ext2fs_le64_to_cpu(hdr.key_offset);
+
+ /* load the written block map */
+ keys_per_block = KEYS_PER_BLOCK(data);
+ lblk = data->first_key_blk;
+ dbg_printf("nr_keys=%lu, kpb=%zu, blksz=%u\n",
+ num_keys, keys_per_block, blocksize);
+ for (i = 0; i < num_keys; i += keys_per_block) {
+ size_t j, max_j;
+ __le32 crc;
+
+ data->key_blk_num = lblk;
+ retval = io_channel_read_blk64(data->undo_file,
+ lblk, 1, data->keyb);
+ if (retval)
+ goto bad_key_replay;
+
+ /* check keys */
+ if (ext2fs_le32_to_cpu(data->keyb->magic) != KEYBLOCK_MAGIC) {
+ retval = EXT2_ET_UNDO_FILE_CORRUPT;
+ goto bad_key_replay;
+ }
+ crc = data->keyb->crc;
+ data->keyb->crc = 0;
+ key_crc = ext2fs_crc32c_le(~0, (unsigned char *)data->keyb,
+ blocksize);
+ if (ext2fs_le32_to_cpu(crc) != key_crc) {
+ retval = EXT2_ET_UNDO_FILE_CORRUPT;
+ goto bad_key_replay;
+ }
+
+ /* load keys from key block */
+ lblk++;
+ max_j = data->num_keys - i;
+ if (max_j > keys_per_block)
+ max_j = keys_per_block;
+ for (j = 0, dkey = data->keyb->keys;
+ j < max_j;
+ j++, dkey++) {
+ blk64_t fsblk = ext2fs_le64_to_cpu(dkey->fsblk);
+ blk64_t undo_blk = fsblk * fs_blocksize / blocksize;
+ size_t size = ext2fs_le32_to_cpu(dkey->size);
+
+ ext2fs_mark_block_bitmap_range2(data->written_block_map,
+ undo_blk,
+ (size + blocksize - 1) / blocksize);
+ lblk += (size + blocksize - 1) / blocksize;
+ data->undo_blk_num = lblk;
+ data->keys_in_block = j + 1;
+ }
+ }
+ dbg_printf("Reopen undo, keyblk=%llu undoblk=%llu nrkeys=%zu kib=%zu\n",
+ data->key_blk_num, data->undo_blk_num, data->num_keys,
+ data->keys_in_block);
+
+ data->hdr.state = hdr.state & ~E2UNDO_STATE_FINISHED;
+ data->hdr.f_compat = hdr.f_compat;
+ data->hdr.f_incompat = hdr.f_incompat;
+ data->hdr.f_rocompat = hdr.f_rocompat;
+ return retval;
+
+bad_key_replay:
+ data->key_blk_num = data->undo_blk_num = 0;
+ data->keys_in_block = 0;
+ ext2fs_free_mem(&data->keyb);
+ ext2fs_free_generic_bitmap(data->written_block_map);
+ data->tdb_written = 0;
+ goto out;
+bad_file:
+ retval = EXT2_ET_UNDO_FILE_CORRUPT;
+out:
+ return retval;
+}
+
+static void undo_atexit(void *p)
+{
+ struct undo_private_data *data = p;
+ errcode_t err;
+
+ err = write_undo_indexes(data, 1);
+ io_channel_close(data->undo_file);
+
+ com_err(data->tdb_file, err, "while force-closing undo file");
+}
+
+static errcode_t undo_open(const char *name, int flags, io_channel *channel)
+{
+ io_channel io = NULL;
+ struct undo_private_data *data = NULL;
+ int undo_fd = -1;
+ errcode_t retval;
+
+ /* We don't support multi-threading, at least for now */
+ flags &= ~IO_FLAG_THREADS;
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+ 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;
+ retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
+ if (retval)
+ goto cleanup;
+
+ io->manager = undo_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;
+
+ memset(data, 0, sizeof(struct undo_private_data));
+ data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
+ data->super_blk_num = 1;
+ data->first_key_blk = 2;
+ data->undo_blk_num = 3;
+
+ if (undo_io_backing_manager) {
+ retval = undo_io_backing_manager->open(name, flags,
+ &data->real);
+ if (retval)
+ goto cleanup;
+
+ data->tdb_file = strdup(tdb_file);
+ if (data->tdb_file == NULL)
+ goto cleanup;
+ undo_fd = ext2fs_open_file(data->tdb_file, O_RDWR | O_CREAT,
+ 0600);
+ if (undo_fd < 0)
+ goto cleanup;
+
+ retval = undo_io_backing_manager->open(data->tdb_file,
+ IO_FLAG_RW,
+ &data->undo_file);
+ if (retval)
+ goto cleanup;
+ } else {
+ data->real = NULL;
+ data->undo_file = NULL;
+ }
+
+ if (data->real)
+ io->flags = (io->flags & ~CHANNEL_FLAGS_DISCARD_ZEROES) |
+ (data->real->flags & CHANNEL_FLAGS_DISCARD_ZEROES);
+
+ /*
+ * setup err handler for read so that we know
+ * when the backing manager fails do short read
+ */
+ if (data->real)
+ undo_err_handler_init(data->real);
+
+ if (data->undo_file) {
+ retval = try_reopen_undo_file(undo_fd, data);
+ if (retval)
+ goto cleanup;
+ }
+ retval = ext2fs_add_exit_fn(undo_atexit, data);
+ if (retval)
+ goto cleanup;
+
+ *channel = io;
+ if (undo_fd >= 0)
+ close(undo_fd);
+ return retval;
+
+cleanup:
+ ext2fs_remove_exit_fn(undo_atexit, data);
+ if (undo_fd >= 0)
+ close(undo_fd);
+ if (data && data->undo_file)
+ io_channel_close(data->undo_file);
+ if (data && data->tdb_file)
+ free(data->tdb_file);
+ if (data && data->real)
+ io_channel_close(data->real);
+ if (data)
+ ext2fs_free_mem(&data);
+ if (io && io->name)
+ ext2fs_free_mem(&io->name);
+ if (io)
+ ext2fs_free_mem(&io);
+ return retval;
+}
+
+static errcode_t undo_close(io_channel channel)
+{
+ struct undo_private_data *data;
+ errcode_t err, retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+ /* Before closing write the file system identity */
+ if (!getenv("UNDO_IO_SIMULATE_UNFINISHED"))
+ data->hdr.state = ext2fs_cpu_to_le32(E2UNDO_STATE_FINISHED);
+ err = write_undo_indexes(data, 1);
+ ext2fs_remove_exit_fn(undo_atexit, data);
+ if (data->real)
+ retval = io_channel_close(data->real);
+ if (data->tdb_file)
+ free(data->tdb_file);
+ if (data->undo_file)
+ io_channel_close(data->undo_file);
+ ext2fs_free_mem(&data->keyb);
+ if (data->written_block_map)
+ ext2fs_free_generic_bitmap(data->written_block_map);
+ ext2fs_free_mem(&channel->private_data);
+ if (channel->name)
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+
+ if (err)
+ return err;
+ return retval;
+}
+
+static errcode_t undo_set_blksize(io_channel channel, int blksize)
+{
+ struct undo_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (blksize > E2UNDO_MAX_BLOCK_SIZE || blksize < E2UNDO_MIN_BLOCK_SIZE)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ if (data->real)
+ retval = io_channel_set_blksize(data->real, blksize);
+ /*
+ * Set the block size used for tdb
+ */
+ if (!data->tdb_data_size || !data->tdb_written)
+ data->tdb_data_size = blksize;
+ channel->block_size = blksize;
+ return retval;
+}
+
+static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
+ int count, void *buf)
+{
+ errcode_t retval = 0;
+ struct undo_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_read_blk64(data->real, block, count, buf);
+
+ return retval;
+}
+
+static errcode_t undo_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ return undo_read_blk64(channel, block, count, buf);
+}
+
+static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
+ int count, const void *buf)
+{
+ struct undo_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+ /*
+ * First write the existing content into database
+ */
+ retval = undo_write_tdb(channel, block, count);
+ if (retval)
+ return retval;
+ if (data->real)
+ retval = io_channel_write_blk64(data->real, block, count, buf);
+
+ return retval;
+}
+
+static errcode_t undo_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ return undo_write_blk64(channel, block, count, buf);
+}
+
+static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *buf)
+{
+ struct undo_private_data *data;
+ errcode_t retval = 0;
+ ext2_loff_t location;
+ unsigned long blk_num, count;;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ location = offset + data->offset;
+ blk_num = location/channel->block_size;
+ /*
+ * the size specified may spread across multiple blocks
+ * also make sure we account for the fact that block start
+ * offset for tdb is different from the backing I/O manager
+ * due to possible different block size
+ */
+ count = (size + (location % channel->block_size) +
+ channel->block_size -1)/channel->block_size;
+ retval = undo_write_tdb(channel, blk_num, count);
+ if (retval)
+ return retval;
+ if (data->real && data->real->manager->write_byte)
+ retval = io_channel_write_byte(data->real, offset, size, buf);
+
+ return retval;
+}
+
+static errcode_t undo_discard(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct undo_private_data *data;
+ errcode_t retval = 0;
+ int icount;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (count > INT_MAX)
+ return EXT2_ET_UNIMPLEMENTED;
+ icount = count;
+
+ /*
+ * First write the existing content into database
+ */
+ retval = undo_write_tdb(channel, block, icount);
+ if (retval)
+ return retval;
+ if (data->real)
+ retval = io_channel_discard(data->real, block, count);
+
+ return retval;
+}
+
+static errcode_t undo_zeroout(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct undo_private_data *data;
+ errcode_t retval = 0;
+ int icount;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (count > INT_MAX)
+ return EXT2_ET_UNIMPLEMENTED;
+ icount = count;
+
+ /*
+ * First write the existing content into database
+ */
+ retval = undo_write_tdb(channel, block, icount);
+ if (retval)
+ return retval;
+ if (data->real)
+ retval = io_channel_zeroout(data->real, block, count);
+
+ return retval;
+}
+
+static errcode_t undo_cache_readahead(io_channel channel,
+ unsigned long long block,
+ unsigned long long count)
+{
+ struct undo_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_cache_readahead(data->real, block, count);
+
+ return retval;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t undo_flush(io_channel channel)
+{
+ errcode_t retval = 0;
+ struct undo_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (data->real)
+ retval = io_channel_flush(data->real);
+
+ return retval;
+}
+
+static errcode_t undo_set_option(io_channel channel, const char *option,
+ const char *arg)
+{
+ errcode_t retval = 0;
+ struct undo_private_data *data;
+ unsigned long tmp;
+ char *end;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (!strcmp(option, "tdb_data_size")) {
+ if (!arg)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ tmp = strtoul(arg, &end, 0);
+ if (*end)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (tmp > E2UNDO_MAX_BLOCK_SIZE || tmp < E2UNDO_MIN_BLOCK_SIZE)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (!data->tdb_data_size || !data->tdb_written) {
+ data->tdb_written = -1;
+ data->tdb_data_size = tmp;
+ }
+ return 0;
+ }
+ /*
+ * Need to support offset option to work with
+ * Unix I/O manager
+ */
+ if (data->real && data->real->manager->set_option) {
+ retval = data->real->manager->set_option(data->real,
+ option, arg);
+ }
+ if (!retval && !strcmp(option, "offset")) {
+ if (!arg)
+ return EXT2_ET_INVALID_ARGUMENT;
+
+ tmp = strtoul(arg, &end, 0);
+ if (*end)
+ return EXT2_ET_INVALID_ARGUMENT;
+ data->offset = tmp;
+ }
+ return retval;
+}
+
+static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
+{
+ errcode_t retval = 0;
+ struct undo_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct undo_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (data->real)
+ retval = (data->real->manager->get_stats)(data->real, stats);
+
+ return retval;
+}
+
+static struct struct_io_manager struct_undo_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Undo I/O Manager",
+ .open = undo_open,
+ .close = undo_close,
+ .set_blksize = undo_set_blksize,
+ .read_blk = undo_read_blk,
+ .write_blk = undo_write_blk,
+ .flush = undo_flush,
+ .write_byte = undo_write_byte,
+ .set_option = undo_set_option,
+ .get_stats = undo_get_stats,
+ .read_blk64 = undo_read_blk64,
+ .write_blk64 = undo_write_blk64,
+ .discard = undo_discard,
+ .zeroout = undo_zeroout,
+ .cache_readahead = undo_cache_readahead,
+};
+
+io_manager undo_io_manager = &struct_undo_manager;
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
new file mode 100644
index 0000000..3171c73
--- /dev/null
+++ b/lib/ext2fs/unix_io.c
@@ -0,0 +1,1510 @@
+/*
+ * unix_io.c --- This is the Unix (well, really POSIX) implementation
+ * of the I/O manager.
+ *
+ * Implements a one-block write-through cache.
+ *
+ * Includes support for Windows NT support under Cygwin.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002 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%
+ */
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
+#define _XOPEN_SOURCE 600
+#define _DARWIN_C_SOURCE
+#define _FILE_OFFSET_BITS 64
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#if HAVE_LINUX_FALLOC_H
+#include <linux/falloc.h>
+#endif
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKROGET)
+#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */
+#endif
+
+#undef ALIGN_DEBUG
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+
+struct unix_cache {
+ char *buf;
+ unsigned long long block;
+ int access_time;
+ unsigned dirty:1;
+ unsigned in_use:1;
+ unsigned write_err:1;
+};
+
+#define CACHE_SIZE 8
+#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */
+#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */
+
+struct unix_private_data {
+ int magic;
+ int dev;
+ int flags;
+ int align;
+ int access_time;
+ ext2_loff_t offset;
+ struct unix_cache cache[CACHE_SIZE];
+ void *bounce;
+ struct struct_io_stats io_stats;
+#ifdef HAVE_PTHREAD
+ pthread_mutex_t cache_mutex;
+ pthread_mutex_t bounce_mutex;
+ pthread_mutex_t stats_mutex;
+#endif
+};
+
+#define IS_ALIGNED(n, align) ((((uintptr_t) n) & \
+ ((uintptr_t) ((align)-1))) == 0)
+
+typedef enum lock_kind {
+ CACHE_MTX, BOUNCE_MTX, STATS_MTX
+} kind_t;
+
+#ifdef HAVE_PTHREAD
+static inline pthread_mutex_t *get_mutex(struct unix_private_data *data,
+ kind_t kind)
+{
+ if (data->flags & IO_FLAG_THREADS) {
+ switch (kind) {
+ case CACHE_MTX:
+ return &data->cache_mutex;
+ case BOUNCE_MTX:
+ return &data->bounce_mutex;
+ case STATS_MTX:
+ return &data->stats_mutex;
+ }
+ }
+ return NULL;
+}
+#endif
+
+static inline void mutex_lock(struct unix_private_data *data, kind_t kind)
+{
+#ifdef HAVE_PTHREAD
+ pthread_mutex_t *mtx = get_mutex(data,kind);
+
+ if (mtx)
+ pthread_mutex_lock(mtx);
+#endif
+}
+
+static inline void mutex_unlock(struct unix_private_data *data, kind_t kind)
+{
+#ifdef HAVE_PTHREAD
+ pthread_mutex_t *mtx = get_mutex(data,kind);
+
+ if (mtx)
+ pthread_mutex_unlock(mtx);
+#endif
+}
+
+static errcode_t unix_get_stats(io_channel channel, io_stats *stats)
+{
+ errcode_t retval = 0;
+
+ struct unix_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (stats) {
+ mutex_lock(data, STATS_MTX);
+ *stats = &data->io_stats;
+ mutex_unlock(data, STATS_MTX);
+ }
+
+ return retval;
+}
+
+static char *safe_getenv(const char *arg)
+{
+ if ((getuid() != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#ifdef HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
+
+/*
+ * Here are the raw I/O functions
+ */
+static errcode_t raw_read_blk(io_channel channel,
+ struct unix_private_data *data,
+ unsigned long long block,
+ int count, void *bufv)
+{
+ errcode_t retval;
+ ssize_t size;
+ ext2_loff_t location;
+ int actual = 0;
+ unsigned char *buf = bufv;
+ ssize_t really_read = 0;
+ unsigned long long aligned_blk;
+ int align_size, offset;
+
+ size = (count < 0) ? -count : (ext2_loff_t) count * channel->block_size;
+ mutex_lock(data, STATS_MTX);
+ data->io_stats.bytes_read += size;
+ mutex_unlock(data, STATS_MTX);
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+ if (data->flags & IO_FLAG_FORCE_BOUNCE)
+ goto bounce_read;
+
+#ifdef HAVE_PREAD64
+ /* Try an aligned pread */
+ if ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(location, channel->align) &&
+ IS_ALIGNED(size, channel->align))) {
+ actual = pread64(data->dev, buf, size, location);
+ if (actual == size)
+ return 0;
+ actual = 0;
+ }
+#elif HAVE_PREAD
+ /* Try an aligned pread */
+ if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
+ ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(location, channel->align) &&
+ IS_ALIGNED(size, channel->align)))) {
+ actual = pread(data->dev, buf, size, location);
+ if (actual == size)
+ return 0;
+ actual = 0;
+ }
+#endif /* HAVE_PREAD */
+
+ if ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(location, channel->align) &&
+ IS_ALIGNED(size, channel->align))) {
+ mutex_lock(data, BOUNCE_MTX);
+ if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_unlock;
+ }
+ actual = read(data->dev, buf, size);
+ if (actual != size) {
+ short_read:
+ if (actual < 0) {
+ retval = errno;
+ actual = 0;
+ } else
+ retval = EXT2_ET_SHORT_READ;
+ goto error_unlock;
+ }
+ goto success_unlock;
+ }
+
+#ifdef ALIGN_DEBUG
+ printf("raw_read_blk: O_DIRECT fallback: %p %lu\n", buf,
+ (unsigned long) size);
+#endif
+
+ /*
+ * The buffer or size which we're trying to read isn't aligned
+ * to the O_DIRECT rules, so we need to do this the hard way...
+ */
+bounce_read:
+ if (channel->align == 0)
+ channel->align = 1;
+ if ((channel->block_size > channel->align) &&
+ (channel->block_size % channel->align) == 0)
+ align_size = channel->block_size;
+ else
+ align_size = channel->align;
+ aligned_blk = location / align_size;
+ offset = location % align_size;
+
+ mutex_lock(data, BOUNCE_MTX);
+ if (ext2fs_llseek(data->dev, aligned_blk * align_size, SEEK_SET) < 0) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_unlock;
+ }
+ while (size > 0) {
+ actual = read(data->dev, data->bounce, align_size);
+ if (actual != align_size) {
+ actual = really_read;
+ buf -= really_read;
+ size += really_read;
+ goto short_read;
+ }
+ if ((actual + offset) > align_size)
+ actual = align_size - offset;
+ if (actual > size)
+ actual = size;
+ memcpy(buf, (char *)data->bounce + offset, actual);
+
+ really_read += actual;
+ size -= actual;
+ buf += actual;
+ offset = 0;
+ aligned_blk++;
+ }
+success_unlock:
+ mutex_unlock(data, BOUNCE_MTX);
+ return 0;
+
+error_unlock:
+ mutex_unlock(data, BOUNCE_MTX);
+ if (actual >= 0 && actual < size)
+ memset((char *) buf+actual, 0, size-actual);
+ if (channel->read_error)
+ retval = (channel->read_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+
+#define RAW_WRITE_NO_HANDLER 1
+
+static errcode_t raw_write_blk(io_channel channel,
+ struct unix_private_data *data,
+ unsigned long long block,
+ int count, const void *bufv,
+ int flags)
+{
+ ssize_t size;
+ ext2_loff_t location;
+ int actual = 0;
+ errcode_t retval;
+ const unsigned char *buf = bufv;
+ unsigned long long aligned_blk;
+ int align_size, offset;
+
+ if (count == 1)
+ size = channel->block_size;
+ else {
+ if (count < 0)
+ size = -count;
+ else
+ size = (ext2_loff_t) count * channel->block_size;
+ }
+ mutex_lock(data, STATS_MTX);
+ data->io_stats.bytes_written += size;
+ mutex_unlock(data, STATS_MTX);
+
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+ if (data->flags & IO_FLAG_FORCE_BOUNCE)
+ goto bounce_write;
+
+#ifdef HAVE_PWRITE64
+ /* Try an aligned pwrite */
+ if ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(location, channel->align) &&
+ IS_ALIGNED(size, channel->align))) {
+ actual = pwrite64(data->dev, buf, size, location);
+ if (actual == size)
+ return 0;
+ }
+#elif HAVE_PWRITE
+ /* Try an aligned pwrite */
+ if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
+ ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(location, channel->align) &&
+ IS_ALIGNED(size, channel->align)))) {
+ actual = pwrite(data->dev, buf, size, location);
+ if (actual == size)
+ return 0;
+ }
+#endif /* HAVE_PWRITE */
+
+ if ((channel->align == 0) ||
+ (IS_ALIGNED(buf, channel->align) &&
+ IS_ALIGNED(location, channel->align) &&
+ IS_ALIGNED(size, channel->align))) {
+ mutex_lock(data, BOUNCE_MTX);
+ if (ext2fs_llseek(data->dev, location, SEEK_SET) < 0) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_unlock;
+ }
+ actual = write(data->dev, buf, size);
+ mutex_unlock(data, BOUNCE_MTX);
+ if (actual < 0) {
+ retval = errno;
+ goto error_out;
+ }
+ if (actual != size) {
+ short_write:
+ retval = EXT2_ET_SHORT_WRITE;
+ goto error_out;
+ }
+ return 0;
+ }
+
+#ifdef ALIGN_DEBUG
+ printf("raw_write_blk: O_DIRECT fallback: %p %lu\n", buf,
+ (unsigned long) size);
+#endif
+ /*
+ * The buffer or size which we're trying to write isn't aligned
+ * to the O_DIRECT rules, so we need to do this the hard way...
+ */
+bounce_write:
+ if (channel->align == 0)
+ channel->align = 1;
+ if ((channel->block_size > channel->align) &&
+ (channel->block_size % channel->align) == 0)
+ align_size = channel->block_size;
+ else
+ align_size = channel->align;
+ aligned_blk = location / align_size;
+ offset = location % align_size;
+
+ while (size > 0) {
+ int actual_w;
+
+ mutex_lock(data, BOUNCE_MTX);
+ if (size < align_size || offset) {
+ if (ext2fs_llseek(data->dev, aligned_blk * align_size,
+ SEEK_SET) < 0) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_unlock;
+ }
+ actual = read(data->dev, data->bounce,
+ align_size);
+ if (actual != align_size) {
+ if (actual < 0) {
+ retval = errno;
+ goto error_unlock;
+ }
+ memset((char *) data->bounce + actual, 0,
+ align_size - actual);
+ }
+ }
+ actual = size;
+ if ((actual + offset) > align_size)
+ actual = align_size - offset;
+ if (actual > size)
+ actual = size;
+ memcpy(((char *)data->bounce) + offset, buf, actual);
+ if (ext2fs_llseek(data->dev, aligned_blk * align_size, SEEK_SET) < 0) {
+ retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+ goto error_unlock;
+ }
+ actual_w = write(data->dev, data->bounce, align_size);
+ mutex_unlock(data, BOUNCE_MTX);
+ if (actual_w < 0) {
+ retval = errno;
+ goto error_out;
+ }
+ if (actual_w != align_size)
+ goto short_write;
+ size -= actual;
+ buf += actual;
+ location += actual;
+ aligned_blk++;
+ offset = 0;
+ }
+ return 0;
+
+error_unlock:
+ mutex_unlock(data, BOUNCE_MTX);
+error_out:
+ if (((flags & RAW_WRITE_NO_HANDLER) == 0) && channel->write_error)
+ retval = (channel->write_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+
+
+/*
+ * Here we implement the cache functions
+ */
+
+/* Allocate the cache buffers */
+static errcode_t alloc_cache(io_channel channel,
+ struct unix_private_data *data)
+{
+ errcode_t retval;
+ struct unix_cache *cache;
+ int i;
+
+ data->access_time = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ cache->block = 0;
+ cache->access_time = 0;
+ cache->dirty = 0;
+ cache->in_use = 0;
+ if (cache->buf)
+ ext2fs_free_mem(&cache->buf);
+ retval = io_channel_alloc_buf(channel, 0, &cache->buf);
+ if (retval)
+ return retval;
+ }
+ if (channel->align || data->flags & IO_FLAG_FORCE_BOUNCE) {
+ if (data->bounce)
+ ext2fs_free_mem(&data->bounce);
+ retval = io_channel_alloc_buf(channel, 0, &data->bounce);
+ }
+ return retval;
+}
+
+/* Free the cache buffers */
+static void free_cache(struct unix_private_data *data)
+{
+ struct unix_cache *cache;
+ int i;
+
+ data->access_time = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ cache->block = 0;
+ cache->access_time = 0;
+ cache->dirty = 0;
+ cache->in_use = 0;
+ if (cache->buf)
+ ext2fs_free_mem(&cache->buf);
+ }
+ if (data->bounce)
+ ext2fs_free_mem(&data->bounce);
+}
+
+#ifndef NO_IO_CACHE
+/*
+ * Try to find a block in the cache. If the block is not found, and
+ * eldest is a non-zero pointer, then fill in eldest with the cache
+ * entry to that should be reused.
+ */
+static struct unix_cache *find_cached_block(struct unix_private_data *data,
+ unsigned long long block,
+ struct unix_cache **eldest)
+{
+ struct unix_cache *cache, *unused_cache, *oldest_cache;
+ int i;
+
+ unused_cache = oldest_cache = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use) {
+ if (!unused_cache)
+ unused_cache = cache;
+ continue;
+ }
+ if (cache->block == block) {
+ cache->access_time = ++data->access_time;
+ return cache;
+ }
+ if (!oldest_cache ||
+ (cache->access_time < oldest_cache->access_time))
+ oldest_cache = cache;
+ }
+ if (eldest)
+ *eldest = (unused_cache) ? unused_cache : oldest_cache;
+ return 0;
+}
+
+/*
+ * Reuse a particular cache entry for another block.
+ */
+static errcode_t reuse_cache(io_channel channel,
+ struct unix_private_data *data, struct unix_cache *cache,
+ unsigned long long block)
+{
+ if (cache->dirty && cache->in_use) {
+ errcode_t retval;
+
+ retval = raw_write_blk(channel, data, cache->block, 1,
+ cache->buf, RAW_WRITE_NO_HANDLER);
+ if (retval) {
+ cache->write_err = 1;
+ return retval;
+ }
+ }
+
+ cache->in_use = 1;
+ cache->dirty = 0;
+ cache->write_err = 0;
+ cache->block = block;
+ cache->access_time = ++data->access_time;
+ return 0;
+}
+
+#define FLUSH_INVALIDATE 0x01
+#define FLUSH_NOLOCK 0x02
+
+/*
+ * Flush all of the blocks in the cache
+ */
+static errcode_t flush_cached_blocks(io_channel channel,
+ struct unix_private_data *data,
+ int flags)
+{
+ struct unix_cache *cache;
+ errcode_t retval, retval2 = 0;
+ int i;
+ int errors_found = 0;
+
+ if ((flags & FLUSH_NOLOCK) == 0)
+ mutex_lock(data, CACHE_MTX);
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use || !cache->dirty)
+ continue;
+ retval = raw_write_blk(channel, data,
+ cache->block, 1, cache->buf,
+ RAW_WRITE_NO_HANDLER);
+ if (retval) {
+ cache->write_err = 1;
+ errors_found = 1;
+ retval2 = retval;
+ } else {
+ cache->dirty = 0;
+ cache->write_err = 0;
+ if (flags & FLUSH_INVALIDATE)
+ cache->in_use = 0;
+ }
+ }
+ if ((flags & FLUSH_NOLOCK) == 0)
+ mutex_unlock(data, CACHE_MTX);
+retry:
+ while (errors_found) {
+ if ((flags & FLUSH_NOLOCK) == 0)
+ mutex_lock(data, CACHE_MTX);
+ errors_found = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use || !cache->write_err)
+ continue;
+ errors_found = 1;
+ if (cache->write_err && channel->write_error) {
+ char *err_buf = NULL;
+ unsigned long long err_block = cache->block;
+
+ cache->dirty = 0;
+ cache->in_use = 0;
+ cache->write_err = 0;
+ if (io_channel_alloc_buf(channel, 0,
+ &err_buf))
+ err_buf = NULL;
+ else
+ memcpy(err_buf, cache->buf,
+ channel->block_size);
+ mutex_unlock(data, CACHE_MTX);
+ (channel->write_error)(channel, err_block,
+ 1, err_buf, channel->block_size, -1,
+ retval2);
+ if (err_buf)
+ ext2fs_free_mem(&err_buf);
+ goto retry;
+ } else
+ cache->write_err = 0;
+ }
+ if ((flags & FLUSH_NOLOCK) == 0)
+ mutex_unlock(data, CACHE_MTX);
+ }
+ return retval2;
+}
+#endif /* NO_IO_CACHE */
+
+#ifdef __linux__
+#ifndef BLKDISCARDZEROES
+#define BLKDISCARDZEROES _IO(0x12,124)
+#endif
+#endif
+
+int ext2fs_open_file(const char *pathname, int flags, mode_t mode)
+{
+ if (mode)
+#if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return open64(pathname, flags, mode);
+ else
+ return open64(pathname, flags);
+#else
+ return open(pathname, flags, mode);
+ else
+ return open(pathname, flags);
+#endif
+}
+
+int ext2fs_stat(const char *path, ext2fs_struct_stat *buf)
+{
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return stat64(path, buf);
+#else
+ return stat(path, buf);
+#endif
+}
+
+int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
+{
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return fstat64(fd, buf);
+#else
+ return fstat(fd, buf);
+#endif
+}
+
+
+static errcode_t unix_open_channel(const char *name, int fd,
+ int flags, io_channel *channel,
+ io_manager io_mgr)
+{
+ io_channel io = NULL;
+ struct unix_private_data *data = NULL;
+ errcode_t retval;
+ ext2fs_struct_stat st;
+#ifdef __linux__
+ struct utsname ut;
+#endif
+
+ if (safe_getenv("UNIX_IO_FORCE_BOUNCE"))
+ flags |= IO_FLAG_FORCE_BOUNCE;
+
+#ifdef __linux__
+ /*
+ * We need to make sure any previous errors in the block
+ * device are thrown away, sigh.
+ */
+ (void) fsync(fd);
+#endif
+
+ 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;
+ retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
+ if (retval)
+ goto cleanup;
+
+ io->manager = io_mgr;
+ 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;
+ io->flags = 0;
+
+ memset(data, 0, sizeof(struct unix_private_data));
+ data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
+ data->io_stats.num_fields = 2;
+ data->flags = flags;
+ data->dev = fd;
+
+#if defined(O_DIRECT)
+ if (flags & IO_FLAG_DIRECT_IO)
+ io->align = ext2fs_get_dio_alignment(data->dev);
+#elif defined(F_NOCACHE)
+ if (flags & IO_FLAG_DIRECT_IO)
+ io->align = 4096;
+#endif
+
+ /*
+ * If the device is really a block device, then set the
+ * appropriate flag, otherwise we can set DISCARD_ZEROES flag
+ * because we are going to use punch hole instead of discard
+ * and if it succeed, subsequent read from sparse area returns
+ * zero.
+ */
+ if (ext2fs_fstat(data->dev, &st) == 0) {
+ if (ext2fsP_is_disk_device(st.st_mode))
+ io->flags |= CHANNEL_FLAGS_BLOCK_DEVICE;
+ else
+ io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+ }
+
+#ifdef BLKDISCARDZEROES
+ {
+ int zeroes = 0;
+ if (ioctl(data->dev, BLKDISCARDZEROES, &zeroes) == 0 &&
+ zeroes)
+ io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+ }
+#endif
+
+#if defined(__CYGWIN__)
+ /*
+ * Some operating systems require that the buffers be aligned,
+ * regardless of O_DIRECT
+ */
+ if (!io->align)
+ io->align = 512;
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (io->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
+ int dio_align = ext2fs_get_dio_alignment(fd);
+
+ if (io->align < dio_align)
+ io->align = dio_align;
+ }
+#endif
+
+ if ((retval = alloc_cache(io, data)))
+ goto cleanup;
+
+#ifdef BLKROGET
+ if (flags & IO_FLAG_RW) {
+ int error;
+ int readonly = 0;
+
+ /* Is the block device actually writable? */
+ error = ioctl(data->dev, BLKROGET, &readonly);
+ if (!error && readonly) {
+ retval = EPERM;
+ goto cleanup;
+ }
+ }
+#endif
+
+#ifdef __linux__
+#undef RLIM_INFINITY
+#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
+#define RLIM_INFINITY ((unsigned long)(~0UL>>1))
+#else
+#define RLIM_INFINITY (~0UL)
+#endif
+ /*
+ * Work around a bug in 2.4.10-2.4.18 kernels where writes to
+ * block devices are wrongly getting hit by the filesize
+ * limit. This workaround isn't perfect, since it won't work
+ * if glibc wasn't built against 2.2 header files. (Sigh.)
+ *
+ */
+ if ((flags & IO_FLAG_RW) &&
+ (uname(&ut) == 0) &&
+ ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+ (ut.release[2] == '4') && (ut.release[3] == '.') &&
+ (ut.release[4] == '1') && (ut.release[5] >= '0') &&
+ (ut.release[5] < '8')) &&
+ (ext2fs_fstat(data->dev, &st) == 0) &&
+ (ext2fsP_is_disk_device(st.st_mode))) {
+ struct rlimit rlim;
+
+ rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
+ setrlimit(RLIMIT_FSIZE, &rlim);
+ getrlimit(RLIMIT_FSIZE, &rlim);
+ if (((unsigned long) rlim.rlim_cur) <
+ ((unsigned long) rlim.rlim_max)) {
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit(RLIMIT_FSIZE, &rlim);
+ }
+ }
+#endif
+#ifdef HAVE_PTHREAD
+ if (flags & IO_FLAG_THREADS) {
+ io->flags |= CHANNEL_FLAGS_THREADS;
+ retval = pthread_mutex_init(&data->cache_mutex, NULL);
+ if (retval)
+ goto cleanup;
+ retval = pthread_mutex_init(&data->bounce_mutex, NULL);
+ if (retval) {
+ pthread_mutex_destroy(&data->cache_mutex);
+ goto cleanup;
+ }
+ retval = pthread_mutex_init(&data->stats_mutex, NULL);
+ if (retval) {
+ pthread_mutex_destroy(&data->cache_mutex);
+ pthread_mutex_destroy(&data->bounce_mutex);
+ goto cleanup;
+ }
+ }
+#endif
+ *channel = io;
+ return 0;
+
+cleanup:
+ if (data) {
+ if (data->dev >= 0)
+ close(data->dev);
+ free_cache(data);
+ ext2fs_free_mem(&data);
+ }
+ if (io) {
+ if (io->name) {
+ ext2fs_free_mem(&io->name);
+ }
+ ext2fs_free_mem(&io);
+ }
+ return retval;
+}
+
+static errcode_t unixfd_open(const char *str_fd, int flags,
+ io_channel *channel)
+{
+ int fd;
+ int fd_flags;
+
+ fd = atoi(str_fd);
+#if defined(HAVE_FCNTL)
+ fd_flags = fcntl(fd, F_GETFD);
+ if (fd_flags == -1)
+ return EBADF;
+
+ flags = 0;
+ if (fd_flags & O_RDWR)
+ flags |= IO_FLAG_RW;
+ if (fd_flags & O_EXCL)
+ flags |= IO_FLAG_EXCLUSIVE;
+#if defined(O_DIRECT)
+ if (fd_flags & O_DIRECT)
+ flags |= IO_FLAG_DIRECT_IO;
+#endif
+#endif /* HAVE_FCNTL */
+
+ return unix_open_channel(str_fd, fd, flags, channel, unixfd_io_manager);
+}
+
+static errcode_t unix_open(const char *name, int flags,
+ io_channel *channel)
+{
+ int fd = -1;
+ int open_flags;
+
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+
+ open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
+ if (flags & IO_FLAG_EXCLUSIVE)
+ open_flags |= O_EXCL;
+#if defined(O_DIRECT)
+ if (flags & IO_FLAG_DIRECT_IO)
+ open_flags |= O_DIRECT;
+#endif
+ fd = ext2fs_open_file(name, open_flags, 0);
+ if (fd < 0)
+ return errno;
+#if defined(F_NOCACHE) && !defined(IO_DIRECT)
+ if (flags & IO_FLAG_DIRECT_IO) {
+ if (fcntl(fd, F_NOCACHE, 1) < 0)
+ return errno;
+ }
+#endif
+ return unix_open_channel(name, fd, flags, channel, unix_io_manager);
+}
+
+static errcode_t unix_close(io_channel channel)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+
+#ifndef NO_IO_CACHE
+ retval = flush_cached_blocks(channel, data, 0);
+#endif
+
+ if (close(data->dev) < 0)
+ retval = errno;
+ free_cache(data);
+#ifdef HAVE_PTHREAD
+ if (data->flags & IO_FLAG_THREADS) {
+ pthread_mutex_destroy(&data->cache_mutex);
+ pthread_mutex_destroy(&data->bounce_mutex);
+ pthread_mutex_destroy(&data->stats_mutex);
+ }
+#endif
+
+ ext2fs_free_mem(&channel->private_data);
+ if (channel->name)
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+ return retval;
+}
+
+static errcode_t unix_set_blksize(io_channel channel, int blksize)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (channel->block_size != blksize) {
+ mutex_lock(data, CACHE_MTX);
+ mutex_lock(data, BOUNCE_MTX);
+#ifndef NO_IO_CACHE
+ if ((retval = flush_cached_blocks(channel, data, FLUSH_NOLOCK))){
+ mutex_unlock(data, BOUNCE_MTX);
+ mutex_unlock(data, CACHE_MTX);
+ return retval;
+ }
+#endif
+
+ channel->block_size = blksize;
+ free_cache(data);
+ retval = alloc_cache(channel, data);
+ mutex_unlock(data, BOUNCE_MTX);
+ mutex_unlock(data, CACHE_MTX);
+ }
+ return retval;
+}
+
+static errcode_t unix_read_blk64(io_channel channel, unsigned long long block,
+ int count, void *buf)
+{
+ struct unix_private_data *data;
+ struct unix_cache *cache;
+ errcode_t retval;
+ char *cp;
+ int i, j;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+ return raw_read_blk(channel, data, block, count, buf);
+#else
+ if (data->flags & IO_FLAG_NOCACHE)
+ return raw_read_blk(channel, data, block, count, buf);
+ /*
+ * If we're doing an odd-sized read or a very large read,
+ * flush out the cache and then do a direct read.
+ */
+ if (count < 0 || count > WRITE_DIRECT_SIZE) {
+ if ((retval = flush_cached_blocks(channel, data, 0)))
+ return retval;
+ return raw_read_blk(channel, data, block, count, buf);
+ }
+
+ cp = buf;
+ mutex_lock(data, CACHE_MTX);
+ while (count > 0) {
+ /* If it's in the cache, use it! */
+ if ((cache = find_cached_block(data, block, NULL))) {
+#ifdef DEBUG
+ printf("Using cached block %lu\n", block);
+#endif
+ memcpy(cp, cache->buf, channel->block_size);
+ count--;
+ block++;
+ cp += channel->block_size;
+ continue;
+ }
+
+ /*
+ * Find the number of uncached blocks so we can do a
+ * single read request
+ */
+ for (i=1; i < count; i++)
+ if (find_cached_block(data, block+i, NULL))
+ break;
+#ifdef DEBUG
+ printf("Reading %d blocks starting at %lu\n", i, block);
+#endif
+ mutex_unlock(data, CACHE_MTX);
+ if ((retval = raw_read_blk(channel, data, block, i, cp)))
+ return retval;
+ mutex_lock(data, CACHE_MTX);
+
+ /* Save the results in the cache */
+ for (j=0; j < i; j++) {
+ if (!find_cached_block(data, block, &cache)) {
+ retval = reuse_cache(channel, data,
+ cache, block);
+ if (retval)
+ goto call_write_handler;
+ memcpy(cache->buf, cp, channel->block_size);
+ }
+ count--;
+ block++;
+ cp += channel->block_size;
+ }
+ }
+ mutex_unlock(data, CACHE_MTX);
+ return 0;
+
+call_write_handler:
+ if (cache->write_err && channel->write_error) {
+ char *err_buf = NULL;
+ unsigned long long err_block = cache->block;
+
+ cache->dirty = 0;
+ cache->in_use = 0;
+ cache->write_err = 0;
+ if (io_channel_alloc_buf(channel, 0, &err_buf))
+ err_buf = NULL;
+ else
+ memcpy(err_buf, cache->buf, channel->block_size);
+ mutex_unlock(data, CACHE_MTX);
+ (channel->write_error)(channel, err_block, 1, err_buf,
+ channel->block_size, -1,
+ retval);
+ if (err_buf)
+ ext2fs_free_mem(&err_buf);
+ } else
+ mutex_unlock(data, CACHE_MTX);
+ return retval;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ return unix_read_blk64(channel, block, count, buf);
+}
+
+static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
+ int count, const void *buf)
+{
+ struct unix_private_data *data;
+ struct unix_cache *cache, *reuse;
+ errcode_t retval = 0;
+ const char *cp;
+ int writethrough;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+ return raw_write_blk(channel, data, block, count, buf, 0);
+#else
+ if (data->flags & IO_FLAG_NOCACHE)
+ return raw_write_blk(channel, data, block, count, buf, 0);
+ /*
+ * If we're doing an odd-sized write or a very large write,
+ * flush out the cache completely and then do a direct write.
+ */
+ if (count < 0 || count > WRITE_DIRECT_SIZE) {
+ if ((retval = flush_cached_blocks(channel, data,
+ FLUSH_INVALIDATE)))
+ return retval;
+ return raw_write_blk(channel, data, block, count, buf, 0);
+ }
+
+ /*
+ * For a moderate-sized multi-block write, first force a write
+ * if we're in write-through cache mode, and then fill the
+ * cache with the blocks.
+ */
+ writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
+ if (writethrough)
+ retval = raw_write_blk(channel, data, block, count, buf, 0);
+
+ cp = buf;
+ mutex_lock(data, CACHE_MTX);
+ while (count > 0) {
+ cache = find_cached_block(data, block, &reuse);
+ if (!cache) {
+ errcode_t err;
+
+ cache = reuse;
+ err = reuse_cache(channel, data, cache, block);
+ if (err)
+ goto call_write_handler;
+ }
+ if (cache->buf != cp)
+ memcpy(cache->buf, cp, channel->block_size);
+ cache->dirty = !writethrough;
+ count--;
+ block++;
+ cp += channel->block_size;
+ }
+ mutex_unlock(data, CACHE_MTX);
+ return retval;
+
+call_write_handler:
+ if (cache->write_err && channel->write_error) {
+ char *err_buf = NULL;
+ unsigned long long err_block = cache->block;
+
+ cache->dirty = 0;
+ cache->in_use = 0;
+ cache->write_err = 0;
+ if (io_channel_alloc_buf(channel, 0, &err_buf))
+ err_buf = NULL;
+ else
+ memcpy(err_buf, cache->buf, channel->block_size);
+ mutex_unlock(data, CACHE_MTX);
+ (channel->write_error)(channel, err_block, 1, err_buf,
+ channel->block_size, -1,
+ retval);
+ if (err_buf)
+ ext2fs_free_mem(&err_buf);
+ } else
+ mutex_unlock(data, CACHE_MTX);
+ return retval;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_cache_readahead(io_channel channel,
+ unsigned long long block,
+ unsigned long long count)
+{
+#ifdef POSIX_FADV_WILLNEED
+ struct unix_private_data *data;
+
+ data = (struct unix_private_data *)channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+ return posix_fadvise(data->dev,
+ (ext2_loff_t)block * channel->block_size + data->offset,
+ (ext2_loff_t)count * channel->block_size,
+ POSIX_FADV_WILLNEED);
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ return unix_write_blk64(channel, block, count, buf);
+}
+
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *buf)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+ ssize_t actual;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (channel->align != 0) {
+#ifdef ALIGN_DEBUG
+ printf("unix_write_byte: O_DIRECT fallback\n");
+#endif
+ return EXT2_ET_UNIMPLEMENTED;
+ }
+
+#ifndef NO_IO_CACHE
+ /*
+ * Flush out the cache completely
+ */
+ if ((retval = flush_cached_blocks(channel, data, FLUSH_INVALIDATE)))
+ return retval;
+#endif
+
+ if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
+ return errno;
+
+ actual = write(data->dev, buf, size);
+ if (actual < 0)
+ return errno;
+ if (actual != size)
+ return EXT2_ET_SHORT_WRITE;
+
+ return 0;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t unix_flush(io_channel channel)
+{
+ struct unix_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+ retval = flush_cached_blocks(channel, data, 0);
+#endif
+#ifdef HAVE_FSYNC
+ if (!retval && fsync(data->dev) != 0)
+ return errno;
+#endif
+ return retval;
+}
+
+static errcode_t unix_set_option(io_channel channel, const char *option,
+ const char *arg)
+{
+ struct unix_private_data *data;
+ unsigned long long tmp;
+ errcode_t retval;
+ char *end;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_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;
+ data->offset = tmp;
+ if (data->offset < 0)
+ return EXT2_ET_INVALID_ARGUMENT;
+ return 0;
+ }
+ if (!strcmp(option, "cache")) {
+ if (!arg)
+ return EXT2_ET_INVALID_ARGUMENT;
+ if (!strcmp(arg, "on")) {
+ data->flags &= ~IO_FLAG_NOCACHE;
+ return 0;
+ }
+ if (!strcmp(arg, "off")) {
+ retval = flush_cached_blocks(channel, data, 0);
+ data->flags |= IO_FLAG_NOCACHE;
+ return retval;
+ }
+ return EXT2_ET_INVALID_ARGUMENT;
+ }
+ return EXT2_ET_INVALID_ARGUMENT;
+}
+
+#if defined(__linux__) && !defined(BLKDISCARD)
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+static errcode_t unix_discard(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct unix_private_data *data;
+ int ret;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
+#ifdef BLKDISCARD
+ __u64 range[2];
+
+ range[0] = (__u64)(block) * channel->block_size + data->offset;
+ range[1] = (__u64)(count) * channel->block_size;
+
+ ret = ioctl(data->dev, BLKDISCARD, &range);
+#else
+ goto unimplemented;
+#endif
+ } else {
+#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
+ /*
+ * If we are not on block device, try to use punch hole
+ * to reclaim free space.
+ */
+ ret = fallocate(data->dev,
+ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ (off_t)(block) * channel->block_size + data->offset,
+ (off_t)(count) * channel->block_size);
+#else
+ goto unimplemented;
+#endif
+ }
+ if (ret < 0) {
+ if (errno == EOPNOTSUPP)
+ goto unimplemented;
+ return errno;
+ }
+ return 0;
+unimplemented:
+ return EXT2_ET_UNIMPLEMENTED;
+}
+
+/*
+ * If we know about ZERO_RANGE, try that before we try PUNCH_HOLE because
+ * ZERO_RANGE doesn't unmap preallocated blocks. We prefer fallocate because
+ * it always invalidates page cache, and libext2fs requires that reads after
+ * ZERO_RANGE return zeroes.
+ */
+static int __unix_zeroout(int fd, off_t offset, off_t len)
+{
+ int ret = -1;
+
+#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_ZERO_RANGE)
+ ret = fallocate(fd, FALLOC_FL_ZERO_RANGE, offset, len);
+ if (ret == 0)
+ return 0;
+#endif
+#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
+ ret = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ offset, len);
+ if (ret == 0)
+ return 0;
+#endif
+ errno = EOPNOTSUPP;
+ return ret;
+}
+
+/* parameters might not be used if OS doesn't support zeroout */
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+static errcode_t unix_zeroout(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct unix_private_data *data;
+ int ret;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct unix_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+ if (safe_getenv("UNIX_IO_NOZEROOUT"))
+ goto unimplemented;
+
+ if (!(channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE)) {
+ /* Regular file, try to use truncate/punch/zero. */
+ struct stat statbuf;
+
+ if (count == 0)
+ return 0;
+ /*
+ * If we're trying to zero a range past the end of the file,
+ * extend the file size, then truncate everything.
+ */
+ ret = fstat(data->dev, &statbuf);
+ if (ret)
+ goto err;
+ if ((unsigned long long) statbuf.st_size <
+ (block + count) * channel->block_size + data->offset) {
+ ret = ftruncate(data->dev,
+ (block + count) * channel->block_size + data->offset);
+ if (ret)
+ goto err;
+ }
+ }
+
+ ret = __unix_zeroout(data->dev,
+ (off_t)(block) * channel->block_size + data->offset,
+ (off_t)(count) * channel->block_size);
+err:
+ if (ret < 0) {
+ if (errno == EOPNOTSUPP)
+ goto unimplemented;
+ return errno;
+ }
+ return 0;
+unimplemented:
+ return EXT2_ET_UNIMPLEMENTED;
+}
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic pop
+#endif
+
+static struct struct_io_manager struct_unix_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Unix I/O Manager",
+ .open = unix_open,
+ .close = unix_close,
+ .set_blksize = unix_set_blksize,
+ .read_blk = unix_read_blk,
+ .write_blk = unix_write_blk,
+ .flush = unix_flush,
+ .write_byte = unix_write_byte,
+ .set_option = unix_set_option,
+ .get_stats = unix_get_stats,
+ .read_blk64 = unix_read_blk64,
+ .write_blk64 = unix_write_blk64,
+ .discard = unix_discard,
+ .cache_readahead = unix_cache_readahead,
+ .zeroout = unix_zeroout,
+};
+
+io_manager unix_io_manager = &struct_unix_manager;
+
+static struct struct_io_manager struct_unixfd_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Unix fd I/O Manager",
+ .open = unixfd_open,
+ .close = unix_close,
+ .set_blksize = unix_set_blksize,
+ .read_blk = unix_read_blk,
+ .write_blk = unix_write_blk,
+ .flush = unix_flush,
+ .write_byte = unix_write_byte,
+ .set_option = unix_set_option,
+ .get_stats = unix_get_stats,
+ .read_blk64 = unix_read_blk64,
+ .write_blk64 = unix_write_blk64,
+ .discard = unix_discard,
+ .cache_readahead = unix_cache_readahead,
+ .zeroout = unix_zeroout,
+};
+
+io_manager unixfd_io_manager = &struct_unixfd_manager;
diff --git a/lib/ext2fs/unlink.c b/lib/ext2fs/unlink.c
new file mode 100644
index 0000000..3ec04cf
--- /dev/null
+++ b/lib/ext2fs/unlink.c
@@ -0,0 +1,100 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#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 (ext2fs_dirent_name_len(dirent) != ls->namelen)
+ return 0;
+ if (strncmp(ls->name, dirent->name, ext2fs_dirent_name_len(dirent)))
+ return 0;
+ }
+ if (!(ls->flags & EXT2FS_UNLINK_FORCE) && 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)
+{
+ 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 = flags;
+ 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/ext2fs/utf8data.h b/lib/ext2fs/utf8data.h
new file mode 100644
index 0000000..76e4f0e
--- /dev/null
+++ b/lib/ext2fs/utf8data.h
@@ -0,0 +1,4109 @@
+/* This file is generated code, do not edit. */
+#ifndef __INCLUDED_FROM_UTF8NORM_C__
+#error Only nls_utf8-norm.c should include this file.
+#endif
+
+static const unsigned int utf8vers = 0xc0100;
+
+static const unsigned int utf8agetab[] = {
+ 0,
+ 0x10100,
+ 0x20000,
+ 0x20100,
+ 0x30000,
+ 0x30100,
+ 0x30200,
+ 0x40000,
+ 0x40100,
+ 0x50000,
+ 0x50100,
+ 0x50200,
+ 0x60000,
+ 0x60100,
+ 0x60200,
+ 0x60300,
+ 0x70000,
+ 0x80000,
+ 0x90000,
+ 0xa0000,
+ 0xb0000,
+ 0xc0000,
+ 0xc0100
+};
+
+static const struct utf8data utf8nfdicfdata[] = {
+ { 0, 0 },
+ { 0x10100, 0 },
+ { 0x20000, 0 },
+ { 0x20100, 0 },
+ { 0x30000, 0 },
+ { 0x30100, 0 },
+ { 0x30200, 1792 },
+ { 0x40000, 3200 },
+ { 0x40100, 3200 },
+ { 0x50000, 3200 },
+ { 0x50100, 3200 },
+ { 0x50200, 3200 },
+ { 0x60000, 3200 },
+ { 0x60100, 3200 },
+ { 0x60200, 3200 },
+ { 0x60300, 3200 },
+ { 0x70000, 3200 },
+ { 0x80000, 3200 },
+ { 0x90000, 3200 },
+ { 0xa0000, 3200 },
+ { 0xb0000, 3200 },
+ { 0xc0000, 3200 },
+ { 0xc0100, 3200 }
+};
+
+static const struct utf8data utf8nfdidata[] = {
+ { 0, 896 },
+ { 0x10100, 896 },
+ { 0x20000, 896 },
+ { 0x20100, 896 },
+ { 0x30000, 896 },
+ { 0x30100, 896 },
+ { 0x30200, 2496 },
+ { 0x40000, 20736 },
+ { 0x40100, 20736 },
+ { 0x50000, 20736 },
+ { 0x50100, 20736 },
+ { 0x50200, 20736 },
+ { 0x60000, 20736 },
+ { 0x60100, 20736 },
+ { 0x60200, 20736 },
+ { 0x60300, 20736 },
+ { 0x70000, 20736 },
+ { 0x80000, 20736 },
+ { 0x90000, 20736 },
+ { 0xa0000, 20736 },
+ { 0xb0000, 20736 },
+ { 0xc0000, 20736 },
+ { 0xc0100, 20736 }
+};
+
+static const unsigned char utf8data[64256] = {
+ /* nfdicf_30100 */
+ 0xd7,0x07,0x66,0x84,0x0c,0x01,0x00,0xc6,0xd5,0x16,0xe4,0x99,0x1a,0xe3,0x63,0x15,
+ 0xe2,0x4c,0x0e,0xc1,0xe0,0x4e,0x0d,0xcf,0x86,0x65,0x2d,0x0d,0x01,0x00,0xd4,0xb8,
+ 0xd3,0x27,0xe2,0x89,0xa3,0xe1,0xce,0x35,0xe0,0x2c,0x22,0xcf,0x86,0xc5,0xe4,0x15,
+ 0x6d,0xe3,0x60,0x68,0xe2,0xf6,0x65,0xe1,0x29,0x65,0xe0,0xee,0x64,0xcf,0x86,0xe5,
+ 0xb3,0x64,0x64,0x96,0x64,0x0b,0x00,0xd2,0x0e,0xe1,0xb5,0x3c,0xe0,0xba,0xa3,0xcf,
+ 0x86,0xcf,0x06,0x01,0x00,0xd1,0x0c,0xe0,0x1e,0xa9,0xcf,0x86,0xcf,0x06,0x02,0xff,
+ 0xff,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x01,
+ 0x00,0xe4,0xe1,0x45,0xe3,0x3b,0x45,0xd2,0x06,0xcf,0x06,0x01,0x00,0xe1,0x87,0xad,
+ 0xd0,0x21,0xcf,0x86,0xe5,0x81,0xaa,0xe4,0x00,0xaa,0xe3,0xbf,0xa9,0xe2,0x9e,0xa9,
+ 0xe1,0x8d,0xa9,0x10,0x08,0x01,0xff,0xe8,0xb1,0x88,0x00,0x01,0xff,0xe6,0x9b,0xb4,
+ 0x00,0xcf,0x86,0xe5,0x63,0xac,0xd4,0x19,0xe3,0xa2,0xab,0xe2,0x81,0xab,0xe1,0x70,
+ 0xab,0x10,0x08,0x01,0xff,0xe9,0xb9,0xbf,0x00,0x01,0xff,0xe8,0xab,0x96,0x00,0xe3,
+ 0x09,0xac,0xe2,0xe8,0xab,0xe1,0xd7,0xab,0x10,0x08,0x01,0xff,0xe7,0xb8,0xb7,0x00,
+ 0x01,0xff,0xe9,0x9b,0xbb,0x00,0x83,0xe2,0x19,0xfa,0xe1,0xf2,0xf6,0xe0,0x6f,0xf5,
+ 0xcf,0x86,0xd5,0x31,0xc4,0xe3,0x54,0x4e,0xe2,0xf5,0x4c,0xe1,0xa4,0xcc,0xe0,0x9c,
+ 0x4b,0xcf,0x86,0xe5,0x8e,0x49,0xe4,0xaf,0x46,0xe3,0x11,0xbd,0xe2,0x68,0xbc,0xe1,
+ 0x43,0xbc,0xe0,0x1c,0xbc,0xcf,0x86,0xe5,0xe9,0xbb,0x94,0x07,0x63,0xd4,0xbb,0x07,
+ 0x00,0x07,0x00,0xe4,0xdb,0xf4,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd2,0x0b,
+ 0xe1,0xea,0xe1,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd1,0x0e,0xe0,0xd9,0xe2,0xcf,0x86,
+ 0xe5,0x9e,0xe2,0xcf,0x06,0x11,0x00,0xd0,0x0b,0xcf,0x86,0xe5,0xd9,0xe2,0xcf,0x06,
+ 0x13,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xe4,0x74,0xf4,0xe3,0x5d,0xf3,
+ 0xd2,0xa0,0xe1,0x13,0xe7,0xd0,0x21,0xcf,0x86,0xe5,0x14,0xe4,0xe4,0x90,0xe3,0xe3,
+ 0x4e,0xe3,0xe2,0x2d,0xe3,0xe1,0x1b,0xe3,0x10,0x08,0x05,0xff,0xe4,0xb8,0xbd,0x00,
+ 0x05,0xff,0xe4,0xb8,0xb8,0x00,0xcf,0x86,0xd5,0x1c,0xe4,0x70,0xe5,0xe3,0x2f,0xe5,
+ 0xe2,0x0e,0xe5,0xe1,0xfd,0xe4,0x10,0x08,0x05,0xff,0xe5,0x92,0xa2,0x00,0x05,0xff,
+ 0xe5,0x93,0xb6,0x00,0xd4,0x34,0xd3,0x18,0xe2,0xf7,0xe5,0xe1,0xe6,0xe5,0x10,0x09,
+ 0x05,0xff,0xf0,0xa1,0x9a,0xa8,0x00,0x05,0xff,0xf0,0xa1,0x9b,0xaa,0x00,0xe2,0x17,
+ 0xe6,0x91,0x11,0x10,0x09,0x05,0xff,0xf0,0xa1,0x8d,0xaa,0x00,0x05,0xff,0xe5,0xac,
+ 0x88,0x00,0x05,0xff,0xe5,0xac,0xbe,0x00,0xe3,0x5d,0xe6,0xd2,0x14,0xe1,0x2c,0xe6,
+ 0x10,0x08,0x05,0xff,0xe5,0xaf,0xb3,0x00,0x05,0xff,0xf0,0xa1,0xac,0x98,0x00,0xe1,
+ 0x38,0xe6,0x10,0x08,0x05,0xff,0xe5,0xbc,0xb3,0x00,0x05,0xff,0xe5,0xb0,0xa2,0x00,
+ 0xd1,0xd5,0xd0,0x6a,0xcf,0x86,0xe5,0x8d,0xeb,0xd4,0x19,0xe3,0xc6,0xea,0xe2,0xa4,
+ 0xea,0xe1,0x93,0xea,0x10,0x08,0x05,0xff,0xe6,0xb4,0xbe,0x00,0x05,0xff,0xe6,0xb5,
+ 0xb7,0x00,0xd3,0x18,0xe2,0x10,0xeb,0xe1,0xff,0xea,0x10,0x09,0x05,0xff,0xf0,0xa3,
+ 0xbd,0x9e,0x00,0x05,0xff,0xf0,0xa3,0xbe,0x8e,0x00,0xd2,0x13,0xe1,0x28,0xeb,0x10,
+ 0x08,0x05,0xff,0xe7,0x81,0xbd,0x00,0x05,0xff,0xe7,0x81,0xb7,0x00,0xd1,0x11,0x10,
+ 0x08,0x05,0xff,0xe7,0x85,0x85,0x00,0x05,0xff,0xf0,0xa4,0x89,0xa3,0x00,0x10,0x08,
+ 0x05,0xff,0xe7,0x86,0x9c,0x00,0x05,0xff,0xe4,0x8e,0xab,0x00,0xcf,0x86,0xe5,0x2a,
+ 0xed,0xd4,0x1a,0xe3,0x62,0xec,0xe2,0x48,0xec,0xe1,0x35,0xec,0x10,0x08,0x05,0xff,
+ 0xe7,0x9b,0xb4,0x00,0x05,0xff,0xf0,0xa5,0x83,0xb3,0x00,0xd3,0x16,0xe2,0xaa,0xec,
+ 0xe1,0x98,0xec,0x10,0x08,0x05,0xff,0xe7,0xa3,0x8c,0x00,0x05,0xff,0xe4,0x83,0xa3,
+ 0x00,0xd2,0x13,0xe1,0xc6,0xec,0x10,0x08,0x05,0xff,0xe4,0x84,0xaf,0x00,0x05,0xff,
+ 0xe7,0xa9,0x80,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa5,0xa5,0xbc,0x00,0x05,
+ 0xff,0xf0,0xa5,0xaa,0xa7,0x00,0x10,0x09,0x05,0xff,0xf0,0xa5,0xaa,0xa7,0x00,0x05,
+ 0xff,0xe7,0xaa,0xae,0x00,0xe0,0xdc,0xef,0xcf,0x86,0xd5,0x1d,0xe4,0x51,0xee,0xe3,
+ 0x0d,0xee,0xe2,0xeb,0xed,0xe1,0xda,0xed,0x10,0x09,0x05,0xff,0xf0,0xa3,0x8d,0x9f,
+ 0x00,0x05,0xff,0xe4,0x8f,0x95,0x00,0xd4,0x19,0xe3,0xf8,0xee,0xe2,0xd4,0xee,0xe1,
+ 0xc3,0xee,0x10,0x08,0x05,0xff,0xe8,0x8d,0x93,0x00,0x05,0xff,0xe8,0x8f,0x8a,0x00,
+ 0xd3,0x18,0xe2,0x43,0xef,0xe1,0x32,0xef,0x10,0x09,0x05,0xff,0xf0,0xa6,0xbe,0xb1,
+ 0x00,0x05,0xff,0xf0,0xa7,0x83,0x92,0x00,0xd2,0x13,0xe1,0x5b,0xef,0x10,0x08,0x05,
+ 0xff,0xe8,0x9a,0x88,0x00,0x05,0xff,0xe8,0x9c,0x8e,0x00,0xd1,0x10,0x10,0x08,0x05,
+ 0xff,0xe8,0x9c,0xa8,0x00,0x05,0xff,0xe8,0x9d,0xab,0x00,0x10,0x08,0x05,0xff,0xe8,
+ 0x9e,0x86,0x00,0x05,0xff,0xe4,0xb5,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* nfdi_30100 */
+ 0x57,0x04,0x01,0x00,0xc6,0xd5,0x16,0xe4,0xc2,0x59,0xe3,0xfb,0x54,0xe2,0x74,0x4f,
+ 0xc1,0xe0,0xa0,0x4d,0xcf,0x86,0x65,0x84,0x4d,0x01,0x00,0xd4,0xb8,0xd3,0x27,0xe2,
+ 0x0c,0xa0,0xe1,0xdf,0x8d,0xe0,0x39,0x71,0xcf,0x86,0xc5,0xe4,0x98,0x69,0xe3,0xe3,
+ 0x64,0xe2,0x79,0x62,0xe1,0xac,0x61,0xe0,0x71,0x61,0xcf,0x86,0xe5,0x36,0x61,0x64,
+ 0x19,0x61,0x0b,0x00,0xd2,0x0e,0xe1,0xc2,0xa0,0xe0,0x3d,0xa0,0xcf,0x86,0xcf,0x06,
+ 0x01,0x00,0xd1,0x0c,0xe0,0xa1,0xa5,0xcf,0x86,0xcf,0x06,0x02,0xff,0xff,0xd0,0x08,
+ 0xcf,0x86,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x01,0x00,0xe4,0x9e,
+ 0xb6,0xe3,0x18,0xae,0xd2,0x06,0xcf,0x06,0x01,0x00,0xe1,0x0a,0xaa,0xd0,0x21,0xcf,
+ 0x86,0xe5,0x04,0xa7,0xe4,0x83,0xa6,0xe3,0x42,0xa6,0xe2,0x21,0xa6,0xe1,0x10,0xa6,
+ 0x10,0x08,0x01,0xff,0xe8,0xb1,0x88,0x00,0x01,0xff,0xe6,0x9b,0xb4,0x00,0xcf,0x86,
+ 0xe5,0xe6,0xa8,0xd4,0x19,0xe3,0x25,0xa8,0xe2,0x04,0xa8,0xe1,0xf3,0xa7,0x10,0x08,
+ 0x01,0xff,0xe9,0xb9,0xbf,0x00,0x01,0xff,0xe8,0xab,0x96,0x00,0xe3,0x8c,0xa8,0xe2,
+ 0x6b,0xa8,0xe1,0x5a,0xa8,0x10,0x08,0x01,0xff,0xe7,0xb8,0xb7,0x00,0x01,0xff,0xe9,
+ 0x9b,0xbb,0x00,0x83,0xe2,0x9c,0xf6,0xe1,0x75,0xf3,0xe0,0xf2,0xf1,0xcf,0x86,0xd5,
+ 0x31,0xc4,0xe3,0x6d,0xcc,0xe2,0x46,0xca,0xe1,0x27,0xc9,0xe0,0xb7,0xbf,0xcf,0x86,
+ 0xe5,0xaa,0xbb,0xe4,0xa3,0xba,0xe3,0x94,0xb9,0xe2,0xeb,0xb8,0xe1,0xc6,0xb8,0xe0,
+ 0x9f,0xb8,0xcf,0x86,0xe5,0x6c,0xb8,0x94,0x07,0x63,0x57,0xb8,0x07,0x00,0x07,0x00,
+ 0xe4,0x5e,0xf1,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd2,0x0b,0xe1,0x6d,0xde,
+ 0xcf,0x86,0xcf,0x06,0x05,0x00,0xd1,0x0e,0xe0,0x5c,0xdf,0xcf,0x86,0xe5,0x21,0xdf,
+ 0xcf,0x06,0x11,0x00,0xd0,0x0b,0xcf,0x86,0xe5,0x5c,0xdf,0xcf,0x06,0x13,0x00,0xcf,
+ 0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xe4,0xf7,0xf0,0xe3,0xe0,0xef,0xd2,0xa0,0xe1,
+ 0x96,0xe3,0xd0,0x21,0xcf,0x86,0xe5,0x97,0xe0,0xe4,0x13,0xe0,0xe3,0xd1,0xdf,0xe2,
+ 0xb0,0xdf,0xe1,0x9e,0xdf,0x10,0x08,0x05,0xff,0xe4,0xb8,0xbd,0x00,0x05,0xff,0xe4,
+ 0xb8,0xb8,0x00,0xcf,0x86,0xd5,0x1c,0xe4,0xf3,0xe1,0xe3,0xb2,0xe1,0xe2,0x91,0xe1,
+ 0xe1,0x80,0xe1,0x10,0x08,0x05,0xff,0xe5,0x92,0xa2,0x00,0x05,0xff,0xe5,0x93,0xb6,
+ 0x00,0xd4,0x34,0xd3,0x18,0xe2,0x7a,0xe2,0xe1,0x69,0xe2,0x10,0x09,0x05,0xff,0xf0,
+ 0xa1,0x9a,0xa8,0x00,0x05,0xff,0xf0,0xa1,0x9b,0xaa,0x00,0xe2,0x9a,0xe2,0x91,0x11,
+ 0x10,0x09,0x05,0xff,0xf0,0xa1,0x8d,0xaa,0x00,0x05,0xff,0xe5,0xac,0x88,0x00,0x05,
+ 0xff,0xe5,0xac,0xbe,0x00,0xe3,0xe0,0xe2,0xd2,0x14,0xe1,0xaf,0xe2,0x10,0x08,0x05,
+ 0xff,0xe5,0xaf,0xb3,0x00,0x05,0xff,0xf0,0xa1,0xac,0x98,0x00,0xe1,0xbb,0xe2,0x10,
+ 0x08,0x05,0xff,0xe5,0xbc,0xb3,0x00,0x05,0xff,0xe5,0xb0,0xa2,0x00,0xd1,0xd5,0xd0,
+ 0x6a,0xcf,0x86,0xe5,0x10,0xe8,0xd4,0x19,0xe3,0x49,0xe7,0xe2,0x27,0xe7,0xe1,0x16,
+ 0xe7,0x10,0x08,0x05,0xff,0xe6,0xb4,0xbe,0x00,0x05,0xff,0xe6,0xb5,0xb7,0x00,0xd3,
+ 0x18,0xe2,0x93,0xe7,0xe1,0x82,0xe7,0x10,0x09,0x05,0xff,0xf0,0xa3,0xbd,0x9e,0x00,
+ 0x05,0xff,0xf0,0xa3,0xbe,0x8e,0x00,0xd2,0x13,0xe1,0xab,0xe7,0x10,0x08,0x05,0xff,
+ 0xe7,0x81,0xbd,0x00,0x05,0xff,0xe7,0x81,0xb7,0x00,0xd1,0x11,0x10,0x08,0x05,0xff,
+ 0xe7,0x85,0x85,0x00,0x05,0xff,0xf0,0xa4,0x89,0xa3,0x00,0x10,0x08,0x05,0xff,0xe7,
+ 0x86,0x9c,0x00,0x05,0xff,0xe4,0x8e,0xab,0x00,0xcf,0x86,0xe5,0xad,0xe9,0xd4,0x1a,
+ 0xe3,0xe5,0xe8,0xe2,0xcb,0xe8,0xe1,0xb8,0xe8,0x10,0x08,0x05,0xff,0xe7,0x9b,0xb4,
+ 0x00,0x05,0xff,0xf0,0xa5,0x83,0xb3,0x00,0xd3,0x16,0xe2,0x2d,0xe9,0xe1,0x1b,0xe9,
+ 0x10,0x08,0x05,0xff,0xe7,0xa3,0x8c,0x00,0x05,0xff,0xe4,0x83,0xa3,0x00,0xd2,0x13,
+ 0xe1,0x49,0xe9,0x10,0x08,0x05,0xff,0xe4,0x84,0xaf,0x00,0x05,0xff,0xe7,0xa9,0x80,
+ 0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa5,0xa5,0xbc,0x00,0x05,0xff,0xf0,0xa5,
+ 0xaa,0xa7,0x00,0x10,0x09,0x05,0xff,0xf0,0xa5,0xaa,0xa7,0x00,0x05,0xff,0xe7,0xaa,
+ 0xae,0x00,0xe0,0x5f,0xec,0xcf,0x86,0xd5,0x1d,0xe4,0xd4,0xea,0xe3,0x90,0xea,0xe2,
+ 0x6e,0xea,0xe1,0x5d,0xea,0x10,0x09,0x05,0xff,0xf0,0xa3,0x8d,0x9f,0x00,0x05,0xff,
+ 0xe4,0x8f,0x95,0x00,0xd4,0x19,0xe3,0x7b,0xeb,0xe2,0x57,0xeb,0xe1,0x46,0xeb,0x10,
+ 0x08,0x05,0xff,0xe8,0x8d,0x93,0x00,0x05,0xff,0xe8,0x8f,0x8a,0x00,0xd3,0x18,0xe2,
+ 0xc6,0xeb,0xe1,0xb5,0xeb,0x10,0x09,0x05,0xff,0xf0,0xa6,0xbe,0xb1,0x00,0x05,0xff,
+ 0xf0,0xa7,0x83,0x92,0x00,0xd2,0x13,0xe1,0xde,0xeb,0x10,0x08,0x05,0xff,0xe8,0x9a,
+ 0x88,0x00,0x05,0xff,0xe8,0x9c,0x8e,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x9c,
+ 0xa8,0x00,0x05,0xff,0xe8,0x9d,0xab,0x00,0x10,0x08,0x05,0xff,0xe8,0x9e,0x86,0x00,
+ 0x05,0xff,0xe4,0xb5,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* nfdicf_30200 */
+ 0xd7,0x07,0x66,0x84,0x05,0x01,0x00,0xc6,0xd5,0x16,0xe4,0x99,0x13,0xe3,0x63,0x0e,
+ 0xe2,0x4c,0x07,0xc1,0xe0,0x4e,0x06,0xcf,0x86,0x65,0x2d,0x06,0x01,0x00,0xd4,0x2a,
+ 0xe3,0xd0,0x35,0xe2,0x88,0x9c,0xe1,0xcd,0x2e,0xe0,0x2b,0x1b,0xcf,0x86,0xc5,0xe4,
+ 0x14,0x66,0xe3,0x5f,0x61,0xe2,0xf5,0x5e,0xe1,0x28,0x5e,0xe0,0xed,0x5d,0xcf,0x86,
+ 0xe5,0xb2,0x5d,0x64,0x95,0x5d,0x0b,0x00,0x83,0xe2,0xa7,0xf3,0xe1,0x80,0xf0,0xe0,
+ 0xfd,0xee,0xcf,0x86,0xd5,0x31,0xc4,0xe3,0xe2,0x47,0xe2,0x83,0x46,0xe1,0x32,0xc6,
+ 0xe0,0x2a,0x45,0xcf,0x86,0xe5,0x1c,0x43,0xe4,0x3d,0x40,0xe3,0x9f,0xb6,0xe2,0xf6,
+ 0xb5,0xe1,0xd1,0xb5,0xe0,0xaa,0xb5,0xcf,0x86,0xe5,0x77,0xb5,0x94,0x07,0x63,0x62,
+ 0xb5,0x07,0x00,0x07,0x00,0xe4,0x69,0xee,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x05,0x00,
+ 0xd2,0x0b,0xe1,0x78,0xdb,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd1,0x0e,0xe0,0x67,0xdc,
+ 0xcf,0x86,0xe5,0x2c,0xdc,0xcf,0x06,0x11,0x00,0xd0,0x0b,0xcf,0x86,0xe5,0x67,0xdc,
+ 0xcf,0x06,0x13,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xe4,0x02,0xee,0xe3,
+ 0xeb,0xec,0xd2,0xa0,0xe1,0xa1,0xe0,0xd0,0x21,0xcf,0x86,0xe5,0xa2,0xdd,0xe4,0x1e,
+ 0xdd,0xe3,0xdc,0xdc,0xe2,0xbb,0xdc,0xe1,0xa9,0xdc,0x10,0x08,0x05,0xff,0xe4,0xb8,
+ 0xbd,0x00,0x05,0xff,0xe4,0xb8,0xb8,0x00,0xcf,0x86,0xd5,0x1c,0xe4,0xfe,0xde,0xe3,
+ 0xbd,0xde,0xe2,0x9c,0xde,0xe1,0x8b,0xde,0x10,0x08,0x05,0xff,0xe5,0x92,0xa2,0x00,
+ 0x05,0xff,0xe5,0x93,0xb6,0x00,0xd4,0x34,0xd3,0x18,0xe2,0x85,0xdf,0xe1,0x74,0xdf,
+ 0x10,0x09,0x05,0xff,0xf0,0xa1,0x9a,0xa8,0x00,0x05,0xff,0xf0,0xa1,0x9b,0xaa,0x00,
+ 0xe2,0xa5,0xdf,0x91,0x11,0x10,0x09,0x05,0xff,0xf0,0xa1,0x8d,0xaa,0x00,0x05,0xff,
+ 0xe5,0xac,0x88,0x00,0x05,0xff,0xe5,0xac,0xbe,0x00,0xe3,0xeb,0xdf,0xd2,0x14,0xe1,
+ 0xba,0xdf,0x10,0x08,0x05,0xff,0xe5,0xaf,0xb3,0x00,0x05,0xff,0xf0,0xa1,0xac,0x98,
+ 0x00,0xe1,0xc6,0xdf,0x10,0x08,0x05,0xff,0xe5,0xbc,0xb3,0x00,0x05,0xff,0xe5,0xb0,
+ 0xa2,0x00,0xd1,0xd5,0xd0,0x6a,0xcf,0x86,0xe5,0x1b,0xe5,0xd4,0x19,0xe3,0x54,0xe4,
+ 0xe2,0x32,0xe4,0xe1,0x21,0xe4,0x10,0x08,0x05,0xff,0xe6,0xb4,0xbe,0x00,0x05,0xff,
+ 0xe6,0xb5,0xb7,0x00,0xd3,0x18,0xe2,0x9e,0xe4,0xe1,0x8d,0xe4,0x10,0x09,0x05,0xff,
+ 0xf0,0xa3,0xbd,0x9e,0x00,0x05,0xff,0xf0,0xa3,0xbe,0x8e,0x00,0xd2,0x13,0xe1,0xb6,
+ 0xe4,0x10,0x08,0x05,0xff,0xe7,0x81,0xbd,0x00,0x05,0xff,0xe7,0x81,0xb7,0x00,0xd1,
+ 0x11,0x10,0x08,0x05,0xff,0xe7,0x85,0x85,0x00,0x05,0xff,0xf0,0xa4,0x89,0xa3,0x00,
+ 0x10,0x08,0x05,0xff,0xe7,0x86,0x9c,0x00,0x05,0xff,0xe4,0x8e,0xab,0x00,0xcf,0x86,
+ 0xe5,0xb8,0xe6,0xd4,0x1a,0xe3,0xf0,0xe5,0xe2,0xd6,0xe5,0xe1,0xc3,0xe5,0x10,0x08,
+ 0x05,0xff,0xe7,0x9b,0xb4,0x00,0x05,0xff,0xf0,0xa5,0x83,0xb3,0x00,0xd3,0x16,0xe2,
+ 0x38,0xe6,0xe1,0x26,0xe6,0x10,0x08,0x05,0xff,0xe7,0xa3,0x8c,0x00,0x05,0xff,0xe4,
+ 0x83,0xa3,0x00,0xd2,0x13,0xe1,0x54,0xe6,0x10,0x08,0x05,0xff,0xe4,0x84,0xaf,0x00,
+ 0x05,0xff,0xe7,0xa9,0x80,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa5,0xa5,0xbc,
+ 0x00,0x05,0xff,0xf0,0xa5,0xaa,0xa7,0x00,0x10,0x09,0x05,0xff,0xf0,0xa5,0xaa,0xa7,
+ 0x00,0x05,0xff,0xe7,0xaa,0xae,0x00,0xe0,0x6a,0xe9,0xcf,0x86,0xd5,0x1d,0xe4,0xdf,
+ 0xe7,0xe3,0x9b,0xe7,0xe2,0x79,0xe7,0xe1,0x68,0xe7,0x10,0x09,0x05,0xff,0xf0,0xa3,
+ 0x8d,0x9f,0x00,0x05,0xff,0xe4,0x8f,0x95,0x00,0xd4,0x19,0xe3,0x86,0xe8,0xe2,0x62,
+ 0xe8,0xe1,0x51,0xe8,0x10,0x08,0x05,0xff,0xe8,0x8d,0x93,0x00,0x05,0xff,0xe8,0x8f,
+ 0x8a,0x00,0xd3,0x18,0xe2,0xd1,0xe8,0xe1,0xc0,0xe8,0x10,0x09,0x05,0xff,0xf0,0xa6,
+ 0xbe,0xb1,0x00,0x05,0xff,0xf0,0xa7,0x83,0x92,0x00,0xd2,0x13,0xe1,0xe9,0xe8,0x10,
+ 0x08,0x05,0xff,0xe8,0x9a,0x88,0x00,0x05,0xff,0xe8,0x9c,0x8e,0x00,0xd1,0x10,0x10,
+ 0x08,0x05,0xff,0xe8,0x9c,0xa8,0x00,0x05,0xff,0xe8,0x9d,0xab,0x00,0x10,0x08,0x05,
+ 0xff,0xe8,0x9e,0x86,0x00,0x05,0xff,0xe4,0xb5,0x97,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* nfdi_30200 */
+ 0x57,0x04,0x01,0x00,0xc6,0xd5,0x16,0xe4,0x82,0x53,0xe3,0xbb,0x4e,0xe2,0x34,0x49,
+ 0xc1,0xe0,0x60,0x47,0xcf,0x86,0x65,0x44,0x47,0x01,0x00,0xd4,0x2a,0xe3,0x1c,0x9a,
+ 0xe2,0xcb,0x99,0xe1,0x9e,0x87,0xe0,0xf8,0x6a,0xcf,0x86,0xc5,0xe4,0x57,0x63,0xe3,
+ 0xa2,0x5e,0xe2,0x38,0x5c,0xe1,0x6b,0x5b,0xe0,0x30,0x5b,0xcf,0x86,0xe5,0xf5,0x5a,
+ 0x64,0xd8,0x5a,0x0b,0x00,0x83,0xe2,0xea,0xf0,0xe1,0xc3,0xed,0xe0,0x40,0xec,0xcf,
+ 0x86,0xd5,0x31,0xc4,0xe3,0xbb,0xc6,0xe2,0x94,0xc4,0xe1,0x75,0xc3,0xe0,0x05,0xba,
+ 0xcf,0x86,0xe5,0xf8,0xb5,0xe4,0xf1,0xb4,0xe3,0xe2,0xb3,0xe2,0x39,0xb3,0xe1,0x14,
+ 0xb3,0xe0,0xed,0xb2,0xcf,0x86,0xe5,0xba,0xb2,0x94,0x07,0x63,0xa5,0xb2,0x07,0x00,
+ 0x07,0x00,0xe4,0xac,0xeb,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd2,0x0b,0xe1,
+ 0xbb,0xd8,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd1,0x0e,0xe0,0xaa,0xd9,0xcf,0x86,0xe5,
+ 0x6f,0xd9,0xcf,0x06,0x11,0x00,0xd0,0x0b,0xcf,0x86,0xe5,0xaa,0xd9,0xcf,0x06,0x13,
+ 0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xe4,0x45,0xeb,0xe3,0x2e,0xea,0xd2,
+ 0xa0,0xe1,0xe4,0xdd,0xd0,0x21,0xcf,0x86,0xe5,0xe5,0xda,0xe4,0x61,0xda,0xe3,0x1f,
+ 0xda,0xe2,0xfe,0xd9,0xe1,0xec,0xd9,0x10,0x08,0x05,0xff,0xe4,0xb8,0xbd,0x00,0x05,
+ 0xff,0xe4,0xb8,0xb8,0x00,0xcf,0x86,0xd5,0x1c,0xe4,0x41,0xdc,0xe3,0x00,0xdc,0xe2,
+ 0xdf,0xdb,0xe1,0xce,0xdb,0x10,0x08,0x05,0xff,0xe5,0x92,0xa2,0x00,0x05,0xff,0xe5,
+ 0x93,0xb6,0x00,0xd4,0x34,0xd3,0x18,0xe2,0xc8,0xdc,0xe1,0xb7,0xdc,0x10,0x09,0x05,
+ 0xff,0xf0,0xa1,0x9a,0xa8,0x00,0x05,0xff,0xf0,0xa1,0x9b,0xaa,0x00,0xe2,0xe8,0xdc,
+ 0x91,0x11,0x10,0x09,0x05,0xff,0xf0,0xa1,0x8d,0xaa,0x00,0x05,0xff,0xe5,0xac,0x88,
+ 0x00,0x05,0xff,0xe5,0xac,0xbe,0x00,0xe3,0x2e,0xdd,0xd2,0x14,0xe1,0xfd,0xdc,0x10,
+ 0x08,0x05,0xff,0xe5,0xaf,0xb3,0x00,0x05,0xff,0xf0,0xa1,0xac,0x98,0x00,0xe1,0x09,
+ 0xdd,0x10,0x08,0x05,0xff,0xe5,0xbc,0xb3,0x00,0x05,0xff,0xe5,0xb0,0xa2,0x00,0xd1,
+ 0xd5,0xd0,0x6a,0xcf,0x86,0xe5,0x5e,0xe2,0xd4,0x19,0xe3,0x97,0xe1,0xe2,0x75,0xe1,
+ 0xe1,0x64,0xe1,0x10,0x08,0x05,0xff,0xe6,0xb4,0xbe,0x00,0x05,0xff,0xe6,0xb5,0xb7,
+ 0x00,0xd3,0x18,0xe2,0xe1,0xe1,0xe1,0xd0,0xe1,0x10,0x09,0x05,0xff,0xf0,0xa3,0xbd,
+ 0x9e,0x00,0x05,0xff,0xf0,0xa3,0xbe,0x8e,0x00,0xd2,0x13,0xe1,0xf9,0xe1,0x10,0x08,
+ 0x05,0xff,0xe7,0x81,0xbd,0x00,0x05,0xff,0xe7,0x81,0xb7,0x00,0xd1,0x11,0x10,0x08,
+ 0x05,0xff,0xe7,0x85,0x85,0x00,0x05,0xff,0xf0,0xa4,0x89,0xa3,0x00,0x10,0x08,0x05,
+ 0xff,0xe7,0x86,0x9c,0x00,0x05,0xff,0xe4,0x8e,0xab,0x00,0xcf,0x86,0xe5,0xfb,0xe3,
+ 0xd4,0x1a,0xe3,0x33,0xe3,0xe2,0x19,0xe3,0xe1,0x06,0xe3,0x10,0x08,0x05,0xff,0xe7,
+ 0x9b,0xb4,0x00,0x05,0xff,0xf0,0xa5,0x83,0xb3,0x00,0xd3,0x16,0xe2,0x7b,0xe3,0xe1,
+ 0x69,0xe3,0x10,0x08,0x05,0xff,0xe7,0xa3,0x8c,0x00,0x05,0xff,0xe4,0x83,0xa3,0x00,
+ 0xd2,0x13,0xe1,0x97,0xe3,0x10,0x08,0x05,0xff,0xe4,0x84,0xaf,0x00,0x05,0xff,0xe7,
+ 0xa9,0x80,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa5,0xa5,0xbc,0x00,0x05,0xff,
+ 0xf0,0xa5,0xaa,0xa7,0x00,0x10,0x09,0x05,0xff,0xf0,0xa5,0xaa,0xa7,0x00,0x05,0xff,
+ 0xe7,0xaa,0xae,0x00,0xe0,0xad,0xe6,0xcf,0x86,0xd5,0x1d,0xe4,0x22,0xe5,0xe3,0xde,
+ 0xe4,0xe2,0xbc,0xe4,0xe1,0xab,0xe4,0x10,0x09,0x05,0xff,0xf0,0xa3,0x8d,0x9f,0x00,
+ 0x05,0xff,0xe4,0x8f,0x95,0x00,0xd4,0x19,0xe3,0xc9,0xe5,0xe2,0xa5,0xe5,0xe1,0x94,
+ 0xe5,0x10,0x08,0x05,0xff,0xe8,0x8d,0x93,0x00,0x05,0xff,0xe8,0x8f,0x8a,0x00,0xd3,
+ 0x18,0xe2,0x14,0xe6,0xe1,0x03,0xe6,0x10,0x09,0x05,0xff,0xf0,0xa6,0xbe,0xb1,0x00,
+ 0x05,0xff,0xf0,0xa7,0x83,0x92,0x00,0xd2,0x13,0xe1,0x2c,0xe6,0x10,0x08,0x05,0xff,
+ 0xe8,0x9a,0x88,0x00,0x05,0xff,0xe8,0x9c,0x8e,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,
+ 0xe8,0x9c,0xa8,0x00,0x05,0xff,0xe8,0x9d,0xab,0x00,0x10,0x08,0x05,0xff,0xe8,0x9e,
+ 0x86,0x00,0x05,0xff,0xe4,0xb5,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* nfdicf_c0100 */
+ 0xd7,0xb0,0x56,0x04,0x01,0x00,0x95,0xa8,0xd4,0x5e,0xd3,0x2e,0xd2,0x16,0xd1,0x0a,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0x61,0x00,0x10,0x06,0x01,0xff,0x62,0x00,0x01,0xff,
+ 0x63,0x00,0xd1,0x0c,0x10,0x06,0x01,0xff,0x64,0x00,0x01,0xff,0x65,0x00,0x10,0x06,
+ 0x01,0xff,0x66,0x00,0x01,0xff,0x67,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x06,0x01,0xff,
+ 0x68,0x00,0x01,0xff,0x69,0x00,0x10,0x06,0x01,0xff,0x6a,0x00,0x01,0xff,0x6b,0x00,
+ 0xd1,0x0c,0x10,0x06,0x01,0xff,0x6c,0x00,0x01,0xff,0x6d,0x00,0x10,0x06,0x01,0xff,
+ 0x6e,0x00,0x01,0xff,0x6f,0x00,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x06,0x01,0xff,
+ 0x70,0x00,0x01,0xff,0x71,0x00,0x10,0x06,0x01,0xff,0x72,0x00,0x01,0xff,0x73,0x00,
+ 0xd1,0x0c,0x10,0x06,0x01,0xff,0x74,0x00,0x01,0xff,0x75,0x00,0x10,0x06,0x01,0xff,
+ 0x76,0x00,0x01,0xff,0x77,0x00,0x92,0x16,0xd1,0x0c,0x10,0x06,0x01,0xff,0x78,0x00,
+ 0x01,0xff,0x79,0x00,0x10,0x06,0x01,0xff,0x7a,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0xc6,0xe5,0xf9,0x14,0xe4,0x6f,0x0d,0xe3,0x39,0x08,0xe2,0x22,0x01,0xc1,0xd0,0x24,
+ 0xcf,0x86,0x55,0x04,0x01,0x00,0xd4,0x07,0x63,0xd8,0x43,0x01,0x00,0x93,0x13,0x52,
+ 0x04,0x01,0x00,0x91,0x0b,0x10,0x04,0x01,0x00,0x01,0xff,0xce,0xbc,0x00,0x01,0x00,
+ 0x01,0x00,0xcf,0x86,0xe5,0xb3,0x44,0xd4,0x7f,0xd3,0x3f,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x61,0xcc,0x80,0x00,0x01,0xff,0x61,0xcc,0x81,0x00,0x10,0x08,0x01,
+ 0xff,0x61,0xcc,0x82,0x00,0x01,0xff,0x61,0xcc,0x83,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x61,0xcc,0x88,0x00,0x01,0xff,0x61,0xcc,0x8a,0x00,0x10,0x07,0x01,0xff,0xc3,
+ 0xa6,0x00,0x01,0xff,0x63,0xcc,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0x65,0xcc,0x80,0x00,0x01,0xff,0x65,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,
+ 0x82,0x00,0x01,0xff,0x65,0xcc,0x88,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x69,0xcc,
+ 0x80,0x00,0x01,0xff,0x69,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x69,0xcc,0x82,0x00,
+ 0x01,0xff,0x69,0xcc,0x88,0x00,0xd3,0x3b,0xd2,0x1f,0xd1,0x0f,0x10,0x07,0x01,0xff,
+ 0xc3,0xb0,0x00,0x01,0xff,0x6e,0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x80,
+ 0x00,0x01,0xff,0x6f,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0x82,
+ 0x00,0x01,0xff,0x6f,0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x88,0x00,0x01,
+ 0x00,0xd2,0x1f,0xd1,0x0f,0x10,0x07,0x01,0xff,0xc3,0xb8,0x00,0x01,0xff,0x75,0xcc,
+ 0x80,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0x81,0x00,0x01,0xff,0x75,0xcc,0x82,0x00,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x75,0xcc,0x88,0x00,0x01,0xff,0x79,0xcc,0x81,0x00,
+ 0x10,0x07,0x01,0xff,0xc3,0xbe,0x00,0x01,0xff,0x73,0x73,0x00,0xe1,0xd4,0x03,0xe0,
+ 0xeb,0x01,0xcf,0x86,0xd5,0xfb,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0x61,0xcc,0x84,0x00,0x01,0xff,0x61,0xcc,0x84,0x00,0x10,0x08,0x01,0xff,
+ 0x61,0xcc,0x86,0x00,0x01,0xff,0x61,0xcc,0x86,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0x61,0xcc,0xa8,0x00,0x01,0xff,0x61,0xcc,0xa8,0x00,0x10,0x08,0x01,0xff,0x63,0xcc,
+ 0x81,0x00,0x01,0xff,0x63,0xcc,0x81,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0x63,0xcc,0x82,0x00,0x01,0xff,0x63,0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x63,0xcc,
+ 0x87,0x00,0x01,0xff,0x63,0xcc,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x63,0xcc,
+ 0x8c,0x00,0x01,0xff,0x63,0xcc,0x8c,0x00,0x10,0x08,0x01,0xff,0x64,0xcc,0x8c,0x00,
+ 0x01,0xff,0x64,0xcc,0x8c,0x00,0xd3,0x3b,0xd2,0x1b,0xd1,0x0b,0x10,0x07,0x01,0xff,
+ 0xc4,0x91,0x00,0x01,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,0x84,0x00,0x01,0xff,0x65,
+ 0xcc,0x84,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x65,0xcc,0x86,0x00,0x01,0xff,0x65,
+ 0xcc,0x86,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,0x87,0x00,0x01,0xff,0x65,0xcc,0x87,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x65,0xcc,0xa8,0x00,0x01,0xff,0x65,
+ 0xcc,0xa8,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,0x8c,0x00,0x01,0xff,0x65,0xcc,0x8c,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x67,0xcc,0x82,0x00,0x01,0xff,0x67,0xcc,0x82,
+ 0x00,0x10,0x08,0x01,0xff,0x67,0xcc,0x86,0x00,0x01,0xff,0x67,0xcc,0x86,0x00,0xd4,
+ 0x7b,0xd3,0x3b,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x67,0xcc,0x87,0x00,0x01,
+ 0xff,0x67,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x67,0xcc,0xa7,0x00,0x01,0xff,0x67,
+ 0xcc,0xa7,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x68,0xcc,0x82,0x00,0x01,0xff,0x68,
+ 0xcc,0x82,0x00,0x10,0x07,0x01,0xff,0xc4,0xa7,0x00,0x01,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0x69,0xcc,0x83,0x00,0x01,0xff,0x69,0xcc,0x83,0x00,0x10,0x08,
+ 0x01,0xff,0x69,0xcc,0x84,0x00,0x01,0xff,0x69,0xcc,0x84,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0x69,0xcc,0x86,0x00,0x01,0xff,0x69,0xcc,0x86,0x00,0x10,0x08,0x01,0xff,
+ 0x69,0xcc,0xa8,0x00,0x01,0xff,0x69,0xcc,0xa8,0x00,0xd3,0x37,0xd2,0x17,0xd1,0x0c,
+ 0x10,0x08,0x01,0xff,0x69,0xcc,0x87,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xc4,0xb3,
+ 0x00,0x01,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6a,0xcc,0x82,0x00,0x01,0xff,0x6a,
+ 0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x6b,0xcc,0xa7,0x00,0x01,0xff,0x6b,0xcc,0xa7,
+ 0x00,0xd2,0x1c,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0x6c,0xcc,0x81,0x00,0x10,
+ 0x08,0x01,0xff,0x6c,0xcc,0x81,0x00,0x01,0xff,0x6c,0xcc,0xa7,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x6c,0xcc,0xa7,0x00,0x01,0xff,0x6c,0xcc,0x8c,0x00,0x10,0x08,0x01,
+ 0xff,0x6c,0xcc,0x8c,0x00,0x01,0xff,0xc5,0x80,0x00,0xcf,0x86,0xd5,0xed,0xd4,0x72,
+ 0xd3,0x37,0xd2,0x17,0xd1,0x0b,0x10,0x04,0x01,0x00,0x01,0xff,0xc5,0x82,0x00,0x10,
+ 0x04,0x01,0x00,0x01,0xff,0x6e,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6e,
+ 0xcc,0x81,0x00,0x01,0xff,0x6e,0xcc,0xa7,0x00,0x10,0x08,0x01,0xff,0x6e,0xcc,0xa7,
+ 0x00,0x01,0xff,0x6e,0xcc,0x8c,0x00,0xd2,0x1b,0xd1,0x10,0x10,0x08,0x01,0xff,0x6e,
+ 0xcc,0x8c,0x00,0x01,0xff,0xca,0xbc,0x6e,0x00,0x10,0x07,0x01,0xff,0xc5,0x8b,0x00,
+ 0x01,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0x84,0x00,0x01,0xff,0x6f,0xcc,
+ 0x84,0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x86,0x00,0x01,0xff,0x6f,0xcc,0x86,0x00,
+ 0xd3,0x3b,0xd2,0x1b,0xd1,0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0x8b,0x00,0x01,0xff,
+ 0x6f,0xcc,0x8b,0x00,0x10,0x07,0x01,0xff,0xc5,0x93,0x00,0x01,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x72,0xcc,0x81,0x00,0x01,0xff,0x72,0xcc,0x81,0x00,0x10,0x08,0x01,
+ 0xff,0x72,0xcc,0xa7,0x00,0x01,0xff,0x72,0xcc,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x72,0xcc,0x8c,0x00,0x01,0xff,0x72,0xcc,0x8c,0x00,0x10,0x08,0x01,
+ 0xff,0x73,0xcc,0x81,0x00,0x01,0xff,0x73,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x73,0xcc,0x82,0x00,0x01,0xff,0x73,0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x73,
+ 0xcc,0xa7,0x00,0x01,0xff,0x73,0xcc,0xa7,0x00,0xd4,0x7b,0xd3,0x3b,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x73,0xcc,0x8c,0x00,0x01,0xff,0x73,0xcc,0x8c,0x00,0x10,
+ 0x08,0x01,0xff,0x74,0xcc,0xa7,0x00,0x01,0xff,0x74,0xcc,0xa7,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x74,0xcc,0x8c,0x00,0x01,0xff,0x74,0xcc,0x8c,0x00,0x10,0x07,0x01,
+ 0xff,0xc5,0xa7,0x00,0x01,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x75,0xcc,
+ 0x83,0x00,0x01,0xff,0x75,0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0x84,0x00,
+ 0x01,0xff,0x75,0xcc,0x84,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x75,0xcc,0x86,0x00,
+ 0x01,0xff,0x75,0xcc,0x86,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0x8a,0x00,0x01,0xff,
+ 0x75,0xcc,0x8a,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x75,0xcc,
+ 0x8b,0x00,0x01,0xff,0x75,0xcc,0x8b,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0xa8,0x00,
+ 0x01,0xff,0x75,0xcc,0xa8,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x77,0xcc,0x82,0x00,
+ 0x01,0xff,0x77,0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x79,0xcc,0x82,0x00,0x01,0xff,
+ 0x79,0xcc,0x82,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x79,0xcc,0x88,0x00,
+ 0x01,0xff,0x7a,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x7a,0xcc,0x81,0x00,0x01,0xff,
+ 0x7a,0xcc,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x7a,0xcc,0x87,0x00,0x01,0xff,
+ 0x7a,0xcc,0x8c,0x00,0x10,0x08,0x01,0xff,0x7a,0xcc,0x8c,0x00,0x01,0xff,0x73,0x00,
+ 0xe0,0x65,0x01,0xcf,0x86,0xd5,0xb4,0xd4,0x5a,0xd3,0x2f,0xd2,0x16,0xd1,0x0b,0x10,
+ 0x04,0x01,0x00,0x01,0xff,0xc9,0x93,0x00,0x10,0x07,0x01,0xff,0xc6,0x83,0x00,0x01,
+ 0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xc6,0x85,0x00,0x01,0x00,0x10,0x07,0x01,0xff,
+ 0xc9,0x94,0x00,0x01,0xff,0xc6,0x88,0x00,0xd2,0x19,0xd1,0x0b,0x10,0x04,0x01,0x00,
+ 0x01,0xff,0xc9,0x96,0x00,0x10,0x07,0x01,0xff,0xc9,0x97,0x00,0x01,0xff,0xc6,0x8c,
+ 0x00,0x51,0x04,0x01,0x00,0x10,0x07,0x01,0xff,0xc7,0x9d,0x00,0x01,0xff,0xc9,0x99,
+ 0x00,0xd3,0x32,0xd2,0x19,0xd1,0x0e,0x10,0x07,0x01,0xff,0xc9,0x9b,0x00,0x01,0xff,
+ 0xc6,0x92,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xc9,0xa0,0x00,0xd1,0x0b,0x10,0x07,
+ 0x01,0xff,0xc9,0xa3,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xc9,0xa9,0x00,0x01,0xff,
+ 0xc9,0xa8,0x00,0xd2,0x0f,0x91,0x0b,0x10,0x07,0x01,0xff,0xc6,0x99,0x00,0x01,0x00,
+ 0x01,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xc9,0xaf,0x00,0x01,0xff,0xc9,0xb2,0x00,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0xc9,0xb5,0x00,0xd4,0x5d,0xd3,0x34,0xd2,0x1b,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0x9b,0x00,0x01,0xff,0x6f,0xcc,0x9b,0x00,0x10,
+ 0x07,0x01,0xff,0xc6,0xa3,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xc6,0xa5,
+ 0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xca,0x80,0x00,0x01,0xff,0xc6,0xa8,0x00,0xd2,
+ 0x0f,0x91,0x0b,0x10,0x04,0x01,0x00,0x01,0xff,0xca,0x83,0x00,0x01,0x00,0xd1,0x0b,
+ 0x10,0x07,0x01,0xff,0xc6,0xad,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xca,0x88,0x00,
+ 0x01,0xff,0x75,0xcc,0x9b,0x00,0xd3,0x33,0xd2,0x1d,0xd1,0x0f,0x10,0x08,0x01,0xff,
+ 0x75,0xcc,0x9b,0x00,0x01,0xff,0xca,0x8a,0x00,0x10,0x07,0x01,0xff,0xca,0x8b,0x00,
+ 0x01,0xff,0xc6,0xb4,0x00,0xd1,0x0b,0x10,0x04,0x01,0x00,0x01,0xff,0xc6,0xb6,0x00,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0xca,0x92,0x00,0xd2,0x0f,0x91,0x0b,0x10,0x07,0x01,
+ 0xff,0xc6,0xb9,0x00,0x01,0x00,0x01,0x00,0x91,0x0b,0x10,0x07,0x01,0xff,0xc6,0xbd,
+ 0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0xd4,0xd4,0x44,0xd3,0x16,0x52,0x04,0x01,
+ 0x00,0x51,0x07,0x01,0xff,0xc7,0x86,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xc7,0x89,
+ 0x00,0xd2,0x12,0x91,0x0b,0x10,0x07,0x01,0xff,0xc7,0x89,0x00,0x01,0x00,0x01,0xff,
+ 0xc7,0x8c,0x00,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0x61,0xcc,0x8c,0x00,0x10,
+ 0x08,0x01,0xff,0x61,0xcc,0x8c,0x00,0x01,0xff,0x69,0xcc,0x8c,0x00,0xd3,0x46,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x69,0xcc,0x8c,0x00,0x01,0xff,0x6f,0xcc,0x8c,
+ 0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x8c,0x00,0x01,0xff,0x75,0xcc,0x8c,0x00,0xd1,
+ 0x12,0x10,0x08,0x01,0xff,0x75,0xcc,0x8c,0x00,0x01,0xff,0x75,0xcc,0x88,0xcc,0x84,
+ 0x00,0x10,0x0a,0x01,0xff,0x75,0xcc,0x88,0xcc,0x84,0x00,0x01,0xff,0x75,0xcc,0x88,
+ 0xcc,0x81,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x75,0xcc,0x88,0xcc,0x81,
+ 0x00,0x01,0xff,0x75,0xcc,0x88,0xcc,0x8c,0x00,0x10,0x0a,0x01,0xff,0x75,0xcc,0x88,
+ 0xcc,0x8c,0x00,0x01,0xff,0x75,0xcc,0x88,0xcc,0x80,0x00,0xd1,0x0e,0x10,0x0a,0x01,
+ 0xff,0x75,0xcc,0x88,0xcc,0x80,0x00,0x01,0x00,0x10,0x0a,0x01,0xff,0x61,0xcc,0x88,
+ 0xcc,0x84,0x00,0x01,0xff,0x61,0xcc,0x88,0xcc,0x84,0x00,0xd4,0x87,0xd3,0x41,0xd2,
+ 0x26,0xd1,0x14,0x10,0x0a,0x01,0xff,0x61,0xcc,0x87,0xcc,0x84,0x00,0x01,0xff,0x61,
+ 0xcc,0x87,0xcc,0x84,0x00,0x10,0x09,0x01,0xff,0xc3,0xa6,0xcc,0x84,0x00,0x01,0xff,
+ 0xc3,0xa6,0xcc,0x84,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xc7,0xa5,0x00,0x01,0x00,
+ 0x10,0x08,0x01,0xff,0x67,0xcc,0x8c,0x00,0x01,0xff,0x67,0xcc,0x8c,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x6b,0xcc,0x8c,0x00,0x01,0xff,0x6b,0xcc,0x8c,0x00,
+ 0x10,0x08,0x01,0xff,0x6f,0xcc,0xa8,0x00,0x01,0xff,0x6f,0xcc,0xa8,0x00,0xd1,0x14,
+ 0x10,0x0a,0x01,0xff,0x6f,0xcc,0xa8,0xcc,0x84,0x00,0x01,0xff,0x6f,0xcc,0xa8,0xcc,
+ 0x84,0x00,0x10,0x09,0x01,0xff,0xca,0x92,0xcc,0x8c,0x00,0x01,0xff,0xca,0x92,0xcc,
+ 0x8c,0x00,0xd3,0x38,0xd2,0x1a,0xd1,0x0f,0x10,0x08,0x01,0xff,0x6a,0xcc,0x8c,0x00,
+ 0x01,0xff,0xc7,0xb3,0x00,0x10,0x07,0x01,0xff,0xc7,0xb3,0x00,0x01,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0x67,0xcc,0x81,0x00,0x01,0xff,0x67,0xcc,0x81,0x00,0x10,0x07,
+ 0x04,0xff,0xc6,0x95,0x00,0x04,0xff,0xc6,0xbf,0x00,0xd2,0x24,0xd1,0x10,0x10,0x08,
+ 0x04,0xff,0x6e,0xcc,0x80,0x00,0x04,0xff,0x6e,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,
+ 0x61,0xcc,0x8a,0xcc,0x81,0x00,0x01,0xff,0x61,0xcc,0x8a,0xcc,0x81,0x00,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xc3,0xa6,0xcc,0x81,0x00,0x01,0xff,0xc3,0xa6,0xcc,0x81,0x00,
+ 0x10,0x09,0x01,0xff,0xc3,0xb8,0xcc,0x81,0x00,0x01,0xff,0xc3,0xb8,0xcc,0x81,0x00,
+ 0xe2,0x31,0x02,0xe1,0xc3,0x44,0xe0,0xc8,0x01,0xcf,0x86,0xd5,0xfb,0xd4,0x80,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x61,0xcc,0x8f,0x00,0x01,0xff,0x61,
+ 0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x61,0xcc,0x91,0x00,0x01,0xff,0x61,0xcc,0x91,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x65,0xcc,0x8f,0x00,0x01,0xff,0x65,0xcc,0x8f,
+ 0x00,0x10,0x08,0x01,0xff,0x65,0xcc,0x91,0x00,0x01,0xff,0x65,0xcc,0x91,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x69,0xcc,0x8f,0x00,0x01,0xff,0x69,0xcc,0x8f,
+ 0x00,0x10,0x08,0x01,0xff,0x69,0xcc,0x91,0x00,0x01,0xff,0x69,0xcc,0x91,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0x8f,0x00,0x01,0xff,0x6f,0xcc,0x8f,0x00,0x10,
+ 0x08,0x01,0xff,0x6f,0xcc,0x91,0x00,0x01,0xff,0x6f,0xcc,0x91,0x00,0xd3,0x40,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x72,0xcc,0x8f,0x00,0x01,0xff,0x72,0xcc,0x8f,
+ 0x00,0x10,0x08,0x01,0xff,0x72,0xcc,0x91,0x00,0x01,0xff,0x72,0xcc,0x91,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x75,0xcc,0x8f,0x00,0x01,0xff,0x75,0xcc,0x8f,0x00,0x10,
+ 0x08,0x01,0xff,0x75,0xcc,0x91,0x00,0x01,0xff,0x75,0xcc,0x91,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x04,0xff,0x73,0xcc,0xa6,0x00,0x04,0xff,0x73,0xcc,0xa6,0x00,0x10,
+ 0x08,0x04,0xff,0x74,0xcc,0xa6,0x00,0x04,0xff,0x74,0xcc,0xa6,0x00,0xd1,0x0b,0x10,
+ 0x07,0x04,0xff,0xc8,0x9d,0x00,0x04,0x00,0x10,0x08,0x04,0xff,0x68,0xcc,0x8c,0x00,
+ 0x04,0xff,0x68,0xcc,0x8c,0x00,0xd4,0x79,0xd3,0x31,0xd2,0x16,0xd1,0x0b,0x10,0x07,
+ 0x06,0xff,0xc6,0x9e,0x00,0x07,0x00,0x10,0x07,0x04,0xff,0xc8,0xa3,0x00,0x04,0x00,
+ 0xd1,0x0b,0x10,0x07,0x04,0xff,0xc8,0xa5,0x00,0x04,0x00,0x10,0x08,0x04,0xff,0x61,
+ 0xcc,0x87,0x00,0x04,0xff,0x61,0xcc,0x87,0x00,0xd2,0x24,0xd1,0x10,0x10,0x08,0x04,
+ 0xff,0x65,0xcc,0xa7,0x00,0x04,0xff,0x65,0xcc,0xa7,0x00,0x10,0x0a,0x04,0xff,0x6f,
+ 0xcc,0x88,0xcc,0x84,0x00,0x04,0xff,0x6f,0xcc,0x88,0xcc,0x84,0x00,0xd1,0x14,0x10,
+ 0x0a,0x04,0xff,0x6f,0xcc,0x83,0xcc,0x84,0x00,0x04,0xff,0x6f,0xcc,0x83,0xcc,0x84,
+ 0x00,0x10,0x08,0x04,0xff,0x6f,0xcc,0x87,0x00,0x04,0xff,0x6f,0xcc,0x87,0x00,0xd3,
+ 0x27,0xe2,0x21,0x43,0xd1,0x14,0x10,0x0a,0x04,0xff,0x6f,0xcc,0x87,0xcc,0x84,0x00,
+ 0x04,0xff,0x6f,0xcc,0x87,0xcc,0x84,0x00,0x10,0x08,0x04,0xff,0x79,0xcc,0x84,0x00,
+ 0x04,0xff,0x79,0xcc,0x84,0x00,0xd2,0x13,0x51,0x04,0x08,0x00,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0xa5,0x00,0x08,0xff,0xc8,0xbc,0x00,0xd1,0x0b,0x10,0x04,0x08,0x00,0x08,
+ 0xff,0xc6,0x9a,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,0xa6,0x00,0x08,0x00,0xcf,0x86,
+ 0x95,0x5f,0x94,0x5b,0xd3,0x2f,0xd2,0x16,0xd1,0x0b,0x10,0x04,0x08,0x00,0x08,0xff,
+ 0xc9,0x82,0x00,0x10,0x04,0x09,0x00,0x09,0xff,0xc6,0x80,0x00,0xd1,0x0e,0x10,0x07,
+ 0x09,0xff,0xca,0x89,0x00,0x09,0xff,0xca,0x8c,0x00,0x10,0x07,0x09,0xff,0xc9,0x87,
+ 0x00,0x09,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x09,0xff,0xc9,0x89,0x00,0x09,0x00,
+ 0x10,0x07,0x09,0xff,0xc9,0x8b,0x00,0x09,0x00,0xd1,0x0b,0x10,0x07,0x09,0xff,0xc9,
+ 0x8d,0x00,0x09,0x00,0x10,0x07,0x09,0xff,0xc9,0x8f,0x00,0x09,0x00,0x01,0x00,0x01,
+ 0x00,0xd1,0x8b,0xd0,0x0c,0xcf,0x86,0xe5,0x10,0x43,0x64,0xef,0x42,0x01,0xe6,0xcf,
+ 0x86,0xd5,0x2a,0xe4,0x99,0x43,0xe3,0x7f,0x43,0xd2,0x11,0xe1,0x5e,0x43,0x10,0x07,
+ 0x01,0xff,0xcc,0x80,0x00,0x01,0xff,0xcc,0x81,0x00,0xe1,0x65,0x43,0x10,0x09,0x01,
+ 0xff,0xcc,0x88,0xcc,0x81,0x00,0x01,0xff,0xce,0xb9,0x00,0xd4,0x0f,0x93,0x0b,0x92,
+ 0x07,0x61,0xab,0x43,0x01,0xea,0x06,0xe6,0x06,0xe6,0xd3,0x2c,0xd2,0x16,0xd1,0x0b,
+ 0x10,0x07,0x0a,0xff,0xcd,0xb1,0x00,0x0a,0x00,0x10,0x07,0x0a,0xff,0xcd,0xb3,0x00,
+ 0x0a,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xca,0xb9,0x00,0x01,0x00,0x10,0x07,0x0a,
+ 0xff,0xcd,0xb7,0x00,0x0a,0x00,0xd2,0x07,0x61,0x97,0x43,0x00,0x00,0x51,0x04,0x09,
+ 0x00,0x10,0x06,0x01,0xff,0x3b,0x00,0x10,0xff,0xcf,0xb3,0x00,0xe0,0x31,0x01,0xcf,
+ 0x86,0xd5,0xd3,0xd4,0x5f,0xd3,0x21,0x52,0x04,0x00,0x00,0xd1,0x0d,0x10,0x04,0x01,
+ 0x00,0x01,0xff,0xc2,0xa8,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x81,
+ 0x00,0x01,0xff,0xc2,0xb7,0x00,0xd2,0x1f,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb5,
+ 0xcc,0x81,0x00,0x01,0xff,0xce,0xb7,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xce,0xb9,
+ 0xcc,0x81,0x00,0x00,0x00,0xd1,0x0d,0x10,0x09,0x01,0xff,0xce,0xbf,0xcc,0x81,0x00,
+ 0x00,0x00,0x10,0x09,0x01,0xff,0xcf,0x85,0xcc,0x81,0x00,0x01,0xff,0xcf,0x89,0xcc,
+ 0x81,0x00,0xd3,0x3c,0xd2,0x20,0xd1,0x12,0x10,0x0b,0x01,0xff,0xce,0xb9,0xcc,0x88,
+ 0xcc,0x81,0x00,0x01,0xff,0xce,0xb1,0x00,0x10,0x07,0x01,0xff,0xce,0xb2,0x00,0x01,
+ 0xff,0xce,0xb3,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xce,0xb4,0x00,0x01,0xff,0xce,
+ 0xb5,0x00,0x10,0x07,0x01,0xff,0xce,0xb6,0x00,0x01,0xff,0xce,0xb7,0x00,0xd2,0x1c,
+ 0xd1,0x0e,0x10,0x07,0x01,0xff,0xce,0xb8,0x00,0x01,0xff,0xce,0xb9,0x00,0x10,0x07,
+ 0x01,0xff,0xce,0xba,0x00,0x01,0xff,0xce,0xbb,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,
+ 0xce,0xbc,0x00,0x01,0xff,0xce,0xbd,0x00,0x10,0x07,0x01,0xff,0xce,0xbe,0x00,0x01,
+ 0xff,0xce,0xbf,0x00,0xe4,0x85,0x43,0xd3,0x35,0xd2,0x19,0xd1,0x0e,0x10,0x07,0x01,
+ 0xff,0xcf,0x80,0x00,0x01,0xff,0xcf,0x81,0x00,0x10,0x04,0x00,0x00,0x01,0xff,0xcf,
+ 0x83,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xcf,0x84,0x00,0x01,0xff,0xcf,0x85,0x00,
+ 0x10,0x07,0x01,0xff,0xcf,0x86,0x00,0x01,0xff,0xcf,0x87,0x00,0xe2,0x2b,0x43,0xd1,
+ 0x0e,0x10,0x07,0x01,0xff,0xcf,0x88,0x00,0x01,0xff,0xcf,0x89,0x00,0x10,0x09,0x01,
+ 0xff,0xce,0xb9,0xcc,0x88,0x00,0x01,0xff,0xcf,0x85,0xcc,0x88,0x00,0xcf,0x86,0xd5,
+ 0x94,0xd4,0x3c,0xd3,0x13,0x92,0x0f,0x51,0x04,0x01,0x00,0x10,0x07,0x01,0xff,0xcf,
+ 0x83,0x00,0x01,0x00,0x01,0x00,0xd2,0x07,0x61,0x3a,0x43,0x01,0x00,0xd1,0x12,0x10,
+ 0x09,0x01,0xff,0xce,0xbf,0xcc,0x81,0x00,0x01,0xff,0xcf,0x85,0xcc,0x81,0x00,0x10,
+ 0x09,0x01,0xff,0xcf,0x89,0xcc,0x81,0x00,0x0a,0xff,0xcf,0x97,0x00,0xd3,0x2c,0xd2,
+ 0x11,0xe1,0x46,0x43,0x10,0x07,0x01,0xff,0xce,0xb2,0x00,0x01,0xff,0xce,0xb8,0x00,
+ 0xd1,0x10,0x10,0x09,0x01,0xff,0xcf,0x92,0xcc,0x88,0x00,0x01,0xff,0xcf,0x86,0x00,
+ 0x10,0x07,0x01,0xff,0xcf,0x80,0x00,0x04,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x06,
+ 0xff,0xcf,0x99,0x00,0x06,0x00,0x10,0x07,0x01,0xff,0xcf,0x9b,0x00,0x04,0x00,0xd1,
+ 0x0b,0x10,0x07,0x01,0xff,0xcf,0x9d,0x00,0x04,0x00,0x10,0x07,0x01,0xff,0xcf,0x9f,
+ 0x00,0x04,0x00,0xd4,0x58,0xd3,0x2c,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xcf,
+ 0xa1,0x00,0x04,0x00,0x10,0x07,0x01,0xff,0xcf,0xa3,0x00,0x01,0x00,0xd1,0x0b,0x10,
+ 0x07,0x01,0xff,0xcf,0xa5,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xcf,0xa7,0x00,0x01,
+ 0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xcf,0xa9,0x00,0x01,0x00,0x10,0x07,
+ 0x01,0xff,0xcf,0xab,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xcf,0xad,0x00,
+ 0x01,0x00,0x10,0x07,0x01,0xff,0xcf,0xaf,0x00,0x01,0x00,0xd3,0x2b,0xd2,0x12,0x91,
+ 0x0e,0x10,0x07,0x01,0xff,0xce,0xba,0x00,0x01,0xff,0xcf,0x81,0x00,0x01,0x00,0xd1,
+ 0x0e,0x10,0x07,0x05,0xff,0xce,0xb8,0x00,0x05,0xff,0xce,0xb5,0x00,0x10,0x04,0x06,
+ 0x00,0x07,0xff,0xcf,0xb8,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x04,0x07,0x00,0x07,0xff,
+ 0xcf,0xb2,0x00,0x10,0x07,0x07,0xff,0xcf,0xbb,0x00,0x07,0x00,0xd1,0x0b,0x10,0x04,
+ 0x08,0x00,0x08,0xff,0xcd,0xbb,0x00,0x10,0x07,0x08,0xff,0xcd,0xbc,0x00,0x08,0xff,
+ 0xcd,0xbd,0x00,0xe3,0xed,0x46,0xe2,0x3d,0x05,0xe1,0x27,0x02,0xe0,0x66,0x01,0xcf,
+ 0x86,0xd5,0xf0,0xd4,0x7e,0xd3,0x40,0xd2,0x22,0xd1,0x12,0x10,0x09,0x04,0xff,0xd0,
+ 0xb5,0xcc,0x80,0x00,0x01,0xff,0xd0,0xb5,0xcc,0x88,0x00,0x10,0x07,0x01,0xff,0xd1,
+ 0x92,0x00,0x01,0xff,0xd0,0xb3,0xcc,0x81,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd1,
+ 0x94,0x00,0x01,0xff,0xd1,0x95,0x00,0x10,0x07,0x01,0xff,0xd1,0x96,0x00,0x01,0xff,
+ 0xd1,0x96,0xcc,0x88,0x00,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd1,0x98,0x00,
+ 0x01,0xff,0xd1,0x99,0x00,0x10,0x07,0x01,0xff,0xd1,0x9a,0x00,0x01,0xff,0xd1,0x9b,
+ 0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xd0,0xba,0xcc,0x81,0x00,0x04,0xff,0xd0,0xb8,
+ 0xcc,0x80,0x00,0x10,0x09,0x01,0xff,0xd1,0x83,0xcc,0x86,0x00,0x01,0xff,0xd1,0x9f,
+ 0x00,0xd3,0x38,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd0,0xb0,0x00,0x01,0xff,
+ 0xd0,0xb1,0x00,0x10,0x07,0x01,0xff,0xd0,0xb2,0x00,0x01,0xff,0xd0,0xb3,0x00,0xd1,
+ 0x0e,0x10,0x07,0x01,0xff,0xd0,0xb4,0x00,0x01,0xff,0xd0,0xb5,0x00,0x10,0x07,0x01,
+ 0xff,0xd0,0xb6,0x00,0x01,0xff,0xd0,0xb7,0x00,0xd2,0x1e,0xd1,0x10,0x10,0x07,0x01,
+ 0xff,0xd0,0xb8,0x00,0x01,0xff,0xd0,0xb8,0xcc,0x86,0x00,0x10,0x07,0x01,0xff,0xd0,
+ 0xba,0x00,0x01,0xff,0xd0,0xbb,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd0,0xbc,0x00,
+ 0x01,0xff,0xd0,0xbd,0x00,0x10,0x07,0x01,0xff,0xd0,0xbe,0x00,0x01,0xff,0xd0,0xbf,
+ 0x00,0xe4,0x25,0x42,0xd3,0x38,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd1,0x80,
+ 0x00,0x01,0xff,0xd1,0x81,0x00,0x10,0x07,0x01,0xff,0xd1,0x82,0x00,0x01,0xff,0xd1,
+ 0x83,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd1,0x84,0x00,0x01,0xff,0xd1,0x85,0x00,
+ 0x10,0x07,0x01,0xff,0xd1,0x86,0x00,0x01,0xff,0xd1,0x87,0x00,0xd2,0x1c,0xd1,0x0e,
+ 0x10,0x07,0x01,0xff,0xd1,0x88,0x00,0x01,0xff,0xd1,0x89,0x00,0x10,0x07,0x01,0xff,
+ 0xd1,0x8a,0x00,0x01,0xff,0xd1,0x8b,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd1,0x8c,
+ 0x00,0x01,0xff,0xd1,0x8d,0x00,0x10,0x07,0x01,0xff,0xd1,0x8e,0x00,0x01,0xff,0xd1,
+ 0x8f,0x00,0xcf,0x86,0xd5,0x07,0x64,0xcf,0x41,0x01,0x00,0xd4,0x58,0xd3,0x2c,0xd2,
+ 0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd1,0xa1,0x00,0x01,0x00,0x10,0x07,0x01,0xff,
+ 0xd1,0xa3,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd1,0xa5,0x00,0x01,0x00,
+ 0x10,0x07,0x01,0xff,0xd1,0xa7,0x00,0x01,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,
+ 0xff,0xd1,0xa9,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd1,0xab,0x00,0x01,0x00,0xd1,
+ 0x0b,0x10,0x07,0x01,0xff,0xd1,0xad,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd1,0xaf,
+ 0x00,0x01,0x00,0xd3,0x33,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd1,0xb1,0x00,
+ 0x01,0x00,0x10,0x07,0x01,0xff,0xd1,0xb3,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,
+ 0xff,0xd1,0xb5,0x00,0x01,0x00,0x10,0x09,0x01,0xff,0xd1,0xb5,0xcc,0x8f,0x00,0x01,
+ 0xff,0xd1,0xb5,0xcc,0x8f,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd1,0xb9,
+ 0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd1,0xbb,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,
+ 0x01,0xff,0xd1,0xbd,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd1,0xbf,0x00,0x01,0x00,
+ 0xe0,0x41,0x01,0xcf,0x86,0xd5,0x8e,0xd4,0x36,0xd3,0x11,0xe2,0x91,0x41,0xe1,0x88,
+ 0x41,0x10,0x07,0x01,0xff,0xd2,0x81,0x00,0x01,0x00,0xd2,0x0f,0x51,0x04,0x04,0x00,
+ 0x10,0x07,0x06,0xff,0xd2,0x8b,0x00,0x06,0x00,0xd1,0x0b,0x10,0x07,0x04,0xff,0xd2,
+ 0x8d,0x00,0x04,0x00,0x10,0x07,0x04,0xff,0xd2,0x8f,0x00,0x04,0x00,0xd3,0x2c,0xd2,
+ 0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,0x91,0x00,0x01,0x00,0x10,0x07,0x01,0xff,
+ 0xd2,0x93,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,0x95,0x00,0x01,0x00,
+ 0x10,0x07,0x01,0xff,0xd2,0x97,0x00,0x01,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,
+ 0xff,0xd2,0x99,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0x9b,0x00,0x01,0x00,0xd1,
+ 0x0b,0x10,0x07,0x01,0xff,0xd2,0x9d,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0x9f,
+ 0x00,0x01,0x00,0xd4,0x58,0xd3,0x2c,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,
+ 0xa1,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0xa3,0x00,0x01,0x00,0xd1,0x0b,0x10,
+ 0x07,0x01,0xff,0xd2,0xa5,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0xa7,0x00,0x01,
+ 0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,0xa9,0x00,0x01,0x00,0x10,0x07,
+ 0x01,0xff,0xd2,0xab,0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,0xad,0x00,
+ 0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0xaf,0x00,0x01,0x00,0xd3,0x2c,0xd2,0x16,0xd1,
+ 0x0b,0x10,0x07,0x01,0xff,0xd2,0xb1,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0xb3,
+ 0x00,0x01,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,0xb5,0x00,0x01,0x00,0x10,0x07,
+ 0x01,0xff,0xd2,0xb7,0x00,0x01,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd2,
+ 0xb9,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0xbb,0x00,0x01,0x00,0xd1,0x0b,0x10,
+ 0x07,0x01,0xff,0xd2,0xbd,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xd2,0xbf,0x00,0x01,
+ 0x00,0xcf,0x86,0xd5,0xdc,0xd4,0x5a,0xd3,0x36,0xd2,0x20,0xd1,0x10,0x10,0x07,0x01,
+ 0xff,0xd3,0x8f,0x00,0x01,0xff,0xd0,0xb6,0xcc,0x86,0x00,0x10,0x09,0x01,0xff,0xd0,
+ 0xb6,0xcc,0x86,0x00,0x01,0xff,0xd3,0x84,0x00,0xd1,0x0b,0x10,0x04,0x01,0x00,0x06,
+ 0xff,0xd3,0x86,0x00,0x10,0x04,0x06,0x00,0x01,0xff,0xd3,0x88,0x00,0xd2,0x16,0xd1,
+ 0x0b,0x10,0x04,0x01,0x00,0x06,0xff,0xd3,0x8a,0x00,0x10,0x04,0x06,0x00,0x01,0xff,
+ 0xd3,0x8c,0x00,0xe1,0x69,0x40,0x10,0x04,0x01,0x00,0x06,0xff,0xd3,0x8e,0x00,0xd3,
+ 0x41,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xd0,0xb0,0xcc,0x86,0x00,0x01,0xff,
+ 0xd0,0xb0,0xcc,0x86,0x00,0x10,0x09,0x01,0xff,0xd0,0xb0,0xcc,0x88,0x00,0x01,0xff,
+ 0xd0,0xb0,0xcc,0x88,0x00,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd3,0x95,0x00,0x01,0x00,
+ 0x10,0x09,0x01,0xff,0xd0,0xb5,0xcc,0x86,0x00,0x01,0xff,0xd0,0xb5,0xcc,0x86,0x00,
+ 0xd2,0x1d,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd3,0x99,0x00,0x01,0x00,0x10,0x09,0x01,
+ 0xff,0xd3,0x99,0xcc,0x88,0x00,0x01,0xff,0xd3,0x99,0xcc,0x88,0x00,0xd1,0x12,0x10,
+ 0x09,0x01,0xff,0xd0,0xb6,0xcc,0x88,0x00,0x01,0xff,0xd0,0xb6,0xcc,0x88,0x00,0x10,
+ 0x09,0x01,0xff,0xd0,0xb7,0xcc,0x88,0x00,0x01,0xff,0xd0,0xb7,0xcc,0x88,0x00,0xd4,
+ 0x82,0xd3,0x41,0xd2,0x1d,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd3,0xa1,0x00,0x01,0x00,
+ 0x10,0x09,0x01,0xff,0xd0,0xb8,0xcc,0x84,0x00,0x01,0xff,0xd0,0xb8,0xcc,0x84,0x00,
+ 0xd1,0x12,0x10,0x09,0x01,0xff,0xd0,0xb8,0xcc,0x88,0x00,0x01,0xff,0xd0,0xb8,0xcc,
+ 0x88,0x00,0x10,0x09,0x01,0xff,0xd0,0xbe,0xcc,0x88,0x00,0x01,0xff,0xd0,0xbe,0xcc,
+ 0x88,0x00,0xd2,0x1d,0xd1,0x0b,0x10,0x07,0x01,0xff,0xd3,0xa9,0x00,0x01,0x00,0x10,
+ 0x09,0x01,0xff,0xd3,0xa9,0xcc,0x88,0x00,0x01,0xff,0xd3,0xa9,0xcc,0x88,0x00,0xd1,
+ 0x12,0x10,0x09,0x04,0xff,0xd1,0x8d,0xcc,0x88,0x00,0x04,0xff,0xd1,0x8d,0xcc,0x88,
+ 0x00,0x10,0x09,0x01,0xff,0xd1,0x83,0xcc,0x84,0x00,0x01,0xff,0xd1,0x83,0xcc,0x84,
+ 0x00,0xd3,0x41,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xd1,0x83,0xcc,0x88,0x00,
+ 0x01,0xff,0xd1,0x83,0xcc,0x88,0x00,0x10,0x09,0x01,0xff,0xd1,0x83,0xcc,0x8b,0x00,
+ 0x01,0xff,0xd1,0x83,0xcc,0x8b,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xd1,0x87,0xcc,
+ 0x88,0x00,0x01,0xff,0xd1,0x87,0xcc,0x88,0x00,0x10,0x07,0x08,0xff,0xd3,0xb7,0x00,
+ 0x08,0x00,0xd2,0x1d,0xd1,0x12,0x10,0x09,0x01,0xff,0xd1,0x8b,0xcc,0x88,0x00,0x01,
+ 0xff,0xd1,0x8b,0xcc,0x88,0x00,0x10,0x07,0x09,0xff,0xd3,0xbb,0x00,0x09,0x00,0xd1,
+ 0x0b,0x10,0x07,0x09,0xff,0xd3,0xbd,0x00,0x09,0x00,0x10,0x07,0x09,0xff,0xd3,0xbf,
+ 0x00,0x09,0x00,0xe1,0x26,0x02,0xe0,0x78,0x01,0xcf,0x86,0xd5,0xb0,0xd4,0x58,0xd3,
+ 0x2c,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x06,0xff,0xd4,0x81,0x00,0x06,0x00,0x10,0x07,
+ 0x06,0xff,0xd4,0x83,0x00,0x06,0x00,0xd1,0x0b,0x10,0x07,0x06,0xff,0xd4,0x85,0x00,
+ 0x06,0x00,0x10,0x07,0x06,0xff,0xd4,0x87,0x00,0x06,0x00,0xd2,0x16,0xd1,0x0b,0x10,
+ 0x07,0x06,0xff,0xd4,0x89,0x00,0x06,0x00,0x10,0x07,0x06,0xff,0xd4,0x8b,0x00,0x06,
+ 0x00,0xd1,0x0b,0x10,0x07,0x06,0xff,0xd4,0x8d,0x00,0x06,0x00,0x10,0x07,0x06,0xff,
+ 0xd4,0x8f,0x00,0x06,0x00,0xd3,0x2c,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x09,0xff,0xd4,
+ 0x91,0x00,0x09,0x00,0x10,0x07,0x09,0xff,0xd4,0x93,0x00,0x09,0x00,0xd1,0x0b,0x10,
+ 0x07,0x0a,0xff,0xd4,0x95,0x00,0x0a,0x00,0x10,0x07,0x0a,0xff,0xd4,0x97,0x00,0x0a,
+ 0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x0a,0xff,0xd4,0x99,0x00,0x0a,0x00,0x10,0x07,
+ 0x0a,0xff,0xd4,0x9b,0x00,0x0a,0x00,0xd1,0x0b,0x10,0x07,0x0a,0xff,0xd4,0x9d,0x00,
+ 0x0a,0x00,0x10,0x07,0x0a,0xff,0xd4,0x9f,0x00,0x0a,0x00,0xd4,0x58,0xd3,0x2c,0xd2,
+ 0x16,0xd1,0x0b,0x10,0x07,0x0a,0xff,0xd4,0xa1,0x00,0x0a,0x00,0x10,0x07,0x0a,0xff,
+ 0xd4,0xa3,0x00,0x0a,0x00,0xd1,0x0b,0x10,0x07,0x0b,0xff,0xd4,0xa5,0x00,0x0b,0x00,
+ 0x10,0x07,0x0c,0xff,0xd4,0xa7,0x00,0x0c,0x00,0xd2,0x16,0xd1,0x0b,0x10,0x07,0x10,
+ 0xff,0xd4,0xa9,0x00,0x10,0x00,0x10,0x07,0x10,0xff,0xd4,0xab,0x00,0x10,0x00,0xd1,
+ 0x0b,0x10,0x07,0x10,0xff,0xd4,0xad,0x00,0x10,0x00,0x10,0x07,0x10,0xff,0xd4,0xaf,
+ 0x00,0x10,0x00,0xd3,0x35,0xd2,0x19,0xd1,0x0b,0x10,0x04,0x00,0x00,0x01,0xff,0xd5,
+ 0xa1,0x00,0x10,0x07,0x01,0xff,0xd5,0xa2,0x00,0x01,0xff,0xd5,0xa3,0x00,0xd1,0x0e,
+ 0x10,0x07,0x01,0xff,0xd5,0xa4,0x00,0x01,0xff,0xd5,0xa5,0x00,0x10,0x07,0x01,0xff,
+ 0xd5,0xa6,0x00,0x01,0xff,0xd5,0xa7,0x00,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x01,0xff,
+ 0xd5,0xa8,0x00,0x01,0xff,0xd5,0xa9,0x00,0x10,0x07,0x01,0xff,0xd5,0xaa,0x00,0x01,
+ 0xff,0xd5,0xab,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd5,0xac,0x00,0x01,0xff,0xd5,
+ 0xad,0x00,0x10,0x07,0x01,0xff,0xd5,0xae,0x00,0x01,0xff,0xd5,0xaf,0x00,0xcf,0x86,
+ 0xe5,0x08,0x3f,0xd4,0x70,0xd3,0x38,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd5,
+ 0xb0,0x00,0x01,0xff,0xd5,0xb1,0x00,0x10,0x07,0x01,0xff,0xd5,0xb2,0x00,0x01,0xff,
+ 0xd5,0xb3,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd5,0xb4,0x00,0x01,0xff,0xd5,0xb5,
+ 0x00,0x10,0x07,0x01,0xff,0xd5,0xb6,0x00,0x01,0xff,0xd5,0xb7,0x00,0xd2,0x1c,0xd1,
+ 0x0e,0x10,0x07,0x01,0xff,0xd5,0xb8,0x00,0x01,0xff,0xd5,0xb9,0x00,0x10,0x07,0x01,
+ 0xff,0xd5,0xba,0x00,0x01,0xff,0xd5,0xbb,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd5,
+ 0xbc,0x00,0x01,0xff,0xd5,0xbd,0x00,0x10,0x07,0x01,0xff,0xd5,0xbe,0x00,0x01,0xff,
+ 0xd5,0xbf,0x00,0xe3,0x87,0x3e,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd6,0x80,
+ 0x00,0x01,0xff,0xd6,0x81,0x00,0x10,0x07,0x01,0xff,0xd6,0x82,0x00,0x01,0xff,0xd6,
+ 0x83,0x00,0xd1,0x0e,0x10,0x07,0x01,0xff,0xd6,0x84,0x00,0x01,0xff,0xd6,0x85,0x00,
+ 0x10,0x07,0x01,0xff,0xd6,0x86,0x00,0x00,0x00,0xe0,0x2f,0x3f,0xcf,0x86,0xe5,0xc0,
+ 0x3e,0xe4,0x97,0x3e,0xe3,0x76,0x3e,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x01,0xff,0xd5,0xa5,0xd6,0x82,0x00,0xe4,0x3e,0x25,0xe3,0xc3,0x1a,
+ 0xe2,0x7b,0x81,0xe1,0xc0,0x13,0xd0,0x1e,0xcf,0x86,0xc5,0xe4,0x08,0x4b,0xe3,0x53,
+ 0x46,0xe2,0xe9,0x43,0xe1,0x1c,0x43,0xe0,0xe1,0x42,0xcf,0x86,0xe5,0xa6,0x42,0x64,
+ 0x89,0x42,0x0b,0x00,0xcf,0x86,0xe5,0xfa,0x01,0xe4,0x03,0x56,0xe3,0x76,0x01,0xe2,
+ 0x8e,0x53,0xd1,0x0c,0xe0,0xef,0x52,0xcf,0x86,0x65,0x8d,0x52,0x04,0x00,0xe0,0x0d,
+ 0x01,0xcf,0x86,0xd5,0x0a,0xe4,0x10,0x53,0x63,0xff,0x52,0x0a,0x00,0xd4,0x80,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x80,0x00,0x01,0xff,0xe2,
+ 0xb4,0x81,0x00,0x10,0x08,0x01,0xff,0xe2,0xb4,0x82,0x00,0x01,0xff,0xe2,0xb4,0x83,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x84,0x00,0x01,0xff,0xe2,0xb4,0x85,
+ 0x00,0x10,0x08,0x01,0xff,0xe2,0xb4,0x86,0x00,0x01,0xff,0xe2,0xb4,0x87,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x88,0x00,0x01,0xff,0xe2,0xb4,0x89,
+ 0x00,0x10,0x08,0x01,0xff,0xe2,0xb4,0x8a,0x00,0x01,0xff,0xe2,0xb4,0x8b,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x8c,0x00,0x01,0xff,0xe2,0xb4,0x8d,0x00,0x10,
+ 0x08,0x01,0xff,0xe2,0xb4,0x8e,0x00,0x01,0xff,0xe2,0xb4,0x8f,0x00,0xd3,0x40,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x90,0x00,0x01,0xff,0xe2,0xb4,0x91,
+ 0x00,0x10,0x08,0x01,0xff,0xe2,0xb4,0x92,0x00,0x01,0xff,0xe2,0xb4,0x93,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x94,0x00,0x01,0xff,0xe2,0xb4,0x95,0x00,0x10,
+ 0x08,0x01,0xff,0xe2,0xb4,0x96,0x00,0x01,0xff,0xe2,0xb4,0x97,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0x98,0x00,0x01,0xff,0xe2,0xb4,0x99,0x00,0x10,
+ 0x08,0x01,0xff,0xe2,0xb4,0x9a,0x00,0x01,0xff,0xe2,0xb4,0x9b,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe2,0xb4,0x9c,0x00,0x01,0xff,0xe2,0xb4,0x9d,0x00,0x10,0x08,0x01,
+ 0xff,0xe2,0xb4,0x9e,0x00,0x01,0xff,0xe2,0xb4,0x9f,0x00,0xcf,0x86,0xe5,0x42,0x52,
+ 0x94,0x50,0xd3,0x3c,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0xa0,0x00,
+ 0x01,0xff,0xe2,0xb4,0xa1,0x00,0x10,0x08,0x01,0xff,0xe2,0xb4,0xa2,0x00,0x01,0xff,
+ 0xe2,0xb4,0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0xb4,0xa4,0x00,0x01,0xff,
+ 0xe2,0xb4,0xa5,0x00,0x10,0x04,0x00,0x00,0x0d,0xff,0xe2,0xb4,0xa7,0x00,0x52,0x04,
+ 0x00,0x00,0x91,0x0c,0x10,0x04,0x00,0x00,0x0d,0xff,0xe2,0xb4,0xad,0x00,0x00,0x00,
+ 0x01,0x00,0xd2,0x1b,0xe1,0xfc,0x52,0xe0,0xad,0x52,0xcf,0x86,0x95,0x0f,0x94,0x0b,
+ 0x93,0x07,0x62,0x92,0x52,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0xd1,0x13,0xe0,
+ 0xd3,0x53,0xcf,0x86,0x95,0x0a,0xe4,0xa8,0x53,0x63,0x97,0x53,0x04,0x00,0x04,0x00,
+ 0xd0,0x0d,0xcf,0x86,0x95,0x07,0x64,0x22,0x54,0x08,0x00,0x04,0x00,0xcf,0x86,0x55,
+ 0x04,0x04,0x00,0x54,0x04,0x04,0x00,0xd3,0x07,0x62,0x2f,0x54,0x04,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8f,0xb0,0x00,0x11,0xff,0xe1,0x8f,0xb1,0x00,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0xb2,0x00,0x11,0xff,0xe1,0x8f,0xb3,0x00,0x91,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0xb4,0x00,0x11,0xff,0xe1,0x8f,0xb5,0x00,0x00,0x00,
+ 0xd4,0x1c,0xe3,0xe0,0x56,0xe2,0x17,0x56,0xe1,0xda,0x55,0xe0,0xbb,0x55,0xcf,0x86,
+ 0x95,0x0a,0xe4,0xa4,0x55,0x63,0x88,0x55,0x04,0x00,0x04,0x00,0xe3,0xd2,0x01,0xe2,
+ 0x2b,0x5a,0xd1,0x0c,0xe0,0x4c,0x59,0xcf,0x86,0x65,0x25,0x59,0x0a,0x00,0xe0,0x9c,
+ 0x59,0xcf,0x86,0xd5,0xc5,0xd4,0x45,0xd3,0x31,0xd2,0x1c,0xd1,0x0e,0x10,0x07,0x12,
+ 0xff,0xd0,0xb2,0x00,0x12,0xff,0xd0,0xb4,0x00,0x10,0x07,0x12,0xff,0xd0,0xbe,0x00,
+ 0x12,0xff,0xd1,0x81,0x00,0x51,0x07,0x12,0xff,0xd1,0x82,0x00,0x10,0x07,0x12,0xff,
+ 0xd1,0x8a,0x00,0x12,0xff,0xd1,0xa3,0x00,0x92,0x10,0x91,0x0c,0x10,0x08,0x12,0xff,
+ 0xea,0x99,0x8b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x14,0xff,0xe1,0x83,0x90,0x00,0x14,0xff,0xe1,0x83,0x91,0x00,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0x92,0x00,0x14,0xff,0xe1,0x83,0x93,0x00,0xd1,0x10,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0x94,0x00,0x14,0xff,0xe1,0x83,0x95,0x00,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0x96,0x00,0x14,0xff,0xe1,0x83,0x97,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0x98,0x00,0x14,0xff,0xe1,0x83,0x99,0x00,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0x9a,0x00,0x14,0xff,0xe1,0x83,0x9b,0x00,0xd1,0x10,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0x9c,0x00,0x14,0xff,0xe1,0x83,0x9d,0x00,0x10,0x08,0x14,0xff,0xe1,0x83,
+ 0x9e,0x00,0x14,0xff,0xe1,0x83,0x9f,0x00,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x14,0xff,0xe1,0x83,0xa0,0x00,0x14,0xff,0xe1,0x83,0xa1,0x00,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0xa2,0x00,0x14,0xff,0xe1,0x83,0xa3,0x00,0xd1,0x10,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0xa4,0x00,0x14,0xff,0xe1,0x83,0xa5,0x00,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0xa6,0x00,0x14,0xff,0xe1,0x83,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0xa8,0x00,0x14,0xff,0xe1,0x83,0xa9,0x00,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0xaa,0x00,0x14,0xff,0xe1,0x83,0xab,0x00,0xd1,0x10,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0xac,0x00,0x14,0xff,0xe1,0x83,0xad,0x00,0x10,0x08,0x14,0xff,0xe1,0x83,
+ 0xae,0x00,0x14,0xff,0xe1,0x83,0xaf,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x14,0xff,0xe1,0x83,0xb0,0x00,0x14,0xff,0xe1,0x83,0xb1,0x00,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0xb2,0x00,0x14,0xff,0xe1,0x83,0xb3,0x00,0xd1,0x10,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0xb4,0x00,0x14,0xff,0xe1,0x83,0xb5,0x00,0x10,0x08,0x14,0xff,0xe1,0x83,
+ 0xb6,0x00,0x14,0xff,0xe1,0x83,0xb7,0x00,0xd2,0x1c,0xd1,0x10,0x10,0x08,0x14,0xff,
+ 0xe1,0x83,0xb8,0x00,0x14,0xff,0xe1,0x83,0xb9,0x00,0x10,0x08,0x14,0xff,0xe1,0x83,
+ 0xba,0x00,0x00,0x00,0xd1,0x0c,0x10,0x04,0x00,0x00,0x14,0xff,0xe1,0x83,0xbd,0x00,
+ 0x10,0x08,0x14,0xff,0xe1,0x83,0xbe,0x00,0x14,0xff,0xe1,0x83,0xbf,0x00,0xe2,0x9d,
+ 0x08,0xe1,0x48,0x04,0xe0,0x1c,0x02,0xcf,0x86,0xe5,0x11,0x01,0xd4,0x84,0xd3,0x40,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x61,0xcc,0xa5,0x00,0x01,0xff,0x61,0xcc,
+ 0xa5,0x00,0x10,0x08,0x01,0xff,0x62,0xcc,0x87,0x00,0x01,0xff,0x62,0xcc,0x87,0x00,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x62,0xcc,0xa3,0x00,0x01,0xff,0x62,0xcc,0xa3,0x00,
+ 0x10,0x08,0x01,0xff,0x62,0xcc,0xb1,0x00,0x01,0xff,0x62,0xcc,0xb1,0x00,0xd2,0x24,
+ 0xd1,0x14,0x10,0x0a,0x01,0xff,0x63,0xcc,0xa7,0xcc,0x81,0x00,0x01,0xff,0x63,0xcc,
+ 0xa7,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x64,0xcc,0x87,0x00,0x01,0xff,0x64,0xcc,
+ 0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x64,0xcc,0xa3,0x00,0x01,0xff,0x64,0xcc,
+ 0xa3,0x00,0x10,0x08,0x01,0xff,0x64,0xcc,0xb1,0x00,0x01,0xff,0x64,0xcc,0xb1,0x00,
+ 0xd3,0x48,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x64,0xcc,0xa7,0x00,0x01,0xff,
+ 0x64,0xcc,0xa7,0x00,0x10,0x08,0x01,0xff,0x64,0xcc,0xad,0x00,0x01,0xff,0x64,0xcc,
+ 0xad,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x65,0xcc,0x84,0xcc,0x80,0x00,0x01,0xff,
+ 0x65,0xcc,0x84,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x65,0xcc,0x84,0xcc,0x81,0x00,
+ 0x01,0xff,0x65,0xcc,0x84,0xcc,0x81,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0x65,0xcc,0xad,0x00,0x01,0xff,0x65,0xcc,0xad,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,
+ 0xb0,0x00,0x01,0xff,0x65,0xcc,0xb0,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x65,0xcc,
+ 0xa7,0xcc,0x86,0x00,0x01,0xff,0x65,0xcc,0xa7,0xcc,0x86,0x00,0x10,0x08,0x01,0xff,
+ 0x66,0xcc,0x87,0x00,0x01,0xff,0x66,0xcc,0x87,0x00,0xd4,0x84,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x67,0xcc,0x84,0x00,0x01,0xff,0x67,0xcc,0x84,0x00,
+ 0x10,0x08,0x01,0xff,0x68,0xcc,0x87,0x00,0x01,0xff,0x68,0xcc,0x87,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0x68,0xcc,0xa3,0x00,0x01,0xff,0x68,0xcc,0xa3,0x00,0x10,0x08,
+ 0x01,0xff,0x68,0xcc,0x88,0x00,0x01,0xff,0x68,0xcc,0x88,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0x68,0xcc,0xa7,0x00,0x01,0xff,0x68,0xcc,0xa7,0x00,0x10,0x08,
+ 0x01,0xff,0x68,0xcc,0xae,0x00,0x01,0xff,0x68,0xcc,0xae,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0x69,0xcc,0xb0,0x00,0x01,0xff,0x69,0xcc,0xb0,0x00,0x10,0x0a,0x01,0xff,
+ 0x69,0xcc,0x88,0xcc,0x81,0x00,0x01,0xff,0x69,0xcc,0x88,0xcc,0x81,0x00,0xd3,0x40,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x6b,0xcc,0x81,0x00,0x01,0xff,0x6b,0xcc,
+ 0x81,0x00,0x10,0x08,0x01,0xff,0x6b,0xcc,0xa3,0x00,0x01,0xff,0x6b,0xcc,0xa3,0x00,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x6b,0xcc,0xb1,0x00,0x01,0xff,0x6b,0xcc,0xb1,0x00,
+ 0x10,0x08,0x01,0xff,0x6c,0xcc,0xa3,0x00,0x01,0xff,0x6c,0xcc,0xa3,0x00,0xd2,0x24,
+ 0xd1,0x14,0x10,0x0a,0x01,0xff,0x6c,0xcc,0xa3,0xcc,0x84,0x00,0x01,0xff,0x6c,0xcc,
+ 0xa3,0xcc,0x84,0x00,0x10,0x08,0x01,0xff,0x6c,0xcc,0xb1,0x00,0x01,0xff,0x6c,0xcc,
+ 0xb1,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6c,0xcc,0xad,0x00,0x01,0xff,0x6c,0xcc,
+ 0xad,0x00,0x10,0x08,0x01,0xff,0x6d,0xcc,0x81,0x00,0x01,0xff,0x6d,0xcc,0x81,0x00,
+ 0xcf,0x86,0xe5,0x15,0x01,0xd4,0x88,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x6d,0xcc,0x87,0x00,0x01,0xff,0x6d,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x6d,
+ 0xcc,0xa3,0x00,0x01,0xff,0x6d,0xcc,0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6e,
+ 0xcc,0x87,0x00,0x01,0xff,0x6e,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x6e,0xcc,0xa3,
+ 0x00,0x01,0xff,0x6e,0xcc,0xa3,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x6e,
+ 0xcc,0xb1,0x00,0x01,0xff,0x6e,0xcc,0xb1,0x00,0x10,0x08,0x01,0xff,0x6e,0xcc,0xad,
+ 0x00,0x01,0xff,0x6e,0xcc,0xad,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x83,
+ 0xcc,0x81,0x00,0x01,0xff,0x6f,0xcc,0x83,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x6f,
+ 0xcc,0x83,0xcc,0x88,0x00,0x01,0xff,0x6f,0xcc,0x83,0xcc,0x88,0x00,0xd3,0x48,0xd2,
+ 0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x84,0xcc,0x80,0x00,0x01,0xff,0x6f,
+ 0xcc,0x84,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x84,0xcc,0x81,0x00,0x01,
+ 0xff,0x6f,0xcc,0x84,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x70,0xcc,0x81,
+ 0x00,0x01,0xff,0x70,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x70,0xcc,0x87,0x00,0x01,
+ 0xff,0x70,0xcc,0x87,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x72,0xcc,0x87,
+ 0x00,0x01,0xff,0x72,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x72,0xcc,0xa3,0x00,0x01,
+ 0xff,0x72,0xcc,0xa3,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x72,0xcc,0xa3,0xcc,0x84,
+ 0x00,0x01,0xff,0x72,0xcc,0xa3,0xcc,0x84,0x00,0x10,0x08,0x01,0xff,0x72,0xcc,0xb1,
+ 0x00,0x01,0xff,0x72,0xcc,0xb1,0x00,0xd4,0x8c,0xd3,0x48,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x73,0xcc,0x87,0x00,0x01,0xff,0x73,0xcc,0x87,0x00,0x10,0x08,0x01,
+ 0xff,0x73,0xcc,0xa3,0x00,0x01,0xff,0x73,0xcc,0xa3,0x00,0xd1,0x14,0x10,0x0a,0x01,
+ 0xff,0x73,0xcc,0x81,0xcc,0x87,0x00,0x01,0xff,0x73,0xcc,0x81,0xcc,0x87,0x00,0x10,
+ 0x0a,0x01,0xff,0x73,0xcc,0x8c,0xcc,0x87,0x00,0x01,0xff,0x73,0xcc,0x8c,0xcc,0x87,
+ 0x00,0xd2,0x24,0xd1,0x14,0x10,0x0a,0x01,0xff,0x73,0xcc,0xa3,0xcc,0x87,0x00,0x01,
+ 0xff,0x73,0xcc,0xa3,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x74,0xcc,0x87,0x00,0x01,
+ 0xff,0x74,0xcc,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x74,0xcc,0xa3,0x00,0x01,
+ 0xff,0x74,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x74,0xcc,0xb1,0x00,0x01,0xff,0x74,
+ 0xcc,0xb1,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x74,0xcc,0xad,
+ 0x00,0x01,0xff,0x74,0xcc,0xad,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0xa4,0x00,0x01,
+ 0xff,0x75,0xcc,0xa4,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x75,0xcc,0xb0,0x00,0x01,
+ 0xff,0x75,0xcc,0xb0,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0xad,0x00,0x01,0xff,0x75,
+ 0xcc,0xad,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x75,0xcc,0x83,0xcc,0x81,
+ 0x00,0x01,0xff,0x75,0xcc,0x83,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x75,0xcc,0x84,
+ 0xcc,0x88,0x00,0x01,0xff,0x75,0xcc,0x84,0xcc,0x88,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x76,0xcc,0x83,0x00,0x01,0xff,0x76,0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x76,
+ 0xcc,0xa3,0x00,0x01,0xff,0x76,0xcc,0xa3,0x00,0xe0,0x11,0x02,0xcf,0x86,0xd5,0xe2,
+ 0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x77,0xcc,0x80,0x00,
+ 0x01,0xff,0x77,0xcc,0x80,0x00,0x10,0x08,0x01,0xff,0x77,0xcc,0x81,0x00,0x01,0xff,
+ 0x77,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x77,0xcc,0x88,0x00,0x01,0xff,
+ 0x77,0xcc,0x88,0x00,0x10,0x08,0x01,0xff,0x77,0xcc,0x87,0x00,0x01,0xff,0x77,0xcc,
+ 0x87,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x77,0xcc,0xa3,0x00,0x01,0xff,
+ 0x77,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x78,0xcc,0x87,0x00,0x01,0xff,0x78,0xcc,
+ 0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x78,0xcc,0x88,0x00,0x01,0xff,0x78,0xcc,
+ 0x88,0x00,0x10,0x08,0x01,0xff,0x79,0xcc,0x87,0x00,0x01,0xff,0x79,0xcc,0x87,0x00,
+ 0xd3,0x33,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x7a,0xcc,0x82,0x00,0x01,0xff,
+ 0x7a,0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x7a,0xcc,0xa3,0x00,0x01,0xff,0x7a,0xcc,
+ 0xa3,0x00,0xe1,0x12,0x59,0x10,0x08,0x01,0xff,0x7a,0xcc,0xb1,0x00,0x01,0xff,0x7a,
+ 0xcc,0xb1,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x77,0xcc,0x8a,0x00,0x01,
+ 0xff,0x79,0xcc,0x8a,0x00,0x10,0x08,0x01,0xff,0x61,0xca,0xbe,0x00,0x02,0xff,0x73,
+ 0xcc,0x87,0x00,0x51,0x04,0x0a,0x00,0x10,0x07,0x0a,0xff,0x73,0x73,0x00,0x0a,0x00,
+ 0xd4,0x98,0xd3,0x48,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x61,0xcc,0xa3,0x00,
+ 0x01,0xff,0x61,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x61,0xcc,0x89,0x00,0x01,0xff,
+ 0x61,0xcc,0x89,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x61,0xcc,0x82,0xcc,0x81,0x00,
+ 0x01,0xff,0x61,0xcc,0x82,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x61,0xcc,0x82,0xcc,
+ 0x80,0x00,0x01,0xff,0x61,0xcc,0x82,0xcc,0x80,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,
+ 0x01,0xff,0x61,0xcc,0x82,0xcc,0x89,0x00,0x01,0xff,0x61,0xcc,0x82,0xcc,0x89,0x00,
+ 0x10,0x0a,0x01,0xff,0x61,0xcc,0x82,0xcc,0x83,0x00,0x01,0xff,0x61,0xcc,0x82,0xcc,
+ 0x83,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x61,0xcc,0xa3,0xcc,0x82,0x00,0x01,0xff,
+ 0x61,0xcc,0xa3,0xcc,0x82,0x00,0x10,0x0a,0x01,0xff,0x61,0xcc,0x86,0xcc,0x81,0x00,
+ 0x01,0xff,0x61,0xcc,0x86,0xcc,0x81,0x00,0xd3,0x50,0xd2,0x28,0xd1,0x14,0x10,0x0a,
+ 0x01,0xff,0x61,0xcc,0x86,0xcc,0x80,0x00,0x01,0xff,0x61,0xcc,0x86,0xcc,0x80,0x00,
+ 0x10,0x0a,0x01,0xff,0x61,0xcc,0x86,0xcc,0x89,0x00,0x01,0xff,0x61,0xcc,0x86,0xcc,
+ 0x89,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x61,0xcc,0x86,0xcc,0x83,0x00,0x01,0xff,
+ 0x61,0xcc,0x86,0xcc,0x83,0x00,0x10,0x0a,0x01,0xff,0x61,0xcc,0xa3,0xcc,0x86,0x00,
+ 0x01,0xff,0x61,0xcc,0xa3,0xcc,0x86,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0x65,0xcc,0xa3,0x00,0x01,0xff,0x65,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,
+ 0x89,0x00,0x01,0xff,0x65,0xcc,0x89,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x65,0xcc,
+ 0x83,0x00,0x01,0xff,0x65,0xcc,0x83,0x00,0x10,0x0a,0x01,0xff,0x65,0xcc,0x82,0xcc,
+ 0x81,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x81,0x00,0xcf,0x86,0xe5,0x31,0x01,0xd4,
+ 0x90,0xd3,0x50,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x65,0xcc,0x82,0xcc,0x80,
+ 0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x65,0xcc,0x82,
+ 0xcc,0x89,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x89,0x00,0xd1,0x14,0x10,0x0a,0x01,
+ 0xff,0x65,0xcc,0x82,0xcc,0x83,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x83,0x00,0x10,
+ 0x0a,0x01,0xff,0x65,0xcc,0xa3,0xcc,0x82,0x00,0x01,0xff,0x65,0xcc,0xa3,0xcc,0x82,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x69,0xcc,0x89,0x00,0x01,0xff,0x69,
+ 0xcc,0x89,0x00,0x10,0x08,0x01,0xff,0x69,0xcc,0xa3,0x00,0x01,0xff,0x69,0xcc,0xa3,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0xa3,0x00,0x01,0xff,0x6f,0xcc,0xa3,
+ 0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x89,0x00,0x01,0xff,0x6f,0xcc,0x89,0x00,0xd3,
+ 0x50,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x81,0x00,0x01,
+ 0xff,0x6f,0xcc,0x82,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x80,
+ 0x00,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x80,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,
+ 0xcc,0x82,0xcc,0x89,0x00,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x89,0x00,0x10,0x0a,0x01,
+ 0xff,0x6f,0xcc,0x82,0xcc,0x83,0x00,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x83,0x00,0xd2,
+ 0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,0xcc,0xa3,0xcc,0x82,0x00,0x01,0xff,0x6f,
+ 0xcc,0xa3,0xcc,0x82,0x00,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x81,0x00,0x01,
+ 0xff,0x6f,0xcc,0x9b,0xcc,0x81,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x9b,
+ 0xcc,0x80,0x00,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x6f,
+ 0xcc,0x9b,0xcc,0x89,0x00,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x89,0x00,0xd4,0x98,0xd3,
+ 0x48,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x83,0x00,0x01,
+ 0xff,0x6f,0xcc,0x9b,0xcc,0x83,0x00,0x10,0x0a,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0xa3,
+ 0x00,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x75,
+ 0xcc,0xa3,0x00,0x01,0xff,0x75,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0x89,
+ 0x00,0x01,0xff,0x75,0xcc,0x89,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x75,
+ 0xcc,0x9b,0xcc,0x81,0x00,0x01,0xff,0x75,0xcc,0x9b,0xcc,0x81,0x00,0x10,0x0a,0x01,
+ 0xff,0x75,0xcc,0x9b,0xcc,0x80,0x00,0x01,0xff,0x75,0xcc,0x9b,0xcc,0x80,0x00,0xd1,
+ 0x14,0x10,0x0a,0x01,0xff,0x75,0xcc,0x9b,0xcc,0x89,0x00,0x01,0xff,0x75,0xcc,0x9b,
+ 0xcc,0x89,0x00,0x10,0x0a,0x01,0xff,0x75,0xcc,0x9b,0xcc,0x83,0x00,0x01,0xff,0x75,
+ 0xcc,0x9b,0xcc,0x83,0x00,0xd3,0x44,0xd2,0x24,0xd1,0x14,0x10,0x0a,0x01,0xff,0x75,
+ 0xcc,0x9b,0xcc,0xa3,0x00,0x01,0xff,0x75,0xcc,0x9b,0xcc,0xa3,0x00,0x10,0x08,0x01,
+ 0xff,0x79,0xcc,0x80,0x00,0x01,0xff,0x79,0xcc,0x80,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x79,0xcc,0xa3,0x00,0x01,0xff,0x79,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x79,
+ 0xcc,0x89,0x00,0x01,0xff,0x79,0xcc,0x89,0x00,0xd2,0x1c,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x79,0xcc,0x83,0x00,0x01,0xff,0x79,0xcc,0x83,0x00,0x10,0x08,0x0a,0xff,0xe1,
+ 0xbb,0xbb,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xe1,0xbb,0xbd,0x00,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xe1,0xbb,0xbf,0x00,0x0a,0x00,0xe1,0xbf,0x02,0xe0,0xa1,
+ 0x01,0xcf,0x86,0xd5,0xc6,0xd4,0x6c,0xd3,0x18,0xe2,0x0e,0x59,0xe1,0xf7,0x58,0x10,
+ 0x09,0x01,0xff,0xce,0xb1,0xcc,0x93,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0x00,0xd2,
+ 0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x93,0x00,0x01,0xff,0xce,0xb1,
+ 0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,
+ 0xce,0xb1,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,
+ 0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,
+ 0xff,0xce,0xb1,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcd,0x82,
+ 0x00,0xd3,0x18,0xe2,0x4a,0x59,0xe1,0x33,0x59,0x10,0x09,0x01,0xff,0xce,0xb5,0xcc,
+ 0x93,0x00,0x01,0xff,0xce,0xb5,0xcc,0x94,0x00,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,
+ 0xff,0xce,0xb5,0xcc,0x93,0x00,0x01,0xff,0xce,0xb5,0xcc,0x94,0x00,0x10,0x0b,0x01,
+ 0xff,0xce,0xb5,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0xb5,0xcc,0x94,0xcc,0x80,
+ 0x00,0x91,0x16,0x10,0x0b,0x01,0xff,0xce,0xb5,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,
+ 0xce,0xb5,0xcc,0x94,0xcc,0x81,0x00,0x00,0x00,0xd4,0x6c,0xd3,0x18,0xe2,0x74,0x59,
+ 0xe1,0x5d,0x59,0x10,0x09,0x01,0xff,0xce,0xb7,0xcc,0x93,0x00,0x01,0xff,0xce,0xb7,
+ 0xcc,0x94,0x00,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb7,0xcc,0x93,0x00,
+ 0x01,0xff,0xce,0xb7,0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,
+ 0x80,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,
+ 0xff,0xce,0xb7,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x81,
+ 0x00,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0xb7,
+ 0xcc,0x94,0xcd,0x82,0x00,0xd3,0x18,0xe2,0xb0,0x59,0xe1,0x99,0x59,0x10,0x09,0x01,
+ 0xff,0xce,0xb9,0xcc,0x93,0x00,0x01,0xff,0xce,0xb9,0xcc,0x94,0x00,0xd2,0x28,0xd1,
+ 0x12,0x10,0x09,0x01,0xff,0xce,0xb9,0xcc,0x93,0x00,0x01,0xff,0xce,0xb9,0xcc,0x94,
+ 0x00,0x10,0x0b,0x01,0xff,0xce,0xb9,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0xb9,
+ 0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb9,0xcc,0x93,0xcc,
+ 0x81,0x00,0x01,0xff,0xce,0xb9,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,0xce,
+ 0xb9,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0xb9,0xcc,0x94,0xcd,0x82,0x00,0xcf,
+ 0x86,0xd5,0xac,0xd4,0x5a,0xd3,0x18,0xe2,0xed,0x59,0xe1,0xd6,0x59,0x10,0x09,0x01,
+ 0xff,0xce,0xbf,0xcc,0x93,0x00,0x01,0xff,0xce,0xbf,0xcc,0x94,0x00,0xd2,0x28,0xd1,
+ 0x12,0x10,0x09,0x01,0xff,0xce,0xbf,0xcc,0x93,0x00,0x01,0xff,0xce,0xbf,0xcc,0x94,
+ 0x00,0x10,0x0b,0x01,0xff,0xce,0xbf,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0xbf,
+ 0xcc,0x94,0xcc,0x80,0x00,0x91,0x16,0x10,0x0b,0x01,0xff,0xce,0xbf,0xcc,0x93,0xcc,
+ 0x81,0x00,0x01,0xff,0xce,0xbf,0xcc,0x94,0xcc,0x81,0x00,0x00,0x00,0xd3,0x18,0xe2,
+ 0x17,0x5a,0xe1,0x00,0x5a,0x10,0x09,0x01,0xff,0xcf,0x85,0xcc,0x93,0x00,0x01,0xff,
+ 0xcf,0x85,0xcc,0x94,0x00,0xd2,0x1c,0xd1,0x0d,0x10,0x04,0x00,0x00,0x01,0xff,0xcf,
+ 0x85,0xcc,0x94,0x00,0x10,0x04,0x00,0x00,0x01,0xff,0xcf,0x85,0xcc,0x94,0xcc,0x80,
+ 0x00,0xd1,0x0f,0x10,0x04,0x00,0x00,0x01,0xff,0xcf,0x85,0xcc,0x94,0xcc,0x81,0x00,
+ 0x10,0x04,0x00,0x00,0x01,0xff,0xcf,0x85,0xcc,0x94,0xcd,0x82,0x00,0xe4,0xd3,0x5a,
+ 0xd3,0x18,0xe2,0x52,0x5a,0xe1,0x3b,0x5a,0x10,0x09,0x01,0xff,0xcf,0x89,0xcc,0x93,
+ 0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0x00,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,
+ 0xcf,0x89,0xcc,0x93,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,
+ 0xcf,0x89,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcc,0x80,0x00,
+ 0xd1,0x16,0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,0xcf,
+ 0x89,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcd,0x82,
+ 0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcd,0x82,0x00,0xe0,0xd9,0x02,0xcf,0x86,0xe5,
+ 0x91,0x01,0xd4,0xc8,0xd3,0x64,0xd2,0x30,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb1,
+ 0xcc,0x93,0xce,0xb9,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xce,0xb9,0x00,0x10,0x0d,
+ 0x01,0xff,0xce,0xb1,0xcc,0x93,0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xce,0xb1,0xcc,
+ 0x94,0xcc,0x80,0xce,0xb9,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,0xce,0xb1,0xcc,0x93,
+ 0xcc,0x81,0xce,0xb9,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcc,0x81,0xce,0xb9,0x00,
+ 0x10,0x0d,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcd,0x82,0xce,0xb9,0x00,0x01,0xff,0xce,
+ 0xb1,0xcc,0x94,0xcd,0x82,0xce,0xb9,0x00,0xd2,0x30,0xd1,0x16,0x10,0x0b,0x01,0xff,
+ 0xce,0xb1,0xcc,0x93,0xce,0xb9,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xce,0xb9,0x00,
+ 0x10,0x0d,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xce,
+ 0xb1,0xcc,0x94,0xcc,0x80,0xce,0xb9,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,0xce,0xb1,
+ 0xcc,0x93,0xcc,0x81,0xce,0xb9,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcc,0x81,0xce,
+ 0xb9,0x00,0x10,0x0d,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcd,0x82,0xce,0xb9,0x00,0x01,
+ 0xff,0xce,0xb1,0xcc,0x94,0xcd,0x82,0xce,0xb9,0x00,0xd3,0x64,0xd2,0x30,0xd1,0x16,
+ 0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xce,0xb9,0x00,0x01,0xff,0xce,0xb7,0xcc,
+ 0x94,0xce,0xb9,0x00,0x10,0x0d,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,0x80,0xce,0xb9,
+ 0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x80,0xce,0xb9,0x00,0xd1,0x1a,0x10,0x0d,
+ 0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,0x81,0xce,0xb9,0x00,0x01,0xff,0xce,0xb7,0xcc,
+ 0x94,0xcc,0x81,0xce,0xb9,0x00,0x10,0x0d,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcd,0x82,
+ 0xce,0xb9,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcd,0x82,0xce,0xb9,0x00,0xd2,0x30,
+ 0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xce,0xb9,0x00,0x01,0xff,0xce,
+ 0xb7,0xcc,0x94,0xce,0xb9,0x00,0x10,0x0d,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,0x80,
+ 0xce,0xb9,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x80,0xce,0xb9,0x00,0xd1,0x1a,
+ 0x10,0x0d,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,0x81,0xce,0xb9,0x00,0x01,0xff,0xce,
+ 0xb7,0xcc,0x94,0xcc,0x81,0xce,0xb9,0x00,0x10,0x0d,0x01,0xff,0xce,0xb7,0xcc,0x93,
+ 0xcd,0x82,0xce,0xb9,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcd,0x82,0xce,0xb9,0x00,
+ 0xd4,0xc8,0xd3,0x64,0xd2,0x30,0xd1,0x16,0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x93,
+ 0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xce,0xb9,0x00,0x10,0x0d,0x01,0xff,
+ 0xcf,0x89,0xcc,0x93,0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcc,
+ 0x80,0xce,0xb9,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcc,0x81,
+ 0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcc,0x81,0xce,0xb9,0x00,0x10,0x0d,
+ 0x01,0xff,0xcf,0x89,0xcc,0x93,0xcd,0x82,0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,
+ 0x94,0xcd,0x82,0xce,0xb9,0x00,0xd2,0x30,0xd1,0x16,0x10,0x0b,0x01,0xff,0xcf,0x89,
+ 0xcc,0x93,0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xce,0xb9,0x00,0x10,0x0d,
+ 0x01,0xff,0xcf,0x89,0xcc,0x93,0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,
+ 0x94,0xcc,0x80,0xce,0xb9,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,0xcf,0x89,0xcc,0x93,
+ 0xcc,0x81,0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcc,0x81,0xce,0xb9,0x00,
+ 0x10,0x0d,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcd,0x82,0xce,0xb9,0x00,0x01,0xff,0xcf,
+ 0x89,0xcc,0x94,0xcd,0x82,0xce,0xb9,0x00,0xd3,0x49,0xd2,0x26,0xd1,0x12,0x10,0x09,
+ 0x01,0xff,0xce,0xb1,0xcc,0x86,0x00,0x01,0xff,0xce,0xb1,0xcc,0x84,0x00,0x10,0x0b,
+ 0x01,0xff,0xce,0xb1,0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xce,0xb1,0xce,0xb9,0x00,
+ 0xd1,0x0f,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,0x81,0xce,0xb9,0x00,0x00,0x00,0x10,
+ 0x09,0x01,0xff,0xce,0xb1,0xcd,0x82,0x00,0x01,0xff,0xce,0xb1,0xcd,0x82,0xce,0xb9,
+ 0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x86,0x00,0x01,0xff,
+ 0xce,0xb1,0xcc,0x84,0x00,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x80,0x00,0x01,0xff,
+ 0xce,0xb1,0xcc,0x81,0x00,0xe1,0xf3,0x5a,0x10,0x09,0x01,0xff,0xce,0xb1,0xce,0xb9,
+ 0x00,0x01,0x00,0xcf,0x86,0xd5,0xbd,0xd4,0x7e,0xd3,0x44,0xd2,0x21,0xd1,0x0d,0x10,
+ 0x04,0x01,0x00,0x01,0xff,0xc2,0xa8,0xcd,0x82,0x00,0x10,0x0b,0x01,0xff,0xce,0xb7,
+ 0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xce,0xb7,0xce,0xb9,0x00,0xd1,0x0f,0x10,0x0b,
+ 0x01,0xff,0xce,0xb7,0xcc,0x81,0xce,0xb9,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xce,
+ 0xb7,0xcd,0x82,0x00,0x01,0xff,0xce,0xb7,0xcd,0x82,0xce,0xb9,0x00,0xd2,0x24,0xd1,
+ 0x12,0x10,0x09,0x01,0xff,0xce,0xb5,0xcc,0x80,0x00,0x01,0xff,0xce,0xb5,0xcc,0x81,
+ 0x00,0x10,0x09,0x01,0xff,0xce,0xb7,0xcc,0x80,0x00,0x01,0xff,0xce,0xb7,0xcc,0x81,
+ 0x00,0xe1,0x02,0x5b,0x10,0x09,0x01,0xff,0xce,0xb7,0xce,0xb9,0x00,0x01,0xff,0xe1,
+ 0xbe,0xbf,0xcc,0x80,0x00,0xd3,0x18,0xe2,0x28,0x5b,0xe1,0x11,0x5b,0x10,0x09,0x01,
+ 0xff,0xce,0xb9,0xcc,0x86,0x00,0x01,0xff,0xce,0xb9,0xcc,0x84,0x00,0xe2,0x4c,0x5b,
+ 0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb9,0xcc,0x86,0x00,0x01,0xff,0xce,0xb9,0xcc,
+ 0x84,0x00,0x10,0x09,0x01,0xff,0xce,0xb9,0xcc,0x80,0x00,0x01,0xff,0xce,0xb9,0xcc,
+ 0x81,0x00,0xd4,0x51,0xd3,0x18,0xe2,0x6f,0x5b,0xe1,0x58,0x5b,0x10,0x09,0x01,0xff,
+ 0xcf,0x85,0xcc,0x86,0x00,0x01,0xff,0xcf,0x85,0xcc,0x84,0x00,0xd2,0x24,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xcf,0x85,0xcc,0x86,0x00,0x01,0xff,0xcf,0x85,0xcc,0x84,0x00,
+ 0x10,0x09,0x01,0xff,0xcf,0x85,0xcc,0x80,0x00,0x01,0xff,0xcf,0x85,0xcc,0x81,0x00,
+ 0xe1,0x8f,0x5b,0x10,0x09,0x01,0xff,0xcf,0x81,0xcc,0x94,0x00,0x01,0xff,0xc2,0xa8,
+ 0xcc,0x80,0x00,0xd3,0x3b,0xd2,0x18,0x51,0x04,0x00,0x00,0x10,0x0b,0x01,0xff,0xcf,
+ 0x89,0xcc,0x80,0xce,0xb9,0x00,0x01,0xff,0xcf,0x89,0xce,0xb9,0x00,0xd1,0x0f,0x10,
+ 0x0b,0x01,0xff,0xcf,0x89,0xcc,0x81,0xce,0xb9,0x00,0x00,0x00,0x10,0x09,0x01,0xff,
+ 0xcf,0x89,0xcd,0x82,0x00,0x01,0xff,0xcf,0x89,0xcd,0x82,0xce,0xb9,0x00,0xd2,0x24,
+ 0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xbf,0xcc,0x80,0x00,0x01,0xff,0xce,0xbf,0xcc,
+ 0x81,0x00,0x10,0x09,0x01,0xff,0xcf,0x89,0xcc,0x80,0x00,0x01,0xff,0xcf,0x89,0xcc,
+ 0x81,0x00,0xe1,0x99,0x5b,0x10,0x09,0x01,0xff,0xcf,0x89,0xce,0xb9,0x00,0x01,0xff,
+ 0xc2,0xb4,0x00,0xe0,0x0c,0x68,0xcf,0x86,0xe5,0x23,0x02,0xe4,0x25,0x01,0xe3,0x85,
+ 0x5e,0xd2,0x2a,0xe1,0x5f,0x5c,0xe0,0xdd,0x5b,0xcf,0x86,0xe5,0xbb,0x5b,0x94,0x1b,
+ 0xe3,0xa4,0x5b,0x92,0x14,0x91,0x10,0x10,0x08,0x01,0xff,0xe2,0x80,0x82,0x00,0x01,
+ 0xff,0xe2,0x80,0x83,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd1,0xd6,0xd0,0x46,0xcf,
+ 0x86,0x55,0x04,0x01,0x00,0xd4,0x29,0xd3,0x13,0x52,0x04,0x01,0x00,0x51,0x04,0x01,
+ 0x00,0x10,0x07,0x01,0xff,0xcf,0x89,0x00,0x01,0x00,0x92,0x12,0x51,0x04,0x01,0x00,
+ 0x10,0x06,0x01,0xff,0x6b,0x00,0x01,0xff,0x61,0xcc,0x8a,0x00,0x01,0x00,0xe3,0x25,
+ 0x5d,0x92,0x10,0x51,0x04,0x01,0x00,0x10,0x08,0x01,0xff,0xe2,0x85,0x8e,0x00,0x01,
+ 0x00,0x01,0x00,0xcf,0x86,0xd5,0x0a,0xe4,0x42,0x5d,0x63,0x2d,0x5d,0x06,0x00,0x94,
+ 0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0x85,0xb0,0x00,0x01,
+ 0xff,0xe2,0x85,0xb1,0x00,0x10,0x08,0x01,0xff,0xe2,0x85,0xb2,0x00,0x01,0xff,0xe2,
+ 0x85,0xb3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0x85,0xb4,0x00,0x01,0xff,0xe2,
+ 0x85,0xb5,0x00,0x10,0x08,0x01,0xff,0xe2,0x85,0xb6,0x00,0x01,0xff,0xe2,0x85,0xb7,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0x85,0xb8,0x00,0x01,0xff,0xe2,
+ 0x85,0xb9,0x00,0x10,0x08,0x01,0xff,0xe2,0x85,0xba,0x00,0x01,0xff,0xe2,0x85,0xbb,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0x85,0xbc,0x00,0x01,0xff,0xe2,0x85,0xbd,
+ 0x00,0x10,0x08,0x01,0xff,0xe2,0x85,0xbe,0x00,0x01,0xff,0xe2,0x85,0xbf,0x00,0x01,
+ 0x00,0xe0,0x34,0x5d,0xcf,0x86,0xe5,0x13,0x5d,0xe4,0xf2,0x5c,0xe3,0xe1,0x5c,0xe2,
+ 0xd4,0x5c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x04,0xff,0xe2,0x86,0x84,0x00,
+ 0xe3,0x23,0x61,0xe2,0xf0,0x60,0xd1,0x0c,0xe0,0x9d,0x60,0xcf,0x86,0x65,0x7e,0x60,
+ 0x01,0x00,0xd0,0x62,0xcf,0x86,0x55,0x04,0x01,0x00,0x54,0x04,0x01,0x00,0xd3,0x18,
+ 0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x08,0x01,0xff,0xe2,0x93,0x90,0x00,
+ 0x01,0xff,0xe2,0x93,0x91,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0x93,
+ 0x92,0x00,0x01,0xff,0xe2,0x93,0x93,0x00,0x10,0x08,0x01,0xff,0xe2,0x93,0x94,0x00,
+ 0x01,0xff,0xe2,0x93,0x95,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe2,0x93,0x96,0x00,
+ 0x01,0xff,0xe2,0x93,0x97,0x00,0x10,0x08,0x01,0xff,0xe2,0x93,0x98,0x00,0x01,0xff,
+ 0xe2,0x93,0x99,0x00,0xcf,0x86,0xe5,0x57,0x60,0x94,0x80,0xd3,0x40,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe2,0x93,0x9a,0x00,0x01,0xff,0xe2,0x93,0x9b,0x00,0x10,
+ 0x08,0x01,0xff,0xe2,0x93,0x9c,0x00,0x01,0xff,0xe2,0x93,0x9d,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe2,0x93,0x9e,0x00,0x01,0xff,0xe2,0x93,0x9f,0x00,0x10,0x08,0x01,
+ 0xff,0xe2,0x93,0xa0,0x00,0x01,0xff,0xe2,0x93,0xa1,0x00,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe2,0x93,0xa2,0x00,0x01,0xff,0xe2,0x93,0xa3,0x00,0x10,0x08,0x01,
+ 0xff,0xe2,0x93,0xa4,0x00,0x01,0xff,0xe2,0x93,0xa5,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0xe2,0x93,0xa6,0x00,0x01,0xff,0xe2,0x93,0xa7,0x00,0x10,0x08,0x01,0xff,0xe2,
+ 0x93,0xa8,0x00,0x01,0xff,0xe2,0x93,0xa9,0x00,0x01,0x00,0xd4,0x0c,0xe3,0x33,0x62,
+ 0xe2,0x2c,0x62,0xcf,0x06,0x04,0x00,0xe3,0x0c,0x65,0xe2,0xff,0x63,0xe1,0x2e,0x02,
+ 0xe0,0x84,0x01,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x08,0xff,0xe2,0xb0,0xb0,0x00,0x08,0xff,0xe2,0xb0,0xb1,0x00,0x10,0x08,
+ 0x08,0xff,0xe2,0xb0,0xb2,0x00,0x08,0xff,0xe2,0xb0,0xb3,0x00,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe2,0xb0,0xb4,0x00,0x08,0xff,0xe2,0xb0,0xb5,0x00,0x10,0x08,0x08,0xff,
+ 0xe2,0xb0,0xb6,0x00,0x08,0xff,0xe2,0xb0,0xb7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe2,0xb0,0xb8,0x00,0x08,0xff,0xe2,0xb0,0xb9,0x00,0x10,0x08,0x08,0xff,
+ 0xe2,0xb0,0xba,0x00,0x08,0xff,0xe2,0xb0,0xbb,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe2,0xb0,0xbc,0x00,0x08,0xff,0xe2,0xb0,0xbd,0x00,0x10,0x08,0x08,0xff,0xe2,0xb0,
+ 0xbe,0x00,0x08,0xff,0xe2,0xb0,0xbf,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe2,0xb1,0x80,0x00,0x08,0xff,0xe2,0xb1,0x81,0x00,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0x82,0x00,0x08,0xff,0xe2,0xb1,0x83,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0x84,0x00,0x08,0xff,0xe2,0xb1,0x85,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,
+ 0x86,0x00,0x08,0xff,0xe2,0xb1,0x87,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0x88,0x00,0x08,0xff,0xe2,0xb1,0x89,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,
+ 0x8a,0x00,0x08,0xff,0xe2,0xb1,0x8b,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe2,0xb1,
+ 0x8c,0x00,0x08,0xff,0xe2,0xb1,0x8d,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,0x8e,0x00,
+ 0x08,0xff,0xe2,0xb1,0x8f,0x00,0x94,0x7c,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe2,0xb1,0x90,0x00,0x08,0xff,0xe2,0xb1,0x91,0x00,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0x92,0x00,0x08,0xff,0xe2,0xb1,0x93,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0x94,0x00,0x08,0xff,0xe2,0xb1,0x95,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,
+ 0x96,0x00,0x08,0xff,0xe2,0xb1,0x97,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe2,0xb1,0x98,0x00,0x08,0xff,0xe2,0xb1,0x99,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,
+ 0x9a,0x00,0x08,0xff,0xe2,0xb1,0x9b,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe2,0xb1,
+ 0x9c,0x00,0x08,0xff,0xe2,0xb1,0x9d,0x00,0x10,0x08,0x08,0xff,0xe2,0xb1,0x9e,0x00,
+ 0x00,0x00,0x08,0x00,0xcf,0x86,0xd5,0x07,0x64,0xef,0x61,0x08,0x00,0xd4,0x63,0xd3,
+ 0x32,0xd2,0x1b,0xd1,0x0c,0x10,0x08,0x09,0xff,0xe2,0xb1,0xa1,0x00,0x09,0x00,0x10,
+ 0x07,0x09,0xff,0xc9,0xab,0x00,0x09,0xff,0xe1,0xb5,0xbd,0x00,0xd1,0x0b,0x10,0x07,
+ 0x09,0xff,0xc9,0xbd,0x00,0x09,0x00,0x10,0x04,0x09,0x00,0x09,0xff,0xe2,0xb1,0xa8,
+ 0x00,0xd2,0x18,0xd1,0x0c,0x10,0x04,0x09,0x00,0x09,0xff,0xe2,0xb1,0xaa,0x00,0x10,
+ 0x04,0x09,0x00,0x09,0xff,0xe2,0xb1,0xac,0x00,0xd1,0x0b,0x10,0x04,0x09,0x00,0x0a,
+ 0xff,0xc9,0x91,0x00,0x10,0x07,0x0a,0xff,0xc9,0xb1,0x00,0x0a,0xff,0xc9,0x90,0x00,
+ 0xd3,0x27,0xd2,0x17,0xd1,0x0b,0x10,0x07,0x0b,0xff,0xc9,0x92,0x00,0x0a,0x00,0x10,
+ 0x08,0x0a,0xff,0xe2,0xb1,0xb3,0x00,0x0a,0x00,0x91,0x0c,0x10,0x04,0x09,0x00,0x09,
+ 0xff,0xe2,0xb1,0xb6,0x00,0x09,0x00,0x52,0x04,0x0a,0x00,0x51,0x04,0x0a,0x00,0x10,
+ 0x07,0x0b,0xff,0xc8,0xbf,0x00,0x0b,0xff,0xc9,0x80,0x00,0xe0,0x83,0x01,0xcf,0x86,
+ 0xd5,0xc0,0xd4,0x60,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,
+ 0x81,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0x83,0x00,0x08,0x00,0xd1,0x0c,
+ 0x10,0x08,0x08,0xff,0xe2,0xb2,0x85,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,
+ 0x87,0x00,0x08,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0x89,0x00,
+ 0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0x8b,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,
+ 0x08,0xff,0xe2,0xb2,0x8d,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0x8f,0x00,
+ 0x08,0x00,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0x91,0x00,
+ 0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0x93,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,
+ 0x08,0xff,0xe2,0xb2,0x95,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0x97,0x00,
+ 0x08,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0x99,0x00,0x08,0x00,
+ 0x10,0x08,0x08,0xff,0xe2,0xb2,0x9b,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,0x08,0xff,
+ 0xe2,0xb2,0x9d,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0x9f,0x00,0x08,0x00,
+ 0xd4,0x60,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0xa1,0x00,
+ 0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0xa3,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,
+ 0x08,0xff,0xe2,0xb2,0xa5,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0xa7,0x00,
+ 0x08,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0xa9,0x00,0x08,0x00,
+ 0x10,0x08,0x08,0xff,0xe2,0xb2,0xab,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,0x08,0xff,
+ 0xe2,0xb2,0xad,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0xaf,0x00,0x08,0x00,
+ 0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0xb1,0x00,0x08,0x00,
+ 0x10,0x08,0x08,0xff,0xe2,0xb2,0xb3,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,0x08,0xff,
+ 0xe2,0xb2,0xb5,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0xb7,0x00,0x08,0x00,
+ 0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,0xb9,0x00,0x08,0x00,0x10,0x08,
+ 0x08,0xff,0xe2,0xb2,0xbb,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb2,
+ 0xbd,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb2,0xbf,0x00,0x08,0x00,0xcf,0x86,
+ 0xd5,0xc0,0xd4,0x60,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb3,
+ 0x81,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0x83,0x00,0x08,0x00,0xd1,0x0c,
+ 0x10,0x08,0x08,0xff,0xe2,0xb3,0x85,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,
+ 0x87,0x00,0x08,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb3,0x89,0x00,
+ 0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0x8b,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,
+ 0x08,0xff,0xe2,0xb3,0x8d,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0x8f,0x00,
+ 0x08,0x00,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb3,0x91,0x00,
+ 0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0x93,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,
+ 0x08,0xff,0xe2,0xb3,0x95,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0x97,0x00,
+ 0x08,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb3,0x99,0x00,0x08,0x00,
+ 0x10,0x08,0x08,0xff,0xe2,0xb3,0x9b,0x00,0x08,0x00,0xd1,0x0c,0x10,0x08,0x08,0xff,
+ 0xe2,0xb3,0x9d,0x00,0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0x9f,0x00,0x08,0x00,
+ 0xd4,0x3b,0xd3,0x1c,0x92,0x18,0xd1,0x0c,0x10,0x08,0x08,0xff,0xe2,0xb3,0xa1,0x00,
+ 0x08,0x00,0x10,0x08,0x08,0xff,0xe2,0xb3,0xa3,0x00,0x08,0x00,0x08,0x00,0xd2,0x10,
+ 0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x0b,0xff,0xe2,0xb3,0xac,0x00,0xe1,0x3b,
+ 0x5f,0x10,0x04,0x0b,0x00,0x0b,0xff,0xe2,0xb3,0xae,0x00,0xe3,0x40,0x5f,0x92,0x10,
+ 0x51,0x04,0x0b,0xe6,0x10,0x08,0x0d,0xff,0xe2,0xb3,0xb3,0x00,0x0d,0x00,0x00,0x00,
+ 0xe2,0x98,0x08,0xd1,0x0b,0xe0,0x11,0x67,0xcf,0x86,0xcf,0x06,0x01,0x00,0xe0,0x65,
+ 0x6c,0xcf,0x86,0xe5,0xa7,0x05,0xd4,0x06,0xcf,0x06,0x04,0x00,0xd3,0x0c,0xe2,0xf8,
+ 0x67,0xe1,0x8f,0x67,0xcf,0x06,0x04,0x00,0xe2,0xdb,0x01,0xe1,0x26,0x01,0xd0,0x09,
+ 0xcf,0x86,0x65,0xf4,0x67,0x0a,0x00,0xcf,0x86,0xd5,0xc0,0xd4,0x60,0xd3,0x30,0xd2,
+ 0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x81,0x00,0x0a,0x00,0x10,0x08,0x0a,
+ 0xff,0xea,0x99,0x83,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x85,
+ 0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x99,0x87,0x00,0x0a,0x00,0xd2,0x18,0xd1,
+ 0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x89,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,
+ 0x99,0x8b,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x8d,0x00,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xea,0x99,0x8f,0x00,0x0a,0x00,0xd3,0x30,0xd2,0x18,0xd1,
+ 0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x91,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,
+ 0x99,0x93,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x95,0x00,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xea,0x99,0x97,0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,
+ 0x08,0x0a,0xff,0xea,0x99,0x99,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x99,0x9b,
+ 0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0x9d,0x00,0x0a,0x00,0x10,
+ 0x08,0x0a,0xff,0xea,0x99,0x9f,0x00,0x0a,0x00,0xe4,0x5d,0x67,0xd3,0x30,0xd2,0x18,
+ 0xd1,0x0c,0x10,0x08,0x0c,0xff,0xea,0x99,0xa1,0x00,0x0c,0x00,0x10,0x08,0x0a,0xff,
+ 0xea,0x99,0xa3,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x99,0xa5,0x00,
+ 0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x99,0xa7,0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,
+ 0x10,0x08,0x0a,0xff,0xea,0x99,0xa9,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x99,
+ 0xab,0x00,0x0a,0x00,0xe1,0x0c,0x67,0x10,0x08,0x0a,0xff,0xea,0x99,0xad,0x00,0x0a,
+ 0x00,0xe0,0x35,0x67,0xcf,0x86,0x95,0xab,0xd4,0x60,0xd3,0x30,0xd2,0x18,0xd1,0x0c,
+ 0x10,0x08,0x0a,0xff,0xea,0x9a,0x81,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9a,
+ 0x83,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9a,0x85,0x00,0x0a,0x00,
+ 0x10,0x08,0x0a,0xff,0xea,0x9a,0x87,0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,
+ 0x0a,0xff,0xea,0x9a,0x89,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9a,0x8b,0x00,
+ 0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9a,0x8d,0x00,0x0a,0x00,0x10,0x08,
+ 0x0a,0xff,0xea,0x9a,0x8f,0x00,0x0a,0x00,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,
+ 0x0a,0xff,0xea,0x9a,0x91,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9a,0x93,0x00,
+ 0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9a,0x95,0x00,0x0a,0x00,0x10,0x08,
+ 0x0a,0xff,0xea,0x9a,0x97,0x00,0x0a,0x00,0xe2,0x92,0x66,0xd1,0x0c,0x10,0x08,0x10,
+ 0xff,0xea,0x9a,0x99,0x00,0x10,0x00,0x10,0x08,0x10,0xff,0xea,0x9a,0x9b,0x00,0x10,
+ 0x00,0x0b,0x00,0xe1,0x10,0x02,0xd0,0xb9,0xcf,0x86,0xd5,0x07,0x64,0x9e,0x66,0x08,
+ 0x00,0xd4,0x58,0xd3,0x28,0xd2,0x10,0x51,0x04,0x09,0x00,0x10,0x08,0x0a,0xff,0xea,
+ 0x9c,0xa3,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9c,0xa5,0x00,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xea,0x9c,0xa7,0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,
+ 0x08,0x0a,0xff,0xea,0x9c,0xa9,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9c,0xab,
+ 0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9c,0xad,0x00,0x0a,0x00,0x10,
+ 0x08,0x0a,0xff,0xea,0x9c,0xaf,0x00,0x0a,0x00,0xd3,0x28,0xd2,0x10,0x51,0x04,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xea,0x9c,0xb3,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,
+ 0xff,0xea,0x9c,0xb5,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9c,0xb7,0x00,0x0a,
+ 0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9c,0xb9,0x00,0x0a,0x00,0x10,
+ 0x08,0x0a,0xff,0xea,0x9c,0xbb,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,
+ 0x9c,0xbd,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9c,0xbf,0x00,0x0a,0x00,0xcf,
+ 0x86,0xd5,0xc0,0xd4,0x60,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,
+ 0x9d,0x81,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x83,0x00,0x0a,0x00,0xd1,
+ 0x0c,0x10,0x08,0x0a,0xff,0xea,0x9d,0x85,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,
+ 0x9d,0x87,0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9d,0x89,
+ 0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x8b,0x00,0x0a,0x00,0xd1,0x0c,0x10,
+ 0x08,0x0a,0xff,0xea,0x9d,0x8d,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x8f,
+ 0x00,0x0a,0x00,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9d,0x91,
+ 0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x93,0x00,0x0a,0x00,0xd1,0x0c,0x10,
+ 0x08,0x0a,0xff,0xea,0x9d,0x95,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x97,
+ 0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9d,0x99,0x00,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x9b,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,
+ 0xff,0xea,0x9d,0x9d,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0x9f,0x00,0x0a,
+ 0x00,0xd4,0x60,0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9d,0xa1,
+ 0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0xa3,0x00,0x0a,0x00,0xd1,0x0c,0x10,
+ 0x08,0x0a,0xff,0xea,0x9d,0xa5,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0xa7,
+ 0x00,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9d,0xa9,0x00,0x0a,
+ 0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0xab,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,
+ 0xff,0xea,0x9d,0xad,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0xaf,0x00,0x0a,
+ 0x00,0x53,0x04,0x0a,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x04,0x0a,0x00,0x0a,0xff,0xea,
+ 0x9d,0xba,0x00,0x10,0x04,0x0a,0x00,0x0a,0xff,0xea,0x9d,0xbc,0x00,0xd1,0x0c,0x10,
+ 0x04,0x0a,0x00,0x0a,0xff,0xe1,0xb5,0xb9,0x00,0x10,0x08,0x0a,0xff,0xea,0x9d,0xbf,
+ 0x00,0x0a,0x00,0xe0,0x71,0x01,0xcf,0x86,0xd5,0xa6,0xd4,0x4e,0xd3,0x30,0xd2,0x18,
+ 0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9e,0x81,0x00,0x0a,0x00,0x10,0x08,0x0a,0xff,
+ 0xea,0x9e,0x83,0x00,0x0a,0x00,0xd1,0x0c,0x10,0x08,0x0a,0xff,0xea,0x9e,0x85,0x00,
+ 0x0a,0x00,0x10,0x08,0x0a,0xff,0xea,0x9e,0x87,0x00,0x0a,0x00,0xd2,0x10,0x51,0x04,
+ 0x0a,0x00,0x10,0x04,0x0a,0x00,0x0a,0xff,0xea,0x9e,0x8c,0x00,0xe1,0x9a,0x64,0x10,
+ 0x04,0x0a,0x00,0x0c,0xff,0xc9,0xa5,0x00,0xd3,0x28,0xd2,0x18,0xd1,0x0c,0x10,0x08,
+ 0x0c,0xff,0xea,0x9e,0x91,0x00,0x0c,0x00,0x10,0x08,0x0d,0xff,0xea,0x9e,0x93,0x00,
+ 0x0d,0x00,0x51,0x04,0x10,0x00,0x10,0x08,0x10,0xff,0xea,0x9e,0x97,0x00,0x10,0x00,
+ 0xd2,0x18,0xd1,0x0c,0x10,0x08,0x10,0xff,0xea,0x9e,0x99,0x00,0x10,0x00,0x10,0x08,
+ 0x10,0xff,0xea,0x9e,0x9b,0x00,0x10,0x00,0xd1,0x0c,0x10,0x08,0x10,0xff,0xea,0x9e,
+ 0x9d,0x00,0x10,0x00,0x10,0x08,0x10,0xff,0xea,0x9e,0x9f,0x00,0x10,0x00,0xd4,0x63,
+ 0xd3,0x30,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x0c,0xff,0xea,0x9e,0xa1,0x00,0x0c,0x00,
+ 0x10,0x08,0x0c,0xff,0xea,0x9e,0xa3,0x00,0x0c,0x00,0xd1,0x0c,0x10,0x08,0x0c,0xff,
+ 0xea,0x9e,0xa5,0x00,0x0c,0x00,0x10,0x08,0x0c,0xff,0xea,0x9e,0xa7,0x00,0x0c,0x00,
+ 0xd2,0x1a,0xd1,0x0c,0x10,0x08,0x0c,0xff,0xea,0x9e,0xa9,0x00,0x0c,0x00,0x10,0x07,
+ 0x0d,0xff,0xc9,0xa6,0x00,0x10,0xff,0xc9,0x9c,0x00,0xd1,0x0e,0x10,0x07,0x10,0xff,
+ 0xc9,0xa1,0x00,0x10,0xff,0xc9,0xac,0x00,0x10,0x07,0x12,0xff,0xc9,0xaa,0x00,0x14,
+ 0x00,0xd3,0x35,0xd2,0x1d,0xd1,0x0e,0x10,0x07,0x10,0xff,0xca,0x9e,0x00,0x10,0xff,
+ 0xca,0x87,0x00,0x10,0x07,0x11,0xff,0xca,0x9d,0x00,0x11,0xff,0xea,0xad,0x93,0x00,
+ 0xd1,0x0c,0x10,0x08,0x11,0xff,0xea,0x9e,0xb5,0x00,0x11,0x00,0x10,0x08,0x11,0xff,
+ 0xea,0x9e,0xb7,0x00,0x11,0x00,0xd2,0x18,0xd1,0x0c,0x10,0x08,0x14,0xff,0xea,0x9e,
+ 0xb9,0x00,0x14,0x00,0x10,0x08,0x15,0xff,0xea,0x9e,0xbb,0x00,0x15,0x00,0xd1,0x0c,
+ 0x10,0x08,0x15,0xff,0xea,0x9e,0xbd,0x00,0x15,0x00,0x10,0x08,0x15,0xff,0xea,0x9e,
+ 0xbf,0x00,0x15,0x00,0xcf,0x86,0xe5,0xd4,0x63,0x94,0x2f,0x93,0x2b,0xd2,0x10,0x51,
+ 0x04,0x00,0x00,0x10,0x08,0x15,0xff,0xea,0x9f,0x83,0x00,0x15,0x00,0xd1,0x0f,0x10,
+ 0x08,0x15,0xff,0xea,0x9e,0x94,0x00,0x15,0xff,0xca,0x82,0x00,0x10,0x08,0x15,0xff,
+ 0xe1,0xb6,0x8e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe4,0xb4,0x66,0xd3,0x1d,0xe2,
+ 0x5b,0x64,0xe1,0x0a,0x64,0xe0,0xf7,0x63,0xcf,0x86,0xe5,0xd8,0x63,0x94,0x0b,0x93,
+ 0x07,0x62,0xc3,0x63,0x08,0x00,0x08,0x00,0x08,0x00,0xd2,0x0f,0xe1,0x5a,0x65,0xe0,
+ 0x27,0x65,0xcf,0x86,0x65,0x0c,0x65,0x0a,0x00,0xd1,0xab,0xd0,0x1a,0xcf,0x86,0xe5,
+ 0x17,0x66,0xe4,0xfa,0x65,0xe3,0xe1,0x65,0xe2,0xd4,0x65,0x91,0x08,0x10,0x04,0x00,
+ 0x00,0x0c,0x00,0x0c,0x00,0xcf,0x86,0x55,0x04,0x10,0x00,0xd4,0x0b,0x93,0x07,0x62,
+ 0x27,0x66,0x11,0x00,0x00,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x11,0xff,
+ 0xe1,0x8e,0xa0,0x00,0x11,0xff,0xe1,0x8e,0xa1,0x00,0x10,0x08,0x11,0xff,0xe1,0x8e,
+ 0xa2,0x00,0x11,0xff,0xe1,0x8e,0xa3,0x00,0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8e,
+ 0xa4,0x00,0x11,0xff,0xe1,0x8e,0xa5,0x00,0x10,0x08,0x11,0xff,0xe1,0x8e,0xa6,0x00,
+ 0x11,0xff,0xe1,0x8e,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8e,
+ 0xa8,0x00,0x11,0xff,0xe1,0x8e,0xa9,0x00,0x10,0x08,0x11,0xff,0xe1,0x8e,0xaa,0x00,
+ 0x11,0xff,0xe1,0x8e,0xab,0x00,0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8e,0xac,0x00,
+ 0x11,0xff,0xe1,0x8e,0xad,0x00,0x10,0x08,0x11,0xff,0xe1,0x8e,0xae,0x00,0x11,0xff,
+ 0xe1,0x8e,0xaf,0x00,0xe0,0xb2,0x65,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,0x40,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8e,0xb0,0x00,0x11,0xff,0xe1,0x8e,
+ 0xb1,0x00,0x10,0x08,0x11,0xff,0xe1,0x8e,0xb2,0x00,0x11,0xff,0xe1,0x8e,0xb3,0x00,
+ 0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8e,0xb4,0x00,0x11,0xff,0xe1,0x8e,0xb5,0x00,
+ 0x10,0x08,0x11,0xff,0xe1,0x8e,0xb6,0x00,0x11,0xff,0xe1,0x8e,0xb7,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8e,0xb8,0x00,0x11,0xff,0xe1,0x8e,0xb9,0x00,
+ 0x10,0x08,0x11,0xff,0xe1,0x8e,0xba,0x00,0x11,0xff,0xe1,0x8e,0xbb,0x00,0xd1,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8e,0xbc,0x00,0x11,0xff,0xe1,0x8e,0xbd,0x00,0x10,0x08,
+ 0x11,0xff,0xe1,0x8e,0xbe,0x00,0x11,0xff,0xe1,0x8e,0xbf,0x00,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8f,0x80,0x00,0x11,0xff,0xe1,0x8f,0x81,0x00,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0x82,0x00,0x11,0xff,0xe1,0x8f,0x83,0x00,0xd1,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0x84,0x00,0x11,0xff,0xe1,0x8f,0x85,0x00,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0x86,0x00,0x11,0xff,0xe1,0x8f,0x87,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0x88,0x00,0x11,0xff,0xe1,0x8f,0x89,0x00,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0x8a,0x00,0x11,0xff,0xe1,0x8f,0x8b,0x00,0xd1,0x10,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0x8c,0x00,0x11,0xff,0xe1,0x8f,0x8d,0x00,0x10,0x08,0x11,0xff,
+ 0xe1,0x8f,0x8e,0x00,0x11,0xff,0xe1,0x8f,0x8f,0x00,0xd4,0x80,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x11,0xff,0xe1,0x8f,0x90,0x00,0x11,0xff,0xe1,0x8f,0x91,0x00,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0x92,0x00,0x11,0xff,0xe1,0x8f,0x93,0x00,0xd1,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0x94,0x00,0x11,0xff,0xe1,0x8f,0x95,0x00,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0x96,0x00,0x11,0xff,0xe1,0x8f,0x97,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0x98,0x00,0x11,0xff,0xe1,0x8f,0x99,0x00,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0x9a,0x00,0x11,0xff,0xe1,0x8f,0x9b,0x00,0xd1,0x10,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0x9c,0x00,0x11,0xff,0xe1,0x8f,0x9d,0x00,0x10,0x08,0x11,0xff,
+ 0xe1,0x8f,0x9e,0x00,0x11,0xff,0xe1,0x8f,0x9f,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x11,0xff,0xe1,0x8f,0xa0,0x00,0x11,0xff,0xe1,0x8f,0xa1,0x00,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0xa2,0x00,0x11,0xff,0xe1,0x8f,0xa3,0x00,0xd1,0x10,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0xa4,0x00,0x11,0xff,0xe1,0x8f,0xa5,0x00,0x10,0x08,0x11,0xff,
+ 0xe1,0x8f,0xa6,0x00,0x11,0xff,0xe1,0x8f,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x11,0xff,0xe1,0x8f,0xa8,0x00,0x11,0xff,0xe1,0x8f,0xa9,0x00,0x10,0x08,0x11,0xff,
+ 0xe1,0x8f,0xaa,0x00,0x11,0xff,0xe1,0x8f,0xab,0x00,0xd1,0x10,0x10,0x08,0x11,0xff,
+ 0xe1,0x8f,0xac,0x00,0x11,0xff,0xe1,0x8f,0xad,0x00,0x10,0x08,0x11,0xff,0xe1,0x8f,
+ 0xae,0x00,0x11,0xff,0xe1,0x8f,0xaf,0x00,0xd1,0x0c,0xe0,0xeb,0x63,0xcf,0x86,0xcf,
+ 0x06,0x02,0xff,0xff,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x06,
+ 0xcf,0x06,0x01,0x00,0xd4,0xae,0xd3,0x09,0xe2,0x54,0x64,0xcf,0x06,0x01,0x00,0xd2,
+ 0x27,0xe1,0x1f,0x70,0xe0,0x26,0x6e,0xcf,0x86,0xe5,0x3f,0x6d,0xe4,0xce,0x6c,0xe3,
+ 0x99,0x6c,0xe2,0x78,0x6c,0xe1,0x67,0x6c,0x10,0x08,0x01,0xff,0xe5,0x88,0x87,0x00,
+ 0x01,0xff,0xe5,0xba,0xa6,0x00,0xe1,0x74,0x74,0xe0,0xe8,0x73,0xcf,0x86,0xe5,0x22,
+ 0x73,0xd4,0x3b,0x93,0x37,0xd2,0x1d,0xd1,0x0e,0x10,0x07,0x01,0xff,0x66,0x66,0x00,
+ 0x01,0xff,0x66,0x69,0x00,0x10,0x07,0x01,0xff,0x66,0x6c,0x00,0x01,0xff,0x66,0x66,
+ 0x69,0x00,0xd1,0x0f,0x10,0x08,0x01,0xff,0x66,0x66,0x6c,0x00,0x01,0xff,0x73,0x74,
+ 0x00,0x10,0x07,0x01,0xff,0x73,0x74,0x00,0x00,0x00,0x00,0x00,0xe3,0xc8,0x72,0xd2,
+ 0x11,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0xff,0xd5,0xb4,0xd5,0xb6,0x00,
+ 0xd1,0x12,0x10,0x09,0x01,0xff,0xd5,0xb4,0xd5,0xa5,0x00,0x01,0xff,0xd5,0xb4,0xd5,
+ 0xab,0x00,0x10,0x09,0x01,0xff,0xd5,0xbe,0xd5,0xb6,0x00,0x01,0xff,0xd5,0xb4,0xd5,
+ 0xad,0x00,0xd3,0x09,0xe2,0x40,0x74,0xcf,0x06,0x01,0x00,0xd2,0x13,0xe1,0x30,0x75,
+ 0xe0,0xc1,0x74,0xcf,0x86,0xe5,0x9e,0x74,0x64,0x8d,0x74,0x06,0xff,0x00,0xe1,0x96,
+ 0x75,0xe0,0x63,0x75,0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x7c,
+ 0xd3,0x3c,0xd2,0x1c,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0xef,0xbd,0x81,0x00,
+ 0x10,0x08,0x01,0xff,0xef,0xbd,0x82,0x00,0x01,0xff,0xef,0xbd,0x83,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xef,0xbd,0x84,0x00,0x01,0xff,0xef,0xbd,0x85,0x00,0x10,0x08,
+ 0x01,0xff,0xef,0xbd,0x86,0x00,0x01,0xff,0xef,0xbd,0x87,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xef,0xbd,0x88,0x00,0x01,0xff,0xef,0xbd,0x89,0x00,0x10,0x08,
+ 0x01,0xff,0xef,0xbd,0x8a,0x00,0x01,0xff,0xef,0xbd,0x8b,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xef,0xbd,0x8c,0x00,0x01,0xff,0xef,0xbd,0x8d,0x00,0x10,0x08,0x01,0xff,
+ 0xef,0xbd,0x8e,0x00,0x01,0xff,0xef,0xbd,0x8f,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xef,0xbd,0x90,0x00,0x01,0xff,0xef,0xbd,0x91,0x00,0x10,0x08,
+ 0x01,0xff,0xef,0xbd,0x92,0x00,0x01,0xff,0xef,0xbd,0x93,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xef,0xbd,0x94,0x00,0x01,0xff,0xef,0xbd,0x95,0x00,0x10,0x08,0x01,0xff,
+ 0xef,0xbd,0x96,0x00,0x01,0xff,0xef,0xbd,0x97,0x00,0x92,0x1c,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xef,0xbd,0x98,0x00,0x01,0xff,0xef,0xbd,0x99,0x00,0x10,0x08,0x01,0xff,
+ 0xef,0xbd,0x9a,0x00,0x01,0x00,0x01,0x00,0x83,0xe2,0x87,0xb3,0xe1,0x60,0xb0,0xe0,
+ 0xdd,0xae,0xcf,0x86,0xe5,0x81,0x9b,0xc4,0xe3,0xc1,0x07,0xe2,0x62,0x06,0xe1,0x11,
+ 0x86,0xe0,0x09,0x05,0xcf,0x86,0xe5,0xfb,0x02,0xd4,0x1c,0xe3,0x7f,0x76,0xe2,0xd6,
+ 0x75,0xe1,0xb1,0x75,0xe0,0x8a,0x75,0xcf,0x86,0xe5,0x57,0x75,0x94,0x07,0x63,0x42,
+ 0x75,0x07,0x00,0x07,0x00,0xe3,0x2b,0x78,0xe2,0xf0,0x77,0xe1,0x77,0x01,0xe0,0x88,
+ 0x77,0xcf,0x86,0xe5,0x21,0x01,0xd4,0x90,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,
+ 0x05,0xff,0xf0,0x90,0x90,0xa8,0x00,0x05,0xff,0xf0,0x90,0x90,0xa9,0x00,0x10,0x09,
+ 0x05,0xff,0xf0,0x90,0x90,0xaa,0x00,0x05,0xff,0xf0,0x90,0x90,0xab,0x00,0xd1,0x12,
+ 0x10,0x09,0x05,0xff,0xf0,0x90,0x90,0xac,0x00,0x05,0xff,0xf0,0x90,0x90,0xad,0x00,
+ 0x10,0x09,0x05,0xff,0xf0,0x90,0x90,0xae,0x00,0x05,0xff,0xf0,0x90,0x90,0xaf,0x00,
+ 0xd2,0x24,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0x90,0x90,0xb0,0x00,0x05,0xff,0xf0,
+ 0x90,0x90,0xb1,0x00,0x10,0x09,0x05,0xff,0xf0,0x90,0x90,0xb2,0x00,0x05,0xff,0xf0,
+ 0x90,0x90,0xb3,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0x90,0x90,0xb4,0x00,0x05,
+ 0xff,0xf0,0x90,0x90,0xb5,0x00,0x10,0x09,0x05,0xff,0xf0,0x90,0x90,0xb6,0x00,0x05,
+ 0xff,0xf0,0x90,0x90,0xb7,0x00,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x05,0xff,
+ 0xf0,0x90,0x90,0xb8,0x00,0x05,0xff,0xf0,0x90,0x90,0xb9,0x00,0x10,0x09,0x05,0xff,
+ 0xf0,0x90,0x90,0xba,0x00,0x05,0xff,0xf0,0x90,0x90,0xbb,0x00,0xd1,0x12,0x10,0x09,
+ 0x05,0xff,0xf0,0x90,0x90,0xbc,0x00,0x05,0xff,0xf0,0x90,0x90,0xbd,0x00,0x10,0x09,
+ 0x05,0xff,0xf0,0x90,0x90,0xbe,0x00,0x05,0xff,0xf0,0x90,0x90,0xbf,0x00,0xd2,0x24,
+ 0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0x90,0x91,0x80,0x00,0x05,0xff,0xf0,0x90,0x91,
+ 0x81,0x00,0x10,0x09,0x05,0xff,0xf0,0x90,0x91,0x82,0x00,0x05,0xff,0xf0,0x90,0x91,
+ 0x83,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0x90,0x91,0x84,0x00,0x05,0xff,0xf0,
+ 0x90,0x91,0x85,0x00,0x10,0x09,0x05,0xff,0xf0,0x90,0x91,0x86,0x00,0x05,0xff,0xf0,
+ 0x90,0x91,0x87,0x00,0x94,0x4c,0x93,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x05,0xff,
+ 0xf0,0x90,0x91,0x88,0x00,0x05,0xff,0xf0,0x90,0x91,0x89,0x00,0x10,0x09,0x05,0xff,
+ 0xf0,0x90,0x91,0x8a,0x00,0x05,0xff,0xf0,0x90,0x91,0x8b,0x00,0xd1,0x12,0x10,0x09,
+ 0x05,0xff,0xf0,0x90,0x91,0x8c,0x00,0x05,0xff,0xf0,0x90,0x91,0x8d,0x00,0x10,0x09,
+ 0x07,0xff,0xf0,0x90,0x91,0x8e,0x00,0x07,0xff,0xf0,0x90,0x91,0x8f,0x00,0x05,0x00,
+ 0x05,0x00,0xd0,0xa0,0xcf,0x86,0xd5,0x07,0x64,0x30,0x76,0x07,0x00,0xd4,0x07,0x63,
+ 0x3d,0x76,0x07,0x00,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x90,
+ 0x93,0x98,0x00,0x12,0xff,0xf0,0x90,0x93,0x99,0x00,0x10,0x09,0x12,0xff,0xf0,0x90,
+ 0x93,0x9a,0x00,0x12,0xff,0xf0,0x90,0x93,0x9b,0x00,0xd1,0x12,0x10,0x09,0x12,0xff,
+ 0xf0,0x90,0x93,0x9c,0x00,0x12,0xff,0xf0,0x90,0x93,0x9d,0x00,0x10,0x09,0x12,0xff,
+ 0xf0,0x90,0x93,0x9e,0x00,0x12,0xff,0xf0,0x90,0x93,0x9f,0x00,0xd2,0x24,0xd1,0x12,
+ 0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xa0,0x00,0x12,0xff,0xf0,0x90,0x93,0xa1,0x00,
+ 0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xa2,0x00,0x12,0xff,0xf0,0x90,0x93,0xa3,0x00,
+ 0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xa4,0x00,0x12,0xff,0xf0,0x90,0x93,
+ 0xa5,0x00,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xa6,0x00,0x12,0xff,0xf0,0x90,0x93,
+ 0xa7,0x00,0xcf,0x86,0xe5,0xc6,0x75,0xd4,0x90,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,
+ 0x09,0x12,0xff,0xf0,0x90,0x93,0xa8,0x00,0x12,0xff,0xf0,0x90,0x93,0xa9,0x00,0x10,
+ 0x09,0x12,0xff,0xf0,0x90,0x93,0xaa,0x00,0x12,0xff,0xf0,0x90,0x93,0xab,0x00,0xd1,
+ 0x12,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xac,0x00,0x12,0xff,0xf0,0x90,0x93,0xad,
+ 0x00,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xae,0x00,0x12,0xff,0xf0,0x90,0x93,0xaf,
+ 0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xb0,0x00,0x12,0xff,
+ 0xf0,0x90,0x93,0xb1,0x00,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xb2,0x00,0x12,0xff,
+ 0xf0,0x90,0x93,0xb3,0x00,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xb4,0x00,
+ 0x12,0xff,0xf0,0x90,0x93,0xb5,0x00,0x10,0x09,0x12,0xff,0xf0,0x90,0x93,0xb6,0x00,
+ 0x12,0xff,0xf0,0x90,0x93,0xb7,0x00,0x93,0x28,0x92,0x24,0xd1,0x12,0x10,0x09,0x12,
+ 0xff,0xf0,0x90,0x93,0xb8,0x00,0x12,0xff,0xf0,0x90,0x93,0xb9,0x00,0x10,0x09,0x12,
+ 0xff,0xf0,0x90,0x93,0xba,0x00,0x12,0xff,0xf0,0x90,0x93,0xbb,0x00,0x00,0x00,0x12,
+ 0x00,0xd4,0x1f,0xe3,0xdf,0x76,0xe2,0x6a,0x76,0xe1,0x09,0x76,0xe0,0xea,0x75,0xcf,
+ 0x86,0xe5,0xb7,0x75,0x94,0x0a,0xe3,0xa2,0x75,0x62,0x99,0x75,0x07,0x00,0x07,0x00,
+ 0xe3,0xde,0x78,0xe2,0xaf,0x78,0xd1,0x09,0xe0,0x4c,0x78,0xcf,0x06,0x0b,0x00,0xe0,
+ 0x7f,0x78,0xcf,0x86,0xe5,0x21,0x01,0xd4,0x90,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,
+ 0x09,0x11,0xff,0xf0,0x90,0xb3,0x80,0x00,0x11,0xff,0xf0,0x90,0xb3,0x81,0x00,0x10,
+ 0x09,0x11,0xff,0xf0,0x90,0xb3,0x82,0x00,0x11,0xff,0xf0,0x90,0xb3,0x83,0x00,0xd1,
+ 0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x84,0x00,0x11,0xff,0xf0,0x90,0xb3,0x85,
+ 0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x86,0x00,0x11,0xff,0xf0,0x90,0xb3,0x87,
+ 0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x88,0x00,0x11,0xff,
+ 0xf0,0x90,0xb3,0x89,0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x8a,0x00,0x11,0xff,
+ 0xf0,0x90,0xb3,0x8b,0x00,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x8c,0x00,
+ 0x11,0xff,0xf0,0x90,0xb3,0x8d,0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x8e,0x00,
+ 0x11,0xff,0xf0,0x90,0xb3,0x8f,0x00,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x11,
+ 0xff,0xf0,0x90,0xb3,0x90,0x00,0x11,0xff,0xf0,0x90,0xb3,0x91,0x00,0x10,0x09,0x11,
+ 0xff,0xf0,0x90,0xb3,0x92,0x00,0x11,0xff,0xf0,0x90,0xb3,0x93,0x00,0xd1,0x12,0x10,
+ 0x09,0x11,0xff,0xf0,0x90,0xb3,0x94,0x00,0x11,0xff,0xf0,0x90,0xb3,0x95,0x00,0x10,
+ 0x09,0x11,0xff,0xf0,0x90,0xb3,0x96,0x00,0x11,0xff,0xf0,0x90,0xb3,0x97,0x00,0xd2,
+ 0x24,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x98,0x00,0x11,0xff,0xf0,0x90,
+ 0xb3,0x99,0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x9a,0x00,0x11,0xff,0xf0,0x90,
+ 0xb3,0x9b,0x00,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x9c,0x00,0x11,0xff,
+ 0xf0,0x90,0xb3,0x9d,0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0x9e,0x00,0x11,0xff,
+ 0xf0,0x90,0xb3,0x9f,0x00,0xd4,0x90,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x11,
+ 0xff,0xf0,0x90,0xb3,0xa0,0x00,0x11,0xff,0xf0,0x90,0xb3,0xa1,0x00,0x10,0x09,0x11,
+ 0xff,0xf0,0x90,0xb3,0xa2,0x00,0x11,0xff,0xf0,0x90,0xb3,0xa3,0x00,0xd1,0x12,0x10,
+ 0x09,0x11,0xff,0xf0,0x90,0xb3,0xa4,0x00,0x11,0xff,0xf0,0x90,0xb3,0xa5,0x00,0x10,
+ 0x09,0x11,0xff,0xf0,0x90,0xb3,0xa6,0x00,0x11,0xff,0xf0,0x90,0xb3,0xa7,0x00,0xd2,
+ 0x24,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0xa8,0x00,0x11,0xff,0xf0,0x90,
+ 0xb3,0xa9,0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0xaa,0x00,0x11,0xff,0xf0,0x90,
+ 0xb3,0xab,0x00,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0xac,0x00,0x11,0xff,
+ 0xf0,0x90,0xb3,0xad,0x00,0x10,0x09,0x11,0xff,0xf0,0x90,0xb3,0xae,0x00,0x11,0xff,
+ 0xf0,0x90,0xb3,0xaf,0x00,0x93,0x23,0x92,0x1f,0xd1,0x12,0x10,0x09,0x11,0xff,0xf0,
+ 0x90,0xb3,0xb0,0x00,0x11,0xff,0xf0,0x90,0xb3,0xb1,0x00,0x10,0x09,0x11,0xff,0xf0,
+ 0x90,0xb3,0xb2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,0x15,0xe4,0x91,
+ 0x7b,0xe3,0x9b,0x79,0xe2,0x94,0x78,0xe1,0xe4,0x77,0xe0,0x9d,0x77,0xcf,0x06,0x0c,
+ 0x00,0xe4,0xeb,0x7e,0xe3,0x44,0x7e,0xe2,0xed,0x7d,0xd1,0x0c,0xe0,0xb2,0x7d,0xcf,
+ 0x86,0x65,0x93,0x7d,0x14,0x00,0xe0,0xb6,0x7d,0xcf,0x86,0x55,0x04,0x00,0x00,0xd4,
+ 0x90,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,0x80,0x00,
+ 0x10,0xff,0xf0,0x91,0xa3,0x81,0x00,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,0x82,0x00,
+ 0x10,0xff,0xf0,0x91,0xa3,0x83,0x00,0xd1,0x12,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,
+ 0x84,0x00,0x10,0xff,0xf0,0x91,0xa3,0x85,0x00,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,
+ 0x86,0x00,0x10,0xff,0xf0,0x91,0xa3,0x87,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x10,
+ 0xff,0xf0,0x91,0xa3,0x88,0x00,0x10,0xff,0xf0,0x91,0xa3,0x89,0x00,0x10,0x09,0x10,
+ 0xff,0xf0,0x91,0xa3,0x8a,0x00,0x10,0xff,0xf0,0x91,0xa3,0x8b,0x00,0xd1,0x12,0x10,
+ 0x09,0x10,0xff,0xf0,0x91,0xa3,0x8c,0x00,0x10,0xff,0xf0,0x91,0xa3,0x8d,0x00,0x10,
+ 0x09,0x10,0xff,0xf0,0x91,0xa3,0x8e,0x00,0x10,0xff,0xf0,0x91,0xa3,0x8f,0x00,0xd3,
+ 0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,0x90,0x00,0x10,0xff,
+ 0xf0,0x91,0xa3,0x91,0x00,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,0x92,0x00,0x10,0xff,
+ 0xf0,0x91,0xa3,0x93,0x00,0xd1,0x12,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,0x94,0x00,
+ 0x10,0xff,0xf0,0x91,0xa3,0x95,0x00,0x10,0x09,0x10,0xff,0xf0,0x91,0xa3,0x96,0x00,
+ 0x10,0xff,0xf0,0x91,0xa3,0x97,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x10,0xff,0xf0,
+ 0x91,0xa3,0x98,0x00,0x10,0xff,0xf0,0x91,0xa3,0x99,0x00,0x10,0x09,0x10,0xff,0xf0,
+ 0x91,0xa3,0x9a,0x00,0x10,0xff,0xf0,0x91,0xa3,0x9b,0x00,0xd1,0x12,0x10,0x09,0x10,
+ 0xff,0xf0,0x91,0xa3,0x9c,0x00,0x10,0xff,0xf0,0x91,0xa3,0x9d,0x00,0x10,0x09,0x10,
+ 0xff,0xf0,0x91,0xa3,0x9e,0x00,0x10,0xff,0xf0,0x91,0xa3,0x9f,0x00,0xd1,0x11,0xe0,
+ 0x12,0x81,0xcf,0x86,0xe5,0x09,0x81,0xe4,0xd2,0x80,0xcf,0x06,0x00,0x00,0xe0,0xdb,
+ 0x82,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x09,0xe3,0x10,0x81,0xcf,0x06,
+ 0x0c,0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xe2,0x3b,0x82,0xe1,0x16,0x82,0xd0,0x06,
+ 0xcf,0x06,0x00,0x00,0xcf,0x86,0xa5,0x21,0x01,0xd4,0x90,0xd3,0x48,0xd2,0x24,0xd1,
+ 0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xa0,0x00,0x14,0xff,0xf0,0x96,0xb9,0xa1,
+ 0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xa2,0x00,0x14,0xff,0xf0,0x96,0xb9,0xa3,
+ 0x00,0xd1,0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xa4,0x00,0x14,0xff,0xf0,0x96,
+ 0xb9,0xa5,0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xa6,0x00,0x14,0xff,0xf0,0x96,
+ 0xb9,0xa7,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xa8,0x00,
+ 0x14,0xff,0xf0,0x96,0xb9,0xa9,0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xaa,0x00,
+ 0x14,0xff,0xf0,0x96,0xb9,0xab,0x00,0xd1,0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,
+ 0xac,0x00,0x14,0xff,0xf0,0x96,0xb9,0xad,0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,
+ 0xae,0x00,0x14,0xff,0xf0,0x96,0xb9,0xaf,0x00,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,
+ 0x09,0x14,0xff,0xf0,0x96,0xb9,0xb0,0x00,0x14,0xff,0xf0,0x96,0xb9,0xb1,0x00,0x10,
+ 0x09,0x14,0xff,0xf0,0x96,0xb9,0xb2,0x00,0x14,0xff,0xf0,0x96,0xb9,0xb3,0x00,0xd1,
+ 0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xb4,0x00,0x14,0xff,0xf0,0x96,0xb9,0xb5,
+ 0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xb6,0x00,0x14,0xff,0xf0,0x96,0xb9,0xb7,
+ 0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xb8,0x00,0x14,0xff,
+ 0xf0,0x96,0xb9,0xb9,0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xba,0x00,0x14,0xff,
+ 0xf0,0x96,0xb9,0xbb,0x00,0xd1,0x12,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xbc,0x00,
+ 0x14,0xff,0xf0,0x96,0xb9,0xbd,0x00,0x10,0x09,0x14,0xff,0xf0,0x96,0xb9,0xbe,0x00,
+ 0x14,0xff,0xf0,0x96,0xb9,0xbf,0x00,0x14,0x00,0xd2,0x14,0xe1,0x25,0x82,0xe0,0x1c,
+ 0x82,0xcf,0x86,0xe5,0xdd,0x81,0xe4,0x9a,0x81,0xcf,0x06,0x12,0x00,0xd1,0x0b,0xe0,
+ 0x51,0x83,0xcf,0x86,0xcf,0x06,0x00,0x00,0xe0,0x95,0x8b,0xcf,0x86,0xd5,0x22,0xe4,
+ 0xd0,0x88,0xe3,0x93,0x88,0xe2,0x38,0x88,0xe1,0x31,0x88,0xe0,0x2a,0x88,0xcf,0x86,
+ 0xe5,0xfb,0x87,0xe4,0xe2,0x87,0x93,0x07,0x62,0xd1,0x87,0x12,0xe6,0x12,0xe6,0xe4,
+ 0x36,0x89,0xe3,0x2f,0x89,0xd2,0x09,0xe1,0xb8,0x88,0xcf,0x06,0x10,0x00,0xe1,0x1f,
+ 0x89,0xe0,0xec,0x88,0xcf,0x86,0xe5,0x21,0x01,0xd4,0x90,0xd3,0x48,0xd2,0x24,0xd1,
+ 0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xa2,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xa3,
+ 0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xa4,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xa5,
+ 0x00,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xa6,0x00,0x12,0xff,0xf0,0x9e,
+ 0xa4,0xa7,0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xa8,0x00,0x12,0xff,0xf0,0x9e,
+ 0xa4,0xa9,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xaa,0x00,
+ 0x12,0xff,0xf0,0x9e,0xa4,0xab,0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xac,0x00,
+ 0x12,0xff,0xf0,0x9e,0xa4,0xad,0x00,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,
+ 0xae,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xaf,0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,
+ 0xb0,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xb1,0x00,0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,
+ 0x09,0x12,0xff,0xf0,0x9e,0xa4,0xb2,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xb3,0x00,0x10,
+ 0x09,0x12,0xff,0xf0,0x9e,0xa4,0xb4,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xb5,0x00,0xd1,
+ 0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xb6,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xb7,
+ 0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xb8,0x00,0x12,0xff,0xf0,0x9e,0xa4,0xb9,
+ 0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xba,0x00,0x12,0xff,
+ 0xf0,0x9e,0xa4,0xbb,0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xbc,0x00,0x12,0xff,
+ 0xf0,0x9e,0xa4,0xbd,0x00,0xd1,0x12,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa4,0xbe,0x00,
+ 0x12,0xff,0xf0,0x9e,0xa4,0xbf,0x00,0x10,0x09,0x12,0xff,0xf0,0x9e,0xa5,0x80,0x00,
+ 0x12,0xff,0xf0,0x9e,0xa5,0x81,0x00,0x94,0x1e,0x93,0x1a,0x92,0x16,0x91,0x12,0x10,
+ 0x09,0x12,0xff,0xf0,0x9e,0xa5,0x82,0x00,0x12,0xff,0xf0,0x9e,0xa5,0x83,0x00,0x12,
+ 0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* nfdi_c0100 */
+ 0x57,0x04,0x01,0x00,0xc6,0xe5,0xac,0x13,0xe4,0x41,0x0c,0xe3,0x7a,0x07,0xe2,0xf3,
+ 0x01,0xc1,0xd0,0x1f,0xcf,0x86,0x55,0x04,0x01,0x00,0x94,0x15,0x53,0x04,0x01,0x00,
+ 0x52,0x04,0x01,0x00,0x91,0x09,0x10,0x04,0x01,0x00,0x01,0xff,0x00,0x01,0x00,0x01,
+ 0x00,0xcf,0x86,0xd5,0xe4,0xd4,0x7c,0xd3,0x3c,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x41,0xcc,0x80,0x00,0x01,0xff,0x41,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x41,
+ 0xcc,0x82,0x00,0x01,0xff,0x41,0xcc,0x83,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x41,
+ 0xcc,0x88,0x00,0x01,0xff,0x41,0xcc,0x8a,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0x43,
+ 0xcc,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x45,0xcc,0x80,0x00,0x01,
+ 0xff,0x45,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x45,0xcc,0x82,0x00,0x01,0xff,0x45,
+ 0xcc,0x88,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x49,0xcc,0x80,0x00,0x01,0xff,0x49,
+ 0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x49,0xcc,0x82,0x00,0x01,0xff,0x49,0xcc,0x88,
+ 0x00,0xd3,0x38,0xd2,0x1c,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0x4e,0xcc,0x83,
+ 0x00,0x10,0x08,0x01,0xff,0x4f,0xcc,0x80,0x00,0x01,0xff,0x4f,0xcc,0x81,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x4f,0xcc,0x82,0x00,0x01,0xff,0x4f,0xcc,0x83,0x00,0x10,
+ 0x08,0x01,0xff,0x4f,0xcc,0x88,0x00,0x01,0x00,0xd2,0x1c,0xd1,0x0c,0x10,0x04,0x01,
+ 0x00,0x01,0xff,0x55,0xcc,0x80,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0x81,0x00,0x01,
+ 0xff,0x55,0xcc,0x82,0x00,0x91,0x10,0x10,0x08,0x01,0xff,0x55,0xcc,0x88,0x00,0x01,
+ 0xff,0x59,0xcc,0x81,0x00,0x01,0x00,0xd4,0x7c,0xd3,0x3c,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x61,0xcc,0x80,0x00,0x01,0xff,0x61,0xcc,0x81,0x00,0x10,0x08,0x01,
+ 0xff,0x61,0xcc,0x82,0x00,0x01,0xff,0x61,0xcc,0x83,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x61,0xcc,0x88,0x00,0x01,0xff,0x61,0xcc,0x8a,0x00,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0x63,0xcc,0xa7,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x65,0xcc,0x80,
+ 0x00,0x01,0xff,0x65,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x65,0xcc,0x82,0x00,0x01,
+ 0xff,0x65,0xcc,0x88,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x69,0xcc,0x80,0x00,0x01,
+ 0xff,0x69,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x69,0xcc,0x82,0x00,0x01,0xff,0x69,
+ 0xcc,0x88,0x00,0xd3,0x38,0xd2,0x1c,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0x6e,
+ 0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x80,0x00,0x01,0xff,0x6f,0xcc,0x81,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x6f,0xcc,0x82,0x00,0x01,0xff,0x6f,0xcc,0x83,
+ 0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x88,0x00,0x01,0x00,0xd2,0x1c,0xd1,0x0c,0x10,
+ 0x04,0x01,0x00,0x01,0xff,0x75,0xcc,0x80,0x00,0x10,0x08,0x01,0xff,0x75,0xcc,0x81,
+ 0x00,0x01,0xff,0x75,0xcc,0x82,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x75,0xcc,0x88,
+ 0x00,0x01,0xff,0x79,0xcc,0x81,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0x79,0xcc,0x88,
+ 0x00,0xe1,0x9a,0x03,0xe0,0xd3,0x01,0xcf,0x86,0xd5,0xf4,0xd4,0x80,0xd3,0x40,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x41,0xcc,0x84,0x00,0x01,0xff,0x61,0xcc,0x84,
+ 0x00,0x10,0x08,0x01,0xff,0x41,0xcc,0x86,0x00,0x01,0xff,0x61,0xcc,0x86,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x41,0xcc,0xa8,0x00,0x01,0xff,0x61,0xcc,0xa8,0x00,0x10,
+ 0x08,0x01,0xff,0x43,0xcc,0x81,0x00,0x01,0xff,0x63,0xcc,0x81,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x43,0xcc,0x82,0x00,0x01,0xff,0x63,0xcc,0x82,0x00,0x10,
+ 0x08,0x01,0xff,0x43,0xcc,0x87,0x00,0x01,0xff,0x63,0xcc,0x87,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x43,0xcc,0x8c,0x00,0x01,0xff,0x63,0xcc,0x8c,0x00,0x10,0x08,0x01,
+ 0xff,0x44,0xcc,0x8c,0x00,0x01,0xff,0x64,0xcc,0x8c,0x00,0xd3,0x34,0xd2,0x14,0x51,
+ 0x04,0x01,0x00,0x10,0x08,0x01,0xff,0x45,0xcc,0x84,0x00,0x01,0xff,0x65,0xcc,0x84,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x45,0xcc,0x86,0x00,0x01,0xff,0x65,0xcc,0x86,
+ 0x00,0x10,0x08,0x01,0xff,0x45,0xcc,0x87,0x00,0x01,0xff,0x65,0xcc,0x87,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x45,0xcc,0xa8,0x00,0x01,0xff,0x65,0xcc,0xa8,
+ 0x00,0x10,0x08,0x01,0xff,0x45,0xcc,0x8c,0x00,0x01,0xff,0x65,0xcc,0x8c,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x47,0xcc,0x82,0x00,0x01,0xff,0x67,0xcc,0x82,0x00,0x10,
+ 0x08,0x01,0xff,0x47,0xcc,0x86,0x00,0x01,0xff,0x67,0xcc,0x86,0x00,0xd4,0x74,0xd3,
+ 0x34,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x47,0xcc,0x87,0x00,0x01,0xff,0x67,
+ 0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x47,0xcc,0xa7,0x00,0x01,0xff,0x67,0xcc,0xa7,
+ 0x00,0x91,0x10,0x10,0x08,0x01,0xff,0x48,0xcc,0x82,0x00,0x01,0xff,0x68,0xcc,0x82,
+ 0x00,0x01,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x49,0xcc,0x83,0x00,0x01,
+ 0xff,0x69,0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x49,0xcc,0x84,0x00,0x01,0xff,0x69,
+ 0xcc,0x84,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x49,0xcc,0x86,0x00,0x01,0xff,0x69,
+ 0xcc,0x86,0x00,0x10,0x08,0x01,0xff,0x49,0xcc,0xa8,0x00,0x01,0xff,0x69,0xcc,0xa8,
+ 0x00,0xd3,0x30,0xd2,0x10,0x91,0x0c,0x10,0x08,0x01,0xff,0x49,0xcc,0x87,0x00,0x01,
+ 0x00,0x01,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x4a,0xcc,0x82,0x00,0x01,0xff,0x6a,
+ 0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x4b,0xcc,0xa7,0x00,0x01,0xff,0x6b,0xcc,0xa7,
+ 0x00,0xd2,0x1c,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0x4c,0xcc,0x81,0x00,0x10,
+ 0x08,0x01,0xff,0x6c,0xcc,0x81,0x00,0x01,0xff,0x4c,0xcc,0xa7,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x6c,0xcc,0xa7,0x00,0x01,0xff,0x4c,0xcc,0x8c,0x00,0x10,0x08,0x01,
+ 0xff,0x6c,0xcc,0x8c,0x00,0x01,0x00,0xcf,0x86,0xd5,0xd4,0xd4,0x60,0xd3,0x30,0xd2,
+ 0x10,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0x4e,0xcc,0x81,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x6e,0xcc,0x81,0x00,0x01,0xff,0x4e,0xcc,0xa7,0x00,0x10,
+ 0x08,0x01,0xff,0x6e,0xcc,0xa7,0x00,0x01,0xff,0x4e,0xcc,0x8c,0x00,0xd2,0x10,0x91,
+ 0x0c,0x10,0x08,0x01,0xff,0x6e,0xcc,0x8c,0x00,0x01,0x00,0x01,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x4f,0xcc,0x84,0x00,0x01,0xff,0x6f,0xcc,0x84,0x00,0x10,0x08,0x01,
+ 0xff,0x4f,0xcc,0x86,0x00,0x01,0xff,0x6f,0xcc,0x86,0x00,0xd3,0x34,0xd2,0x14,0x91,
+ 0x10,0x10,0x08,0x01,0xff,0x4f,0xcc,0x8b,0x00,0x01,0xff,0x6f,0xcc,0x8b,0x00,0x01,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x52,0xcc,0x81,0x00,0x01,0xff,0x72,0xcc,0x81,
+ 0x00,0x10,0x08,0x01,0xff,0x52,0xcc,0xa7,0x00,0x01,0xff,0x72,0xcc,0xa7,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x52,0xcc,0x8c,0x00,0x01,0xff,0x72,0xcc,0x8c,
+ 0x00,0x10,0x08,0x01,0xff,0x53,0xcc,0x81,0x00,0x01,0xff,0x73,0xcc,0x81,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x53,0xcc,0x82,0x00,0x01,0xff,0x73,0xcc,0x82,0x00,0x10,
+ 0x08,0x01,0xff,0x53,0xcc,0xa7,0x00,0x01,0xff,0x73,0xcc,0xa7,0x00,0xd4,0x74,0xd3,
+ 0x34,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x53,0xcc,0x8c,0x00,0x01,0xff,0x73,
+ 0xcc,0x8c,0x00,0x10,0x08,0x01,0xff,0x54,0xcc,0xa7,0x00,0x01,0xff,0x74,0xcc,0xa7,
+ 0x00,0x91,0x10,0x10,0x08,0x01,0xff,0x54,0xcc,0x8c,0x00,0x01,0xff,0x74,0xcc,0x8c,
+ 0x00,0x01,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x55,0xcc,0x83,0x00,0x01,
+ 0xff,0x75,0xcc,0x83,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0x84,0x00,0x01,0xff,0x75,
+ 0xcc,0x84,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x55,0xcc,0x86,0x00,0x01,0xff,0x75,
+ 0xcc,0x86,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0x8a,0x00,0x01,0xff,0x75,0xcc,0x8a,
+ 0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x55,0xcc,0x8b,0x00,0x01,
+ 0xff,0x75,0xcc,0x8b,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0xa8,0x00,0x01,0xff,0x75,
+ 0xcc,0xa8,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x57,0xcc,0x82,0x00,0x01,0xff,0x77,
+ 0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x59,0xcc,0x82,0x00,0x01,0xff,0x79,0xcc,0x82,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x59,0xcc,0x88,0x00,0x01,0xff,0x5a,
+ 0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x7a,0xcc,0x81,0x00,0x01,0xff,0x5a,0xcc,0x87,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x7a,0xcc,0x87,0x00,0x01,0xff,0x5a,0xcc,0x8c,
+ 0x00,0x10,0x08,0x01,0xff,0x7a,0xcc,0x8c,0x00,0x01,0x00,0xd0,0x4a,0xcf,0x86,0x55,
+ 0x04,0x01,0x00,0xd4,0x2c,0xd3,0x18,0x92,0x14,0x91,0x10,0x10,0x08,0x01,0xff,0x4f,
+ 0xcc,0x9b,0x00,0x01,0xff,0x6f,0xcc,0x9b,0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,
+ 0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0x55,0xcc,0x9b,0x00,0x93,
+ 0x14,0x92,0x10,0x91,0x0c,0x10,0x08,0x01,0xff,0x75,0xcc,0x9b,0x00,0x01,0x00,0x01,
+ 0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0xb4,0xd4,0x24,0x53,0x04,0x01,0x00,0x52,
+ 0x04,0x01,0x00,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0x41,0xcc,0x8c,0x00,0x10,
+ 0x08,0x01,0xff,0x61,0xcc,0x8c,0x00,0x01,0xff,0x49,0xcc,0x8c,0x00,0xd3,0x46,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x69,0xcc,0x8c,0x00,0x01,0xff,0x4f,0xcc,0x8c,
+ 0x00,0x10,0x08,0x01,0xff,0x6f,0xcc,0x8c,0x00,0x01,0xff,0x55,0xcc,0x8c,0x00,0xd1,
+ 0x12,0x10,0x08,0x01,0xff,0x75,0xcc,0x8c,0x00,0x01,0xff,0x55,0xcc,0x88,0xcc,0x84,
+ 0x00,0x10,0x0a,0x01,0xff,0x75,0xcc,0x88,0xcc,0x84,0x00,0x01,0xff,0x55,0xcc,0x88,
+ 0xcc,0x81,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x75,0xcc,0x88,0xcc,0x81,
+ 0x00,0x01,0xff,0x55,0xcc,0x88,0xcc,0x8c,0x00,0x10,0x0a,0x01,0xff,0x75,0xcc,0x88,
+ 0xcc,0x8c,0x00,0x01,0xff,0x55,0xcc,0x88,0xcc,0x80,0x00,0xd1,0x0e,0x10,0x0a,0x01,
+ 0xff,0x75,0xcc,0x88,0xcc,0x80,0x00,0x01,0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0x88,
+ 0xcc,0x84,0x00,0x01,0xff,0x61,0xcc,0x88,0xcc,0x84,0x00,0xd4,0x80,0xd3,0x3a,0xd2,
+ 0x26,0xd1,0x14,0x10,0x0a,0x01,0xff,0x41,0xcc,0x87,0xcc,0x84,0x00,0x01,0xff,0x61,
+ 0xcc,0x87,0xcc,0x84,0x00,0x10,0x09,0x01,0xff,0xc3,0x86,0xcc,0x84,0x00,0x01,0xff,
+ 0xc3,0xa6,0xcc,0x84,0x00,0x51,0x04,0x01,0x00,0x10,0x08,0x01,0xff,0x47,0xcc,0x8c,
+ 0x00,0x01,0xff,0x67,0xcc,0x8c,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x4b,
+ 0xcc,0x8c,0x00,0x01,0xff,0x6b,0xcc,0x8c,0x00,0x10,0x08,0x01,0xff,0x4f,0xcc,0xa8,
+ 0x00,0x01,0xff,0x6f,0xcc,0xa8,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,0xcc,0xa8,
+ 0xcc,0x84,0x00,0x01,0xff,0x6f,0xcc,0xa8,0xcc,0x84,0x00,0x10,0x09,0x01,0xff,0xc6,
+ 0xb7,0xcc,0x8c,0x00,0x01,0xff,0xca,0x92,0xcc,0x8c,0x00,0xd3,0x24,0xd2,0x10,0x91,
+ 0x0c,0x10,0x08,0x01,0xff,0x6a,0xcc,0x8c,0x00,0x01,0x00,0x01,0x00,0x91,0x10,0x10,
+ 0x08,0x01,0xff,0x47,0xcc,0x81,0x00,0x01,0xff,0x67,0xcc,0x81,0x00,0x04,0x00,0xd2,
+ 0x24,0xd1,0x10,0x10,0x08,0x04,0xff,0x4e,0xcc,0x80,0x00,0x04,0xff,0x6e,0xcc,0x80,
+ 0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0x8a,0xcc,0x81,0x00,0x01,0xff,0x61,0xcc,0x8a,
+ 0xcc,0x81,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xc3,0x86,0xcc,0x81,0x00,0x01,0xff,
+ 0xc3,0xa6,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xc3,0x98,0xcc,0x81,0x00,0x01,0xff,
+ 0xc3,0xb8,0xcc,0x81,0x00,0xe2,0x07,0x02,0xe1,0xae,0x01,0xe0,0x93,0x01,0xcf,0x86,
+ 0xd5,0xf4,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x41,0xcc,
+ 0x8f,0x00,0x01,0xff,0x61,0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x41,0xcc,0x91,0x00,
+ 0x01,0xff,0x61,0xcc,0x91,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x45,0xcc,0x8f,0x00,
+ 0x01,0xff,0x65,0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x45,0xcc,0x91,0x00,0x01,0xff,
+ 0x65,0xcc,0x91,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x49,0xcc,0x8f,0x00,
+ 0x01,0xff,0x69,0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x49,0xcc,0x91,0x00,0x01,0xff,
+ 0x69,0xcc,0x91,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x4f,0xcc,0x8f,0x00,0x01,0xff,
+ 0x6f,0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x4f,0xcc,0x91,0x00,0x01,0xff,0x6f,0xcc,
+ 0x91,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x52,0xcc,0x8f,0x00,
+ 0x01,0xff,0x72,0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x52,0xcc,0x91,0x00,0x01,0xff,
+ 0x72,0xcc,0x91,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x55,0xcc,0x8f,0x00,0x01,0xff,
+ 0x75,0xcc,0x8f,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0x91,0x00,0x01,0xff,0x75,0xcc,
+ 0x91,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x04,0xff,0x53,0xcc,0xa6,0x00,0x04,0xff,
+ 0x73,0xcc,0xa6,0x00,0x10,0x08,0x04,0xff,0x54,0xcc,0xa6,0x00,0x04,0xff,0x74,0xcc,
+ 0xa6,0x00,0x51,0x04,0x04,0x00,0x10,0x08,0x04,0xff,0x48,0xcc,0x8c,0x00,0x04,0xff,
+ 0x68,0xcc,0x8c,0x00,0xd4,0x68,0xd3,0x20,0xd2,0x0c,0x91,0x08,0x10,0x04,0x06,0x00,
+ 0x07,0x00,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x08,0x04,0xff,0x41,0xcc,0x87,0x00,
+ 0x04,0xff,0x61,0xcc,0x87,0x00,0xd2,0x24,0xd1,0x10,0x10,0x08,0x04,0xff,0x45,0xcc,
+ 0xa7,0x00,0x04,0xff,0x65,0xcc,0xa7,0x00,0x10,0x0a,0x04,0xff,0x4f,0xcc,0x88,0xcc,
+ 0x84,0x00,0x04,0xff,0x6f,0xcc,0x88,0xcc,0x84,0x00,0xd1,0x14,0x10,0x0a,0x04,0xff,
+ 0x4f,0xcc,0x83,0xcc,0x84,0x00,0x04,0xff,0x6f,0xcc,0x83,0xcc,0x84,0x00,0x10,0x08,
+ 0x04,0xff,0x4f,0xcc,0x87,0x00,0x04,0xff,0x6f,0xcc,0x87,0x00,0x93,0x30,0xd2,0x24,
+ 0xd1,0x14,0x10,0x0a,0x04,0xff,0x4f,0xcc,0x87,0xcc,0x84,0x00,0x04,0xff,0x6f,0xcc,
+ 0x87,0xcc,0x84,0x00,0x10,0x08,0x04,0xff,0x59,0xcc,0x84,0x00,0x04,0xff,0x79,0xcc,
+ 0x84,0x00,0x51,0x04,0x07,0x00,0x10,0x04,0x07,0x00,0x08,0x00,0x08,0x00,0xcf,0x86,
+ 0x95,0x14,0x94,0x10,0x93,0x0c,0x92,0x08,0x11,0x04,0x08,0x00,0x09,0x00,0x09,0x00,
+ 0x09,0x00,0x01,0x00,0x01,0x00,0xd0,0x22,0xcf,0x86,0x55,0x04,0x01,0x00,0x94,0x18,
+ 0x53,0x04,0x01,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x04,0x00,0x04,0x00,
+ 0x11,0x04,0x04,0x00,0x07,0x00,0x01,0x00,0xcf,0x86,0xd5,0x18,0x54,0x04,0x01,0x00,
+ 0x53,0x04,0x01,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,
+ 0x04,0x00,0x94,0x18,0x53,0x04,0x01,0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x04,0x00,
+ 0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x07,0x00,0x07,0x00,0xe1,0x35,0x01,0xd0,
+ 0x72,0xcf,0x86,0xd5,0x24,0x54,0x04,0x01,0xe6,0xd3,0x10,0x52,0x04,0x01,0xe6,0x91,
+ 0x08,0x10,0x04,0x01,0xe6,0x01,0xe8,0x01,0xdc,0x92,0x0c,0x51,0x04,0x01,0xdc,0x10,
+ 0x04,0x01,0xe8,0x01,0xd8,0x01,0xdc,0xd4,0x2c,0xd3,0x1c,0xd2,0x10,0xd1,0x08,0x10,
+ 0x04,0x01,0xdc,0x01,0xca,0x10,0x04,0x01,0xca,0x01,0xdc,0x51,0x04,0x01,0xdc,0x10,
+ 0x04,0x01,0xdc,0x01,0xca,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0xca,0x01,0xdc,0x01,
+ 0xdc,0x01,0xdc,0xd3,0x08,0x12,0x04,0x01,0xdc,0x01,0x01,0xd2,0x0c,0x91,0x08,0x10,
+ 0x04,0x01,0x01,0x01,0xdc,0x01,0xdc,0x91,0x08,0x10,0x04,0x01,0xdc,0x01,0xe6,0x01,
+ 0xe6,0xcf,0x86,0xd5,0x7f,0xd4,0x47,0xd3,0x2e,0xd2,0x19,0xd1,0x0e,0x10,0x07,0x01,
+ 0xff,0xcc,0x80,0x00,0x01,0xff,0xcc,0x81,0x00,0x10,0x04,0x01,0xe6,0x01,0xff,0xcc,
+ 0x93,0x00,0xd1,0x0d,0x10,0x09,0x01,0xff,0xcc,0x88,0xcc,0x81,0x00,0x01,0xf0,0x10,
+ 0x04,0x04,0xe6,0x04,0xdc,0xd2,0x08,0x11,0x04,0x04,0xdc,0x04,0xe6,0xd1,0x08,0x10,
+ 0x04,0x04,0xe6,0x04,0xdc,0x10,0x04,0x04,0xdc,0x06,0xff,0x00,0xd3,0x18,0xd2,0x0c,
+ 0x51,0x04,0x07,0xe6,0x10,0x04,0x07,0xe6,0x07,0xdc,0x51,0x04,0x07,0xdc,0x10,0x04,
+ 0x07,0xdc,0x07,0xe6,0xd2,0x10,0xd1,0x08,0x10,0x04,0x08,0xe8,0x08,0xdc,0x10,0x04,
+ 0x08,0xdc,0x08,0xe6,0xd1,0x08,0x10,0x04,0x08,0xe9,0x07,0xea,0x10,0x04,0x07,0xea,
+ 0x07,0xe9,0xd4,0x14,0x93,0x10,0x92,0x0c,0x51,0x04,0x01,0xea,0x10,0x04,0x04,0xe9,
+ 0x06,0xe6,0x06,0xe6,0x06,0xe6,0xd3,0x13,0x52,0x04,0x0a,0x00,0x91,0x0b,0x10,0x07,
+ 0x01,0xff,0xca,0xb9,0x00,0x01,0x00,0x0a,0x00,0xd2,0x0c,0x51,0x04,0x00,0x00,0x10,
+ 0x04,0x01,0x00,0x09,0x00,0x51,0x04,0x09,0x00,0x10,0x06,0x01,0xff,0x3b,0x00,0x10,
+ 0x00,0xd0,0xe1,0xcf,0x86,0xd5,0x7a,0xd4,0x5f,0xd3,0x21,0x52,0x04,0x00,0x00,0xd1,
+ 0x0d,0x10,0x04,0x01,0x00,0x01,0xff,0xc2,0xa8,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,
+ 0xce,0x91,0xcc,0x81,0x00,0x01,0xff,0xc2,0xb7,0x00,0xd2,0x1f,0xd1,0x12,0x10,0x09,
+ 0x01,0xff,0xce,0x95,0xcc,0x81,0x00,0x01,0xff,0xce,0x97,0xcc,0x81,0x00,0x10,0x09,
+ 0x01,0xff,0xce,0x99,0xcc,0x81,0x00,0x00,0x00,0xd1,0x0d,0x10,0x09,0x01,0xff,0xce,
+ 0x9f,0xcc,0x81,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xce,0xa5,0xcc,0x81,0x00,0x01,
+ 0xff,0xce,0xa9,0xcc,0x81,0x00,0x93,0x17,0x92,0x13,0x91,0x0f,0x10,0x0b,0x01,0xff,
+ 0xce,0xb9,0xcc,0x88,0xcc,0x81,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,
+ 0x4a,0xd3,0x10,0x92,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0x01,
+ 0x00,0xd2,0x16,0x51,0x04,0x01,0x00,0x10,0x09,0x01,0xff,0xce,0x99,0xcc,0x88,0x00,
+ 0x01,0xff,0xce,0xa5,0xcc,0x88,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,
+ 0x81,0x00,0x01,0xff,0xce,0xb5,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xce,0xb7,0xcc,
+ 0x81,0x00,0x01,0xff,0xce,0xb9,0xcc,0x81,0x00,0x93,0x17,0x92,0x13,0x91,0x0f,0x10,
+ 0x0b,0x01,0xff,0xcf,0x85,0xcc,0x88,0xcc,0x81,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0xcf,0x86,0xd5,0x7b,0xd4,0x39,0x53,0x04,0x01,0x00,0xd2,0x16,0x51,0x04,
+ 0x01,0x00,0x10,0x09,0x01,0xff,0xce,0xb9,0xcc,0x88,0x00,0x01,0xff,0xcf,0x85,0xcc,
+ 0x88,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xbf,0xcc,0x81,0x00,0x01,0xff,0xcf,
+ 0x85,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xcf,0x89,0xcc,0x81,0x00,0x0a,0x00,0xd3,
+ 0x26,0xd2,0x11,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xcf,0x92,0xcc,
+ 0x81,0x00,0xd1,0x0d,0x10,0x09,0x01,0xff,0xcf,0x92,0xcc,0x88,0x00,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x04,0x00,0xd2,0x0c,0x51,0x04,0x06,0x00,0x10,0x04,0x01,0x00,0x04,
+ 0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x04,0x00,0x10,0x04,0x01,0x00,0x04,0x00,0xd4,
+ 0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x04,0x00,0x01,0x00,0x01,
+ 0x00,0x01,0x00,0xd3,0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x05,0x00,0x10,0x04,0x06,
+ 0x00,0x07,0x00,0x12,0x04,0x07,0x00,0x08,0x00,0xe3,0x47,0x04,0xe2,0xbe,0x02,0xe1,
+ 0x07,0x01,0xd0,0x8b,0xcf,0x86,0xd5,0x6c,0xd4,0x53,0xd3,0x30,0xd2,0x1f,0xd1,0x12,
+ 0x10,0x09,0x04,0xff,0xd0,0x95,0xcc,0x80,0x00,0x01,0xff,0xd0,0x95,0xcc,0x88,0x00,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0xd0,0x93,0xcc,0x81,0x00,0x51,0x04,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x01,0xff,0xd0,0x86,0xcc,0x88,0x00,0x52,0x04,0x01,0x00,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xd0,0x9a,0xcc,0x81,0x00,0x04,0xff,0xd0,0x98,0xcc,0x80,0x00,
+ 0x10,0x09,0x01,0xff,0xd0,0xa3,0xcc,0x86,0x00,0x01,0x00,0x53,0x04,0x01,0x00,0x92,
+ 0x11,0x91,0x0d,0x10,0x04,0x01,0x00,0x01,0xff,0xd0,0x98,0xcc,0x86,0x00,0x01,0x00,
+ 0x01,0x00,0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,0x92,0x11,0x91,0x0d,0x10,0x04,
+ 0x01,0x00,0x01,0xff,0xd0,0xb8,0xcc,0x86,0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,
+ 0x57,0x54,0x04,0x01,0x00,0xd3,0x30,0xd2,0x1f,0xd1,0x12,0x10,0x09,0x04,0xff,0xd0,
+ 0xb5,0xcc,0x80,0x00,0x01,0xff,0xd0,0xb5,0xcc,0x88,0x00,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0xd0,0xb3,0xcc,0x81,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,
+ 0xd1,0x96,0xcc,0x88,0x00,0x52,0x04,0x01,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xd0,
+ 0xba,0xcc,0x81,0x00,0x04,0xff,0xd0,0xb8,0xcc,0x80,0x00,0x10,0x09,0x01,0xff,0xd1,
+ 0x83,0xcc,0x86,0x00,0x01,0x00,0x54,0x04,0x01,0x00,0x93,0x1a,0x52,0x04,0x01,0x00,
+ 0x51,0x04,0x01,0x00,0x10,0x09,0x01,0xff,0xd1,0xb4,0xcc,0x8f,0x00,0x01,0xff,0xd1,
+ 0xb5,0xcc,0x8f,0x00,0x01,0x00,0xd0,0x2e,0xcf,0x86,0x95,0x28,0x94,0x24,0xd3,0x18,
+ 0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xe6,0x51,0x04,0x01,0xe6,
+ 0x10,0x04,0x01,0xe6,0x0a,0xe6,0x92,0x08,0x11,0x04,0x04,0x00,0x06,0x00,0x04,0x00,
+ 0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0xbe,0xd4,0x4a,0xd3,0x2a,0xd2,0x1a,0xd1,0x0d,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0xd0,0x96,0xcc,0x86,0x00,0x10,0x09,0x01,0xff,0xd0,
+ 0xb6,0xcc,0x86,0x00,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x06,0x00,0x10,0x04,
+ 0x06,0x00,0x01,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x06,0x00,0x10,0x04,
+ 0x06,0x00,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x06,0x00,0x10,0x04,0x06,0x00,
+ 0x09,0x00,0xd3,0x3a,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xd0,0x90,0xcc,0x86,
+ 0x00,0x01,0xff,0xd0,0xb0,0xcc,0x86,0x00,0x10,0x09,0x01,0xff,0xd0,0x90,0xcc,0x88,
+ 0x00,0x01,0xff,0xd0,0xb0,0xcc,0x88,0x00,0x51,0x04,0x01,0x00,0x10,0x09,0x01,0xff,
+ 0xd0,0x95,0xcc,0x86,0x00,0x01,0xff,0xd0,0xb5,0xcc,0x86,0x00,0xd2,0x16,0x51,0x04,
+ 0x01,0x00,0x10,0x09,0x01,0xff,0xd3,0x98,0xcc,0x88,0x00,0x01,0xff,0xd3,0x99,0xcc,
+ 0x88,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xd0,0x96,0xcc,0x88,0x00,0x01,0xff,0xd0,
+ 0xb6,0xcc,0x88,0x00,0x10,0x09,0x01,0xff,0xd0,0x97,0xcc,0x88,0x00,0x01,0xff,0xd0,
+ 0xb7,0xcc,0x88,0x00,0xd4,0x74,0xd3,0x3a,0xd2,0x16,0x51,0x04,0x01,0x00,0x10,0x09,
+ 0x01,0xff,0xd0,0x98,0xcc,0x84,0x00,0x01,0xff,0xd0,0xb8,0xcc,0x84,0x00,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xd0,0x98,0xcc,0x88,0x00,0x01,0xff,0xd0,0xb8,0xcc,0x88,0x00,
+ 0x10,0x09,0x01,0xff,0xd0,0x9e,0xcc,0x88,0x00,0x01,0xff,0xd0,0xbe,0xcc,0x88,0x00,
+ 0xd2,0x16,0x51,0x04,0x01,0x00,0x10,0x09,0x01,0xff,0xd3,0xa8,0xcc,0x88,0x00,0x01,
+ 0xff,0xd3,0xa9,0xcc,0x88,0x00,0xd1,0x12,0x10,0x09,0x04,0xff,0xd0,0xad,0xcc,0x88,
+ 0x00,0x04,0xff,0xd1,0x8d,0xcc,0x88,0x00,0x10,0x09,0x01,0xff,0xd0,0xa3,0xcc,0x84,
+ 0x00,0x01,0xff,0xd1,0x83,0xcc,0x84,0x00,0xd3,0x3a,0xd2,0x24,0xd1,0x12,0x10,0x09,
+ 0x01,0xff,0xd0,0xa3,0xcc,0x88,0x00,0x01,0xff,0xd1,0x83,0xcc,0x88,0x00,0x10,0x09,
+ 0x01,0xff,0xd0,0xa3,0xcc,0x8b,0x00,0x01,0xff,0xd1,0x83,0xcc,0x8b,0x00,0x91,0x12,
+ 0x10,0x09,0x01,0xff,0xd0,0xa7,0xcc,0x88,0x00,0x01,0xff,0xd1,0x87,0xcc,0x88,0x00,
+ 0x08,0x00,0x92,0x16,0x91,0x12,0x10,0x09,0x01,0xff,0xd0,0xab,0xcc,0x88,0x00,0x01,
+ 0xff,0xd1,0x8b,0xcc,0x88,0x00,0x09,0x00,0x09,0x00,0xd1,0x74,0xd0,0x36,0xcf,0x86,
+ 0xd5,0x10,0x54,0x04,0x06,0x00,0x93,0x08,0x12,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,
+ 0xd4,0x10,0x93,0x0c,0x52,0x04,0x0a,0x00,0x11,0x04,0x0b,0x00,0x0c,0x00,0x10,0x00,
+ 0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0xcf,0x86,0xd5,0x24,0x54,0x04,0x01,0x00,0xd3,0x10,0x52,0x04,0x01,0x00,
+ 0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x14,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd0,0xba,
+ 0xcf,0x86,0xd5,0x4c,0xd4,0x24,0x53,0x04,0x01,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,
+ 0x14,0x00,0x01,0x00,0x10,0x04,0x04,0x00,0x00,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,
+ 0x10,0x00,0x10,0x04,0x10,0x00,0x0d,0x00,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,
+ 0x00,0x00,0x02,0xdc,0x02,0xe6,0x51,0x04,0x02,0xe6,0x10,0x04,0x02,0xdc,0x02,0xe6,
+ 0x92,0x0c,0x51,0x04,0x02,0xe6,0x10,0x04,0x02,0xde,0x02,0xdc,0x02,0xe6,0xd4,0x2c,
+ 0xd3,0x10,0x92,0x0c,0x51,0x04,0x02,0xe6,0x10,0x04,0x08,0xdc,0x02,0xdc,0x02,0xdc,
+ 0xd2,0x0c,0x51,0x04,0x02,0xe6,0x10,0x04,0x02,0xdc,0x02,0xe6,0xd1,0x08,0x10,0x04,
+ 0x02,0xe6,0x02,0xde,0x10,0x04,0x02,0xe4,0x02,0xe6,0xd3,0x20,0xd2,0x10,0xd1,0x08,
+ 0x10,0x04,0x01,0x0a,0x01,0x0b,0x10,0x04,0x01,0x0c,0x01,0x0d,0xd1,0x08,0x10,0x04,
+ 0x01,0x0e,0x01,0x0f,0x10,0x04,0x01,0x10,0x01,0x11,0xd2,0x10,0xd1,0x08,0x10,0x04,
+ 0x01,0x12,0x01,0x13,0x10,0x04,0x09,0x13,0x01,0x14,0xd1,0x08,0x10,0x04,0x01,0x15,
+ 0x01,0x16,0x10,0x04,0x01,0x00,0x01,0x17,0xcf,0x86,0xd5,0x28,0x94,0x24,0x93,0x20,
+ 0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x01,0x18,0x10,0x04,0x01,0x19,0x01,0x00,
+ 0xd1,0x08,0x10,0x04,0x02,0xe6,0x08,0xdc,0x10,0x04,0x08,0x00,0x08,0x12,0x00,0x00,
+ 0x01,0x00,0xd4,0x1c,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,
+ 0x01,0x00,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x14,0x00,0x93,0x10,
+ 0x52,0x04,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xe2,0xfb,0x01,0xe1,0x2b,0x01,0xd0,0xa8,0xcf,0x86,0xd5,0x55,0xd4,0x28,0xd3,0x10,
+ 0x52,0x04,0x07,0x00,0x91,0x08,0x10,0x04,0x0d,0x00,0x10,0x00,0x0a,0x00,0xd2,0x0c,
+ 0x51,0x04,0x0a,0x00,0x10,0x04,0x0a,0x00,0x08,0x00,0x91,0x08,0x10,0x04,0x01,0x00,
+ 0x07,0x00,0x07,0x00,0xd3,0x0c,0x52,0x04,0x07,0xe6,0x11,0x04,0x07,0xe6,0x0a,0xe6,
+ 0xd2,0x10,0xd1,0x08,0x10,0x04,0x0a,0x1e,0x0a,0x1f,0x10,0x04,0x0a,0x20,0x01,0x00,
+ 0xd1,0x09,0x10,0x05,0x0f,0xff,0x00,0x00,0x00,0x10,0x04,0x08,0x00,0x01,0x00,0xd4,
+ 0x3d,0x93,0x39,0xd2,0x1a,0xd1,0x08,0x10,0x04,0x0c,0x00,0x01,0x00,0x10,0x09,0x01,
+ 0xff,0xd8,0xa7,0xd9,0x93,0x00,0x01,0xff,0xd8,0xa7,0xd9,0x94,0x00,0xd1,0x12,0x10,
+ 0x09,0x01,0xff,0xd9,0x88,0xd9,0x94,0x00,0x01,0xff,0xd8,0xa7,0xd9,0x95,0x00,0x10,
+ 0x09,0x01,0xff,0xd9,0x8a,0xd9,0x94,0x00,0x01,0x00,0x01,0x00,0x53,0x04,0x01,0x00,
+ 0x92,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x0a,0x00,0x0a,0x00,0xcf,0x86,
+ 0xd5,0x5c,0xd4,0x20,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,
+ 0x01,0x00,0x01,0x1b,0xd1,0x08,0x10,0x04,0x01,0x1c,0x01,0x1d,0x10,0x04,0x01,0x1e,
+ 0x01,0x1f,0xd3,0x20,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x20,0x01,0x21,0x10,0x04,
+ 0x01,0x22,0x04,0xe6,0xd1,0x08,0x10,0x04,0x04,0xe6,0x04,0xdc,0x10,0x04,0x07,0xdc,
+ 0x07,0xe6,0xd2,0x0c,0x91,0x08,0x10,0x04,0x07,0xe6,0x08,0xe6,0x08,0xe6,0xd1,0x08,
+ 0x10,0x04,0x08,0xdc,0x08,0xe6,0x10,0x04,0x08,0xe6,0x0c,0xdc,0xd4,0x10,0x53,0x04,
+ 0x01,0x00,0x52,0x04,0x01,0x00,0x11,0x04,0x01,0x00,0x06,0x00,0x93,0x10,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x01,0x23,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd0,0x22,
+ 0xcf,0x86,0x55,0x04,0x01,0x00,0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,0xd2,0x08,
+ 0x11,0x04,0x04,0x00,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x04,0x00,
+ 0xcf,0x86,0xd5,0x5b,0xd4,0x2e,0xd3,0x1e,0x92,0x1a,0xd1,0x0d,0x10,0x09,0x01,0xff,
+ 0xdb,0x95,0xd9,0x94,0x00,0x01,0x00,0x10,0x09,0x01,0xff,0xdb,0x81,0xd9,0x94,0x00,
+ 0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,
+ 0x04,0x00,0xd3,0x19,0xd2,0x11,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,
+ 0xdb,0x92,0xd9,0x94,0x00,0x11,0x04,0x01,0x00,0x01,0xe6,0x52,0x04,0x01,0xe6,0xd1,
+ 0x08,0x10,0x04,0x01,0xe6,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xe6,0xd4,0x38,0xd3,
+ 0x1c,0xd2,0x0c,0x51,0x04,0x01,0xe6,0x10,0x04,0x01,0xe6,0x01,0xdc,0xd1,0x08,0x10,
+ 0x04,0x01,0xe6,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xe6,0xd2,0x10,0xd1,0x08,0x10,
+ 0x04,0x01,0xe6,0x01,0x00,0x10,0x04,0x01,0xdc,0x01,0xe6,0x91,0x08,0x10,0x04,0x01,
+ 0xe6,0x01,0xdc,0x07,0x00,0x53,0x04,0x01,0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x04,
+ 0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x07,0x00,0xd1,0xc8,0xd0,0x76,0xcf,
+ 0x86,0xd5,0x28,0xd4,0x14,0x53,0x04,0x04,0x00,0x52,0x04,0x04,0x00,0x51,0x04,0x04,
+ 0x00,0x10,0x04,0x00,0x00,0x04,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x04,
+ 0x00,0x04,0x24,0x04,0x00,0x04,0x00,0x04,0x00,0xd4,0x14,0x53,0x04,0x04,0x00,0x52,
+ 0x04,0x04,0x00,0x91,0x08,0x10,0x04,0x04,0x00,0x07,0x00,0x07,0x00,0xd3,0x1c,0xd2,
+ 0x0c,0x91,0x08,0x10,0x04,0x04,0xe6,0x04,0xdc,0x04,0xe6,0xd1,0x08,0x10,0x04,0x04,
+ 0xdc,0x04,0xe6,0x10,0x04,0x04,0xe6,0x04,0xdc,0xd2,0x0c,0x51,0x04,0x04,0xdc,0x10,
+ 0x04,0x04,0xe6,0x04,0xdc,0xd1,0x08,0x10,0x04,0x04,0xdc,0x04,0xe6,0x10,0x04,0x04,
+ 0xdc,0x04,0xe6,0xcf,0x86,0xd5,0x3c,0x94,0x38,0xd3,0x1c,0xd2,0x0c,0x51,0x04,0x04,
+ 0xe6,0x10,0x04,0x04,0xdc,0x04,0xe6,0xd1,0x08,0x10,0x04,0x04,0xdc,0x04,0xe6,0x10,
+ 0x04,0x04,0xdc,0x04,0xe6,0xd2,0x10,0xd1,0x08,0x10,0x04,0x04,0xdc,0x04,0xe6,0x10,
+ 0x04,0x04,0xe6,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x07,0x00,0x07,0x00,0x08,
+ 0x00,0x94,0x10,0x53,0x04,0x08,0x00,0x52,0x04,0x08,0x00,0x11,0x04,0x08,0x00,0x0a,
+ 0x00,0x0a,0x00,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x04,0x00,0x54,0x04,0x04,0x00,0x93,
+ 0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x04,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0xcf,0x86,0x55,0x04,0x09,0x00,0xd4,0x14,0x53,0x04,0x09,0x00,0x92,0x0c,0x51,
+ 0x04,0x09,0x00,0x10,0x04,0x09,0x00,0x09,0xe6,0x09,0xe6,0xd3,0x10,0x92,0x0c,0x51,
+ 0x04,0x09,0xe6,0x10,0x04,0x09,0xdc,0x09,0xe6,0x09,0x00,0xd2,0x0c,0x51,0x04,0x09,
+ 0x00,0x10,0x04,0x09,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x14,0xdc,0x14,
+ 0x00,0xe4,0xf8,0x57,0xe3,0x45,0x3f,0xe2,0xf4,0x3e,0xe1,0xc7,0x2c,0xe0,0x21,0x10,
+ 0xcf,0x86,0xc5,0xe4,0x80,0x08,0xe3,0xcb,0x03,0xe2,0x61,0x01,0xd1,0x94,0xd0,0x5a,
+ 0xcf,0x86,0xd5,0x20,0x54,0x04,0x0b,0x00,0xd3,0x0c,0x52,0x04,0x0b,0x00,0x11,0x04,
+ 0x0b,0x00,0x0b,0xe6,0x92,0x0c,0x51,0x04,0x0b,0xe6,0x10,0x04,0x0b,0x00,0x0b,0xe6,
+ 0x0b,0xe6,0xd4,0x24,0xd3,0x10,0x52,0x04,0x0b,0xe6,0x91,0x08,0x10,0x04,0x0b,0x00,
+ 0x0b,0xe6,0x0b,0xe6,0xd2,0x0c,0x91,0x08,0x10,0x04,0x0b,0x00,0x0b,0xe6,0x0b,0xe6,
+ 0x11,0x04,0x0b,0xe6,0x00,0x00,0x53,0x04,0x0b,0x00,0x52,0x04,0x0b,0x00,0x51,0x04,
+ 0x0b,0x00,0x10,0x04,0x0b,0x00,0x00,0x00,0xcf,0x86,0xd5,0x20,0x54,0x04,0x0c,0x00,
+ 0x53,0x04,0x0c,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x0c,0x00,0x0c,0xdc,0x0c,0xdc,
+ 0x51,0x04,0x00,0x00,0x10,0x04,0x0c,0x00,0x00,0x00,0x94,0x14,0x53,0x04,0x13,0x00,
+ 0x92,0x0c,0x51,0x04,0x13,0x00,0x10,0x04,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xd0,0x4a,0xcf,0x86,0x55,0x04,0x00,0x00,0xd4,0x20,0xd3,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x0d,0x00,0x10,0x00,0x0d,0x00,0x0d,0x00,0x52,0x04,0x0d,0x00,0x91,0x08,
+ 0x10,0x04,0x0d,0x00,0x10,0x00,0x10,0x00,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x10,0x00,
+ 0x10,0x04,0x10,0x00,0x11,0x00,0x91,0x08,0x10,0x04,0x11,0x00,0x00,0x00,0x12,0x00,
+ 0x52,0x04,0x12,0x00,0x11,0x04,0x12,0x00,0x00,0x00,0xcf,0x86,0xd5,0x18,0x54,0x04,
+ 0x00,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x14,0xdc,
+ 0x12,0xe6,0x12,0xe6,0xd4,0x30,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x12,0xe6,0x10,0x04,
+ 0x12,0x00,0x11,0xdc,0x51,0x04,0x0d,0xe6,0x10,0x04,0x0d,0xdc,0x0d,0xe6,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x0d,0xe6,0x0d,0xdc,0x0d,0xe6,0x91,0x08,0x10,0x04,0x0d,0xe6,
+ 0x0d,0xdc,0x0d,0xdc,0xd3,0x1c,0xd2,0x10,0xd1,0x08,0x10,0x04,0x0d,0x1b,0x0d,0x1c,
+ 0x10,0x04,0x0d,0x1d,0x0d,0xe6,0x51,0x04,0x0d,0xe6,0x10,0x04,0x0d,0xdc,0x0d,0xe6,
+ 0xd2,0x10,0xd1,0x08,0x10,0x04,0x0d,0xe6,0x0d,0xdc,0x10,0x04,0x0d,0xdc,0x0d,0xe6,
+ 0x51,0x04,0x0d,0xe6,0x10,0x04,0x0d,0xe6,0x10,0xe6,0xe1,0x3a,0x01,0xd0,0x77,0xcf,
+ 0x86,0xd5,0x20,0x94,0x1c,0x93,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x0b,0x00,0x01,
+ 0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x07,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,
+ 0x00,0xd4,0x1b,0x53,0x04,0x01,0x00,0x92,0x13,0x91,0x0f,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0xe0,0xa4,0xa8,0xe0,0xa4,0xbc,0x00,0x01,0x00,0x01,0x00,0xd3,0x26,0xd2,0x13,
+ 0x91,0x0f,0x10,0x04,0x01,0x00,0x01,0xff,0xe0,0xa4,0xb0,0xe0,0xa4,0xbc,0x00,0x01,
+ 0x00,0x91,0x0f,0x10,0x0b,0x01,0xff,0xe0,0xa4,0xb3,0xe0,0xa4,0xbc,0x00,0x01,0x00,
+ 0x01,0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x0c,0x00,0x91,0x08,0x10,0x04,0x01,0x07,
+ 0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0x8c,0xd4,0x18,0x53,0x04,0x01,0x00,0x52,0x04,
+ 0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x01,0x09,0x10,0x04,0x0b,0x00,0x0c,0x00,
+ 0xd3,0x1c,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x01,0xe6,0x10,0x04,0x01,0xdc,
+ 0x01,0xe6,0x91,0x08,0x10,0x04,0x01,0xe6,0x0b,0x00,0x0c,0x00,0xd2,0x2c,0xd1,0x16,
+ 0x10,0x0b,0x01,0xff,0xe0,0xa4,0x95,0xe0,0xa4,0xbc,0x00,0x01,0xff,0xe0,0xa4,0x96,
+ 0xe0,0xa4,0xbc,0x00,0x10,0x0b,0x01,0xff,0xe0,0xa4,0x97,0xe0,0xa4,0xbc,0x00,0x01,
+ 0xff,0xe0,0xa4,0x9c,0xe0,0xa4,0xbc,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xe0,0xa4,
+ 0xa1,0xe0,0xa4,0xbc,0x00,0x01,0xff,0xe0,0xa4,0xa2,0xe0,0xa4,0xbc,0x00,0x10,0x0b,
+ 0x01,0xff,0xe0,0xa4,0xab,0xe0,0xa4,0xbc,0x00,0x01,0xff,0xe0,0xa4,0xaf,0xe0,0xa4,
+ 0xbc,0x00,0x54,0x04,0x01,0x00,0xd3,0x14,0x92,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,
+ 0x0a,0x00,0x10,0x04,0x0a,0x00,0x0c,0x00,0x0c,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,
+ 0x10,0x00,0x0b,0x00,0x10,0x04,0x0b,0x00,0x09,0x00,0x91,0x08,0x10,0x04,0x09,0x00,
+ 0x08,0x00,0x09,0x00,0xd0,0x86,0xcf,0x86,0xd5,0x44,0xd4,0x2c,0xd3,0x18,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x10,0x00,0x01,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,
+ 0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,
+ 0x10,0x04,0x00,0x00,0x01,0x00,0x93,0x14,0x92,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,
+ 0x01,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
+ 0xd3,0x18,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x00,0x00,
+ 0x91,0x08,0x10,0x04,0x01,0x07,0x07,0x00,0x01,0x00,0xcf,0x86,0xd5,0x7b,0xd4,0x42,
+ 0xd3,0x14,0x52,0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,
+ 0x00,0x00,0x01,0x00,0xd2,0x17,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,
+ 0x00,0x00,0x01,0xff,0xe0,0xa7,0x87,0xe0,0xa6,0xbe,0x00,0xd1,0x0f,0x10,0x0b,0x01,
+ 0xff,0xe0,0xa7,0x87,0xe0,0xa7,0x97,0x00,0x01,0x09,0x10,0x04,0x08,0x00,0x00,0x00,
+ 0xd3,0x10,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,
+ 0x52,0x04,0x00,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xe0,0xa6,0xa1,0xe0,0xa6,0xbc,
+ 0x00,0x01,0xff,0xe0,0xa6,0xa2,0xe0,0xa6,0xbc,0x00,0x10,0x04,0x00,0x00,0x01,0xff,
+ 0xe0,0xa6,0xaf,0xe0,0xa6,0xbc,0x00,0xd4,0x10,0x93,0x0c,0x52,0x04,0x01,0x00,0x11,
+ 0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,
+ 0x00,0x10,0x04,0x01,0x00,0x0b,0x00,0x51,0x04,0x13,0x00,0x10,0x04,0x14,0xe6,0x00,
+ 0x00,0xe2,0x48,0x02,0xe1,0x4f,0x01,0xd0,0xa4,0xcf,0x86,0xd5,0x4c,0xd4,0x34,0xd3,
+ 0x1c,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,0x00,0x07,0x00,0x10,0x04,0x01,0x00,0x07,
+ 0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,
+ 0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x01,
+ 0x00,0x93,0x14,0x92,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x00,
+ 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,0x01,0x00,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,0x2e,0xd2,0x17,0xd1,
+ 0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xe0,0xa8,0xb2,
+ 0xe0,0xa8,0xbc,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,
+ 0xe0,0xa8,0xb8,0xe0,0xa8,0xbc,0x00,0x00,0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x00,
+ 0x00,0x91,0x08,0x10,0x04,0x01,0x07,0x00,0x00,0x01,0x00,0xcf,0x86,0xd5,0x80,0xd4,
+ 0x34,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x51,
+ 0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,
+ 0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x01,
+ 0x09,0x00,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0a,0x00,0x00,
+ 0x00,0x00,0x00,0xd2,0x25,0xd1,0x0f,0x10,0x04,0x00,0x00,0x01,0xff,0xe0,0xa8,0x96,
+ 0xe0,0xa8,0xbc,0x00,0x10,0x0b,0x01,0xff,0xe0,0xa8,0x97,0xe0,0xa8,0xbc,0x00,0x01,
+ 0xff,0xe0,0xa8,0x9c,0xe0,0xa8,0xbc,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,
+ 0x10,0x0b,0x01,0xff,0xe0,0xa8,0xab,0xe0,0xa8,0xbc,0x00,0x00,0x00,0xd4,0x10,0x93,
+ 0x0c,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x93,0x14,0x52,
+ 0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x0a,0x00,0x10,0x04,0x14,0x00,0x00,
+ 0x00,0x00,0x00,0xd0,0x82,0xcf,0x86,0xd5,0x40,0xd4,0x2c,0xd3,0x18,0xd2,0x0c,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x01,
+ 0x00,0x01,0x00,0x52,0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x07,0x00,0x01,0x00,0x10,
+ 0x04,0x00,0x00,0x01,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x00,
+ 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,0x01,0x00,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,0x18,0xd2,0x0c,0x91,
+ 0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x01,
+ 0x00,0x01,0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x01,
+ 0x07,0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0x3c,0xd4,0x28,0xd3,0x10,0x52,0x04,0x01,
+ 0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,
+ 0x00,0x10,0x04,0x00,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x01,0x09,0x00,
+ 0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0xd4,0x18,0x93,0x14,0xd2,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x07,
+ 0x00,0x07,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,0x10,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x0d,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x11,0x00,0x13,0x00,0x13,0x00,0xe1,0x24,0x01,0xd0,0x86,0xcf,0x86,
+ 0xd5,0x44,0xd4,0x2c,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,
+ 0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,
+ 0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0x93,0x14,
+ 0x92,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,
+ 0x01,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,0x01,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,
+ 0x01,0x00,0x00,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x07,0x00,0x01,0x00,
+ 0xd2,0x08,0x11,0x04,0x01,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x01,0x07,0x01,0x00,
+ 0x01,0x00,0xcf,0x86,0xd5,0x73,0xd4,0x45,0xd3,0x14,0x52,0x04,0x01,0x00,0xd1,0x08,
+ 0x10,0x04,0x0a,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0xd2,0x1e,0xd1,0x0f,
+ 0x10,0x0b,0x01,0xff,0xe0,0xad,0x87,0xe0,0xad,0x96,0x00,0x00,0x00,0x10,0x04,0x00,
+ 0x00,0x01,0xff,0xe0,0xad,0x87,0xe0,0xac,0xbe,0x00,0x91,0x0f,0x10,0x0b,0x01,0xff,
+ 0xe0,0xad,0x87,0xe0,0xad,0x97,0x00,0x01,0x09,0x00,0x00,0xd3,0x0c,0x52,0x04,0x00,
+ 0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x52,0x04,0x00,0x00,0xd1,0x16,0x10,0x0b,0x01,
+ 0xff,0xe0,0xac,0xa1,0xe0,0xac,0xbc,0x00,0x01,0xff,0xe0,0xac,0xa2,0xe0,0xac,0xbc,
+ 0x00,0x10,0x04,0x00,0x00,0x01,0x00,0xd4,0x14,0x93,0x10,0xd2,0x08,0x11,0x04,0x01,
+ 0x00,0x0a,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x93,0x10,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x01,0x00,0x07,0x00,0x0c,0x00,0x0c,0x00,0x00,0x00,0xd0,0xb1,0xcf,
+ 0x86,0xd5,0x63,0xd4,0x28,0xd3,0x14,0xd2,0x08,0x11,0x04,0x00,0x00,0x01,0x00,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0xd3,0x1f,0xd2,0x0c,0x91,
+ 0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x91,0x0f,0x10,0x0b,0x01,0xff,0xe0,
+ 0xae,0x92,0xe0,0xaf,0x97,0x00,0x01,0x00,0x00,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,
+ 0x00,0x00,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x01,0x00,0xd4,0x2c,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,
+ 0x00,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0xd2,0x0c,
+ 0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x01,0x00,
+ 0xd3,0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x08,0x00,0x01,0x00,
+ 0xd2,0x08,0x11,0x04,0x01,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0xcf,0x86,
+ 0xd5,0x61,0xd4,0x45,0xd3,0x14,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0xd2,0x1e,0xd1,0x08,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x10,0x0b,0x01,0xff,0xe0,0xaf,0x86,0xe0,0xae,0xbe,0x00,0x01,0xff,0xe0,
+ 0xaf,0x87,0xe0,0xae,0xbe,0x00,0x91,0x0f,0x10,0x0b,0x01,0xff,0xe0,0xaf,0x86,0xe0,
+ 0xaf,0x97,0x00,0x01,0x09,0x00,0x00,0x93,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x0a,
+ 0x00,0x00,0x00,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0x00,
+ 0x00,0xd4,0x14,0x93,0x10,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x08,
+ 0x00,0x01,0x00,0x01,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x07,0x00,0x07,0x00,0x92,0x0c,0x51,0x04,0x07,0x00,0x10,0x04,0x07,0x00,0x00,
+ 0x00,0x00,0x00,0xe3,0x1c,0x04,0xe2,0x1a,0x02,0xd1,0xf3,0xd0,0x76,0xcf,0x86,0xd5,
+ 0x3c,0xd4,0x28,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x10,0x00,0x01,0x00,0x01,
+ 0x00,0x91,0x08,0x10,0x04,0x14,0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0x91,
+ 0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,0x01,
+ 0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,
+ 0x10,0x52,0x04,0x01,0x00,0x91,0x08,0x10,0x04,0x10,0x00,0x01,0x00,0x01,0x00,0xd2,
+ 0x08,0x11,0x04,0x01,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x0a,0x00,0x01,
+ 0x00,0xcf,0x86,0xd5,0x53,0xd4,0x2f,0xd3,0x10,0x52,0x04,0x01,0x00,0x91,0x08,0x10,
+ 0x04,0x01,0x00,0x00,0x00,0x01,0x00,0xd2,0x13,0x91,0x0f,0x10,0x0b,0x01,0xff,0xe0,
+ 0xb1,0x86,0xe0,0xb1,0x96,0x00,0x00,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,
+ 0x01,0x09,0x00,0x00,0xd3,0x14,0x52,0x04,0x00,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,
+ 0x01,0x54,0x10,0x04,0x01,0x5b,0x00,0x00,0x92,0x0c,0x51,0x04,0x0a,0x00,0x10,0x04,
+ 0x11,0x00,0x00,0x00,0x00,0x00,0xd4,0x14,0x93,0x10,0xd2,0x08,0x11,0x04,0x01,0x00,
+ 0x0a,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x93,0x10,0x52,0x04,0x00,0x00,
+ 0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x15,0x00,0x0a,0x00,0xd0,0x76,0xcf,0x86,
+ 0xd5,0x3c,0xd4,0x28,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x12,0x00,0x10,0x00,
+ 0x01,0x00,0x91,0x08,0x10,0x04,0x14,0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,
+ 0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,
+ 0x01,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
+ 0xd3,0x10,0x52,0x04,0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,
+ 0xd2,0x08,0x11,0x04,0x01,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x07,0x07,0x07,0x00,
+ 0x01,0x00,0xcf,0x86,0xd5,0x82,0xd4,0x5e,0xd3,0x2a,0xd2,0x13,0x91,0x0f,0x10,0x0b,
+ 0x01,0xff,0xe0,0xb2,0xbf,0xe0,0xb3,0x95,0x00,0x01,0x00,0x01,0x00,0xd1,0x08,0x10,
+ 0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xe0,0xb3,0x86,0xe0,0xb3,
+ 0x95,0x00,0xd2,0x28,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe0,0xb3,0x86,0xe0,0xb3,0x96,
+ 0x00,0x00,0x00,0x10,0x0b,0x01,0xff,0xe0,0xb3,0x86,0xe0,0xb3,0x82,0x00,0x01,0xff,
+ 0xe0,0xb3,0x86,0xe0,0xb3,0x82,0xe0,0xb3,0x95,0x00,0x91,0x08,0x10,0x04,0x01,0x00,
+ 0x01,0x09,0x00,0x00,0xd3,0x14,0x52,0x04,0x00,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,
+ 0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,
+ 0x10,0x04,0x01,0x00,0x00,0x00,0xd4,0x14,0x93,0x10,0xd2,0x08,0x11,0x04,0x01,0x00,
+ 0x09,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x93,0x14,0x92,0x10,0xd1,0x08,
+ 0x10,0x04,0x00,0x00,0x09,0x00,0x10,0x04,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xe1,0x06,0x01,0xd0,0x6e,0xcf,0x86,0xd5,0x3c,0xd4,0x28,0xd3,0x18,0xd2,0x0c,0x91,
+ 0x08,0x10,0x04,0x13,0x00,0x10,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x01,
+ 0x00,0x01,0x00,0x52,0x04,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,
+ 0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,
+ 0x00,0x01,0x00,0xd4,0x14,0x53,0x04,0x01,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,
+ 0x00,0x0c,0x00,0x01,0x00,0x01,0x00,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,
+ 0x00,0x10,0x04,0x0c,0x00,0x13,0x09,0x91,0x08,0x10,0x04,0x13,0x09,0x0a,0x00,0x01,
+ 0x00,0xcf,0x86,0xd5,0x65,0xd4,0x45,0xd3,0x10,0x52,0x04,0x01,0x00,0x91,0x08,0x10,
+ 0x04,0x0a,0x00,0x00,0x00,0x01,0x00,0xd2,0x1e,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,
+ 0x00,0x10,0x0b,0x01,0xff,0xe0,0xb5,0x86,0xe0,0xb4,0xbe,0x00,0x01,0xff,0xe0,0xb5,
+ 0x87,0xe0,0xb4,0xbe,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe0,0xb5,0x86,0xe0,0xb5,
+ 0x97,0x00,0x01,0x09,0x10,0x04,0x0c,0x00,0x12,0x00,0xd3,0x10,0x52,0x04,0x00,0x00,
+ 0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x01,0x00,0x52,0x04,0x12,0x00,0x51,0x04,
+ 0x12,0x00,0x10,0x04,0x12,0x00,0x11,0x00,0xd4,0x14,0x93,0x10,0xd2,0x08,0x11,0x04,
+ 0x01,0x00,0x0a,0x00,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,0x0c,0x52,0x04,
+ 0x0a,0x00,0x11,0x04,0x0a,0x00,0x12,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x12,0x00,
+ 0x0a,0x00,0x0a,0x00,0x0a,0x00,0xd0,0x5a,0xcf,0x86,0xd5,0x34,0xd4,0x18,0x93,0x14,
+ 0xd2,0x08,0x11,0x04,0x00,0x00,0x04,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x04,0x00,
+ 0x04,0x00,0x04,0x00,0xd3,0x10,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,
+ 0x04,0x00,0x00,0x00,0x92,0x08,0x11,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x54,0x04,
+ 0x04,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x04,0x00,0x10,0x04,0x00,0x00,0x04,0x00,
+ 0x04,0x00,0x52,0x04,0x04,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x04,0x00,0x00,0x00,
+ 0xcf,0x86,0xd5,0x77,0xd4,0x28,0xd3,0x10,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,
+ 0x10,0x04,0x04,0x00,0x00,0x00,0xd2,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x04,0x09,
+ 0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x04,0x00,0xd3,0x14,0x52,0x04,
+ 0x04,0x00,0xd1,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x10,0x04,0x04,0x00,0x00,0x00,
+ 0xd2,0x13,0x51,0x04,0x04,0x00,0x10,0x0b,0x04,0xff,0xe0,0xb7,0x99,0xe0,0xb7,0x8a,
+ 0x00,0x04,0x00,0xd1,0x19,0x10,0x0b,0x04,0xff,0xe0,0xb7,0x99,0xe0,0xb7,0x8f,0x00,
+ 0x04,0xff,0xe0,0xb7,0x99,0xe0,0xb7,0x8f,0xe0,0xb7,0x8a,0x00,0x10,0x0b,0x04,0xff,
+ 0xe0,0xb7,0x99,0xe0,0xb7,0x9f,0x00,0x04,0x00,0xd4,0x10,0x93,0x0c,0x52,0x04,0x00,
+ 0x00,0x11,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0x93,0x14,0xd2,0x08,0x11,0x04,0x00,
+ 0x00,0x04,0x00,0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe2,
+ 0x31,0x01,0xd1,0x58,0xd0,0x3a,0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,0x67,0x10,0x04,
+ 0x01,0x09,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x01,0x00,0xcf,0x86,
+ 0x95,0x18,0xd4,0x0c,0x53,0x04,0x01,0x00,0x12,0x04,0x01,0x6b,0x01,0x00,0x53,0x04,
+ 0x01,0x00,0x12,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0xd0,0x9e,0xcf,0x86,0xd5,0x54,
+ 0xd4,0x3c,0xd3,0x20,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x10,0x04,
+ 0x01,0x00,0x00,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x15,0x00,
+ 0x01,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x15,0x00,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x91,0x08,0x10,0x04,0x15,0x00,0x01,0x00,0x15,0x00,0xd3,0x08,0x12,0x04,
+ 0x15,0x00,0x01,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x15,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0xd4,0x30,0xd3,0x1c,0xd2,0x0c,0x91,0x08,0x10,0x04,0x15,0x00,0x01,0x00,
+ 0x01,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x10,0x04,0x00,0x00,0x01,0x00,
+ 0xd2,0x08,0x11,0x04,0x15,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x15,0x00,0x01,0x00,
+ 0x01,0x00,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,0x76,0x10,0x04,0x15,0x09,
+ 0x01,0x00,0x11,0x04,0x01,0x00,0x00,0x00,0xcf,0x86,0x95,0x34,0xd4,0x20,0xd3,0x14,
+ 0x52,0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x52,0x04,0x01,0x7a,0x11,0x04,0x01,0x00,0x00,0x00,0x53,0x04,0x01,0x00,
+ 0xd2,0x08,0x11,0x04,0x01,0x00,0x00,0x00,0x11,0x04,0x01,0x00,0x0d,0x00,0x00,0x00,
+ 0xe1,0x2b,0x01,0xd0,0x3e,0xcf,0x86,0xd5,0x14,0x54,0x04,0x02,0x00,0x53,0x04,0x02,
+ 0x00,0x92,0x08,0x11,0x04,0x02,0xdc,0x02,0x00,0x02,0x00,0x54,0x04,0x02,0x00,0xd3,
+ 0x14,0x52,0x04,0x02,0x00,0xd1,0x08,0x10,0x04,0x02,0x00,0x02,0xdc,0x10,0x04,0x02,
+ 0x00,0x02,0xdc,0x92,0x0c,0x91,0x08,0x10,0x04,0x02,0x00,0x02,0xd8,0x02,0x00,0x02,
+ 0x00,0xcf,0x86,0xd5,0x73,0xd4,0x36,0xd3,0x17,0x92,0x13,0x51,0x04,0x02,0x00,0x10,
+ 0x04,0x02,0x00,0x02,0xff,0xe0,0xbd,0x82,0xe0,0xbe,0xb7,0x00,0x02,0x00,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x00,0x00,0x02,0x00,0x02,0x00,0x91,0x0f,0x10,0x04,0x02,0x00,
+ 0x02,0xff,0xe0,0xbd,0x8c,0xe0,0xbe,0xb7,0x00,0x02,0x00,0xd3,0x26,0xd2,0x13,0x51,
+ 0x04,0x02,0x00,0x10,0x0b,0x02,0xff,0xe0,0xbd,0x91,0xe0,0xbe,0xb7,0x00,0x02,0x00,
+ 0x51,0x04,0x02,0x00,0x10,0x04,0x02,0x00,0x02,0xff,0xe0,0xbd,0x96,0xe0,0xbe,0xb7,
+ 0x00,0x52,0x04,0x02,0x00,0x91,0x0f,0x10,0x0b,0x02,0xff,0xe0,0xbd,0x9b,0xe0,0xbe,
+ 0xb7,0x00,0x02,0x00,0x02,0x00,0xd4,0x27,0x53,0x04,0x02,0x00,0xd2,0x17,0xd1,0x0f,
+ 0x10,0x04,0x02,0x00,0x02,0xff,0xe0,0xbd,0x80,0xe0,0xbe,0xb5,0x00,0x10,0x04,0x04,
+ 0x00,0x0a,0x00,0x91,0x08,0x10,0x04,0x0a,0x00,0x00,0x00,0x00,0x00,0xd3,0x35,0xd2,
+ 0x17,0xd1,0x08,0x10,0x04,0x00,0x00,0x02,0x81,0x10,0x04,0x02,0x82,0x02,0xff,0xe0,
+ 0xbd,0xb1,0xe0,0xbd,0xb2,0x00,0xd1,0x0f,0x10,0x04,0x02,0x84,0x02,0xff,0xe0,0xbd,
+ 0xb1,0xe0,0xbd,0xb4,0x00,0x10,0x0b,0x02,0xff,0xe0,0xbe,0xb2,0xe0,0xbe,0x80,0x00,
+ 0x02,0x00,0xd2,0x13,0x91,0x0f,0x10,0x0b,0x02,0xff,0xe0,0xbe,0xb3,0xe0,0xbe,0x80,
+ 0x00,0x02,0x00,0x02,0x82,0x11,0x04,0x02,0x82,0x02,0x00,0xd0,0xd3,0xcf,0x86,0xd5,
+ 0x65,0xd4,0x27,0xd3,0x1f,0xd2,0x13,0x91,0x0f,0x10,0x04,0x02,0x82,0x02,0xff,0xe0,
+ 0xbd,0xb1,0xe0,0xbe,0x80,0x00,0x02,0xe6,0x91,0x08,0x10,0x04,0x02,0x09,0x02,0x00,
+ 0x02,0xe6,0x12,0x04,0x02,0x00,0x0c,0x00,0xd3,0x1f,0xd2,0x13,0x51,0x04,0x02,0x00,
+ 0x10,0x04,0x02,0x00,0x02,0xff,0xe0,0xbe,0x92,0xe0,0xbe,0xb7,0x00,0x51,0x04,0x02,
+ 0x00,0x10,0x04,0x04,0x00,0x02,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x02,
+ 0x00,0x02,0x00,0x91,0x0f,0x10,0x04,0x02,0x00,0x02,0xff,0xe0,0xbe,0x9c,0xe0,0xbe,
+ 0xb7,0x00,0x02,0x00,0xd4,0x3d,0xd3,0x26,0xd2,0x13,0x51,0x04,0x02,0x00,0x10,0x0b,
+ 0x02,0xff,0xe0,0xbe,0xa1,0xe0,0xbe,0xb7,0x00,0x02,0x00,0x51,0x04,0x02,0x00,0x10,
+ 0x04,0x02,0x00,0x02,0xff,0xe0,0xbe,0xa6,0xe0,0xbe,0xb7,0x00,0x52,0x04,0x02,0x00,
+ 0x91,0x0f,0x10,0x0b,0x02,0xff,0xe0,0xbe,0xab,0xe0,0xbe,0xb7,0x00,0x02,0x00,0x04,
+ 0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x04,0x00,0x02,0x00,0x02,0x00,0x02,
+ 0x00,0xd2,0x13,0x91,0x0f,0x10,0x04,0x04,0x00,0x02,0xff,0xe0,0xbe,0x90,0xe0,0xbe,
+ 0xb5,0x00,0x04,0x00,0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x04,0x00,0xcf,0x86,
+ 0x95,0x4c,0xd4,0x24,0xd3,0x10,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,
+ 0x04,0xdc,0x04,0x00,0x52,0x04,0x04,0x00,0xd1,0x08,0x10,0x04,0x04,0x00,0x00,0x00,
+ 0x10,0x04,0x0a,0x00,0x04,0x00,0xd3,0x14,0xd2,0x08,0x11,0x04,0x08,0x00,0x0a,0x00,
+ 0x91,0x08,0x10,0x04,0x0a,0x00,0x0b,0x00,0x0b,0x00,0x92,0x10,0xd1,0x08,0x10,0x04,
+ 0x0b,0x00,0x0c,0x00,0x10,0x04,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,
+ 0xe5,0xf7,0x04,0xe4,0x79,0x03,0xe3,0x7b,0x01,0xe2,0x04,0x01,0xd1,0x7f,0xd0,0x65,
+ 0xcf,0x86,0x55,0x04,0x04,0x00,0xd4,0x33,0xd3,0x1f,0xd2,0x0c,0x51,0x04,0x04,0x00,
+ 0x10,0x04,0x0a,0x00,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x0b,0x04,0xff,0xe1,0x80,
+ 0xa5,0xe1,0x80,0xae,0x00,0x04,0x00,0x92,0x10,0xd1,0x08,0x10,0x04,0x0a,0x00,0x04,
+ 0x00,0x10,0x04,0x04,0x00,0x0a,0x00,0x04,0x00,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x04,
+ 0x00,0x10,0x04,0x04,0x00,0x0a,0x00,0x51,0x04,0x0a,0x00,0x10,0x04,0x04,0x00,0x04,
+ 0x07,0x92,0x10,0xd1,0x08,0x10,0x04,0x04,0x00,0x04,0x09,0x10,0x04,0x0a,0x09,0x0a,
+ 0x00,0x0a,0x00,0xcf,0x86,0x95,0x14,0x54,0x04,0x04,0x00,0x53,0x04,0x04,0x00,0x92,
+ 0x08,0x11,0x04,0x04,0x00,0x0a,0x00,0x0a,0x00,0x0a,0x00,0xd0,0x2e,0xcf,0x86,0x95,
+ 0x28,0xd4,0x14,0x53,0x04,0x0a,0x00,0x52,0x04,0x0a,0x00,0x91,0x08,0x10,0x04,0x0a,
+ 0x00,0x0a,0xdc,0x0a,0x00,0x53,0x04,0x0a,0x00,0xd2,0x08,0x11,0x04,0x0a,0x00,0x0b,
+ 0x00,0x11,0x04,0x0b,0x00,0x0a,0x00,0x01,0x00,0xcf,0x86,0xd5,0x24,0x94,0x20,0xd3,
+ 0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x00,0x00,0x0d,0x00,0x52,
+ 0x04,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x54,
+ 0x04,0x01,0x00,0xd3,0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x06,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x06,0x00,0x08,0x00,0x10,0x04,0x08,
+ 0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x08,0x00,0x0d,0x00,0x0d,0x00,0xd1,0x3e,0xd0,
+ 0x06,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x1d,0x54,0x04,0x01,0x00,0x53,0x04,0x01,
+ 0x00,0xd2,0x08,0x11,0x04,0x01,0x00,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,
+ 0x00,0x01,0xff,0x00,0x94,0x15,0x93,0x11,0x92,0x0d,0x91,0x09,0x10,0x05,0x01,0xff,
+ 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd0,0x1e,0xcf,0x86,0x55,
+ 0x04,0x01,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x0b,0x00,0x0b,0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,0x00,0x54,
+ 0x04,0x01,0x00,0x53,0x04,0x01,0x00,0x92,0x08,0x11,0x04,0x01,0x00,0x0b,0x00,0x0b,
+ 0x00,0xe2,0x21,0x01,0xd1,0x6c,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x94,0x14,0x93,0x10,
+ 0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,0x04,0x00,
+ 0x04,0x00,0x04,0x00,0xcf,0x86,0x95,0x48,0xd4,0x24,0xd3,0x10,0x52,0x04,0x04,0x00,
+ 0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,
+ 0x04,0x00,0x00,0x00,0x04,0x00,0x11,0x04,0x04,0x00,0x00,0x00,0xd3,0x10,0x52,0x04,
+ 0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x00,0x00,0xd2,0x0c,0x91,0x08,
+ 0x10,0x04,0x04,0x00,0x00,0x00,0x04,0x00,0x11,0x04,0x04,0x00,0x00,0x00,0x04,0x00,
+ 0xd0,0x62,0xcf,0x86,0xd5,0x28,0x94,0x24,0xd3,0x10,0x52,0x04,0x04,0x00,0x51,0x04,
+ 0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x04,0x00,
+ 0x00,0x00,0x04,0x00,0x11,0x04,0x04,0x00,0x00,0x00,0x04,0x00,0xd4,0x14,0x53,0x04,
+ 0x04,0x00,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,
+ 0xd3,0x14,0xd2,0x0c,0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x04,0x00,0x11,0x04,
+ 0x04,0x00,0x00,0x00,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,
+ 0x00,0x00,0xcf,0x86,0xd5,0x38,0xd4,0x24,0xd3,0x14,0xd2,0x0c,0x91,0x08,0x10,0x04,
+ 0x04,0x00,0x00,0x00,0x04,0x00,0x11,0x04,0x04,0x00,0x00,0x00,0x52,0x04,0x04,0x00,
+ 0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,0x93,0x10,0x52,0x04,0x04,0x00,
+ 0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x00,0x00,0x04,0x00,0x94,0x14,0x53,0x04,
+ 0x04,0x00,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,
+ 0x04,0x00,0xd1,0x9c,0xd0,0x3e,0xcf,0x86,0x95,0x38,0xd4,0x14,0x53,0x04,0x04,0x00,
+ 0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,0xd3,0x14,
+ 0xd2,0x0c,0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x04,0x00,0x11,0x04,0x04,0x00,
+ 0x00,0x00,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,
+ 0x04,0x00,0xcf,0x86,0xd5,0x34,0xd4,0x14,0x93,0x10,0x52,0x04,0x04,0x00,0x51,0x04,
+ 0x04,0x00,0x10,0x04,0x04,0x00,0x08,0x00,0x04,0x00,0x53,0x04,0x04,0x00,0xd2,0x0c,
+ 0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x00,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,
+ 0x0c,0xe6,0x10,0x04,0x0c,0xe6,0x08,0xe6,0xd4,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x08,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x53,0x04,0x04,0x00,
+ 0x52,0x04,0x04,0x00,0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0xd0,0x1a,
+ 0xcf,0x86,0x95,0x14,0x54,0x04,0x08,0x00,0x53,0x04,0x08,0x00,0x92,0x08,0x11,0x04,
+ 0x08,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xcf,0x86,0x55,0x04,0x04,0x00,0x54,0x04,
+ 0x04,0x00,0xd3,0x10,0x52,0x04,0x04,0x00,0x91,0x08,0x10,0x04,0x04,0x00,0x11,0x00,
+ 0x00,0x00,0x52,0x04,0x11,0x00,0x11,0x04,0x11,0x00,0x00,0x00,0xd3,0x30,0xd2,0x2a,
+ 0xd1,0x24,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x0b,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,
+ 0xcf,0x06,0x04,0x00,0xcf,0x06,0x04,0x00,0xcf,0x06,0x04,0x00,0xd2,0x6c,0xd1,0x24,
+ 0xd0,0x06,0xcf,0x06,0x04,0x00,0xcf,0x86,0x55,0x04,0x04,0x00,0x54,0x04,0x04,0x00,
+ 0x93,0x10,0x52,0x04,0x04,0x00,0x51,0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x0b,0x00,
+ 0x0b,0x00,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,0x04,0x00,0x53,0x04,0x04,0x00,
+ 0x52,0x04,0x04,0x00,0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x00,
+ 0xcf,0x86,0x55,0x04,0x04,0x00,0x54,0x04,0x04,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x04,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x80,0xd0,0x46,0xcf,0x86,0xd5,0x28,
+ 0xd4,0x14,0x53,0x04,0x06,0x00,0x52,0x04,0x06,0x00,0x91,0x08,0x10,0x04,0x06,0x00,
+ 0x00,0x00,0x06,0x00,0x93,0x10,0x52,0x04,0x06,0x00,0x91,0x08,0x10,0x04,0x06,0x09,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x04,0x06,0x00,0x93,0x14,0x52,0x04,0x06,0x00,
+ 0xd1,0x08,0x10,0x04,0x06,0x09,0x06,0x00,0x10,0x04,0x06,0x00,0x00,0x00,0x00,0x00,
+ 0xcf,0x86,0xd5,0x10,0x54,0x04,0x06,0x00,0x93,0x08,0x12,0x04,0x06,0x00,0x00,0x00,
+ 0x00,0x00,0xd4,0x14,0x53,0x04,0x06,0x00,0x52,0x04,0x06,0x00,0x91,0x08,0x10,0x04,
+ 0x06,0x00,0x00,0x00,0x06,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x06,0x00,
+ 0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0xd0,0x1b,0xcf,0x86,0x55,0x04,0x04,0x00,
+ 0x54,0x04,0x04,0x00,0x93,0x0d,0x52,0x04,0x04,0x00,0x11,0x05,0x04,0xff,0x00,0x04,
+ 0x00,0x04,0x00,0xcf,0x86,0xd5,0x24,0x54,0x04,0x04,0x00,0xd3,0x10,0x92,0x0c,0x51,
+ 0x04,0x04,0x00,0x10,0x04,0x04,0x09,0x04,0x00,0x04,0x00,0x52,0x04,0x04,0x00,0x91,
+ 0x08,0x10,0x04,0x04,0x00,0x07,0xe6,0x00,0x00,0xd4,0x10,0x53,0x04,0x04,0x00,0x92,
+ 0x08,0x11,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x07,0x00,0x92,0x08,0x11,
+ 0x04,0x07,0x00,0x00,0x00,0x00,0x00,0xe4,0xb7,0x03,0xe3,0x58,0x01,0xd2,0x8f,0xd1,
+ 0x53,0xd0,0x35,0xcf,0x86,0x95,0x2f,0xd4,0x1f,0x53,0x04,0x04,0x00,0xd2,0x0d,0x51,
+ 0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x04,0xff,0x00,0x51,0x05,0x04,0xff,0x00,0x10,
+ 0x05,0x04,0xff,0x00,0x00,0x00,0x53,0x04,0x04,0x00,0x92,0x08,0x11,0x04,0x04,0x00,
+ 0x00,0x00,0x00,0x00,0x04,0x00,0xcf,0x86,0x55,0x04,0x04,0x00,0x54,0x04,0x04,0x00,
+ 0x53,0x04,0x04,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xd0,0x22,0xcf,0x86,0x55,0x04,0x04,0x00,0x94,0x18,0x53,0x04,0x04,0x00,
+ 0x92,0x10,0xd1,0x08,0x10,0x04,0x04,0x00,0x04,0xe4,0x10,0x04,0x0a,0x00,0x00,0x00,
+ 0x00,0x00,0x0b,0x00,0xcf,0x86,0x55,0x04,0x0b,0x00,0x54,0x04,0x0b,0x00,0x93,0x0c,
+ 0x52,0x04,0x0b,0x00,0x11,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0xd1,0x80,0xd0,0x42,
+ 0xcf,0x86,0xd5,0x1c,0x54,0x04,0x07,0x00,0x53,0x04,0x07,0x00,0x52,0x04,0x07,0x00,
+ 0xd1,0x08,0x10,0x04,0x07,0x00,0x10,0x00,0x10,0x04,0x10,0x00,0x00,0x00,0xd4,0x0c,
+ 0x53,0x04,0x07,0x00,0x12,0x04,0x07,0x00,0x00,0x00,0x53,0x04,0x07,0x00,0x92,0x10,
+ 0xd1,0x08,0x10,0x04,0x07,0x00,0x07,0xde,0x10,0x04,0x07,0xe6,0x07,0xdc,0x00,0x00,
+ 0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x07,0x00,
+ 0x00,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0xd4,0x10,0x53,0x04,0x07,0x00,
+ 0x52,0x04,0x07,0x00,0x11,0x04,0x07,0x00,0x00,0x00,0x93,0x10,0x52,0x04,0x07,0x00,
+ 0x91,0x08,0x10,0x04,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd0,0x1a,0xcf,0x86,
+ 0x55,0x04,0x08,0x00,0x94,0x10,0x53,0x04,0x08,0x00,0x92,0x08,0x11,0x04,0x08,0x00,
+ 0x0b,0x00,0x00,0x00,0x08,0x00,0xcf,0x86,0x95,0x28,0xd4,0x10,0x53,0x04,0x08,0x00,
+ 0x92,0x08,0x11,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x08,0x00,0xd2,0x0c,
+ 0x51,0x04,0x08,0x00,0x10,0x04,0x0b,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x08,0x00,
+ 0x07,0x00,0xd2,0xe4,0xd1,0x80,0xd0,0x2e,0xcf,0x86,0x95,0x28,0x54,0x04,0x08,0x00,
+ 0xd3,0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x08,0xe6,
+ 0xd2,0x0c,0x91,0x08,0x10,0x04,0x08,0xdc,0x08,0x00,0x08,0x00,0x11,0x04,0x00,0x00,
+ 0x08,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x18,0x54,0x04,0x0b,0x00,0x53,0x04,0x0b,0x00,
+ 0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,0x00,0x00,0xd4,0x14,
+ 0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0b,0x09,0x0b,0x00,0x0b,0x00,0x0b,0x00,
+ 0x0b,0x00,0xd3,0x10,0x52,0x04,0x0b,0x00,0x91,0x08,0x10,0x04,0x0b,0x00,0x0b,0xe6,
+ 0x0b,0xe6,0x52,0x04,0x0b,0xe6,0xd1,0x08,0x10,0x04,0x0b,0xe6,0x00,0x00,0x10,0x04,
+ 0x00,0x00,0x0b,0xdc,0xd0,0x5e,0xcf,0x86,0xd5,0x20,0xd4,0x10,0x53,0x04,0x0b,0x00,
+ 0x92,0x08,0x11,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x0b,0x00,0x92,0x08,
+ 0x11,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0xd4,0x10,0x53,0x04,0x0b,0x00,0x52,0x04,
+ 0x0b,0x00,0x11,0x04,0x0b,0x00,0x00,0x00,0xd3,0x10,0x52,0x04,0x10,0xe6,0x91,0x08,
+ 0x10,0x04,0x10,0xe6,0x10,0xdc,0x10,0xdc,0xd2,0x0c,0x51,0x04,0x10,0xdc,0x10,0x04,
+ 0x10,0xdc,0x10,0xe6,0xd1,0x08,0x10,0x04,0x10,0xe6,0x10,0xdc,0x10,0x04,0x10,0x00,
+ 0x00,0x00,0xcf,0x06,0x00,0x00,0xe1,0x1e,0x01,0xd0,0xaa,0xcf,0x86,0xd5,0x6e,0xd4,
+ 0x53,0xd3,0x17,0x52,0x04,0x09,0x00,0x51,0x04,0x09,0x00,0x10,0x0b,0x09,0xff,0xe1,
+ 0xac,0x85,0xe1,0xac,0xb5,0x00,0x09,0x00,0xd2,0x1e,0xd1,0x0f,0x10,0x0b,0x09,0xff,
+ 0xe1,0xac,0x87,0xe1,0xac,0xb5,0x00,0x09,0x00,0x10,0x0b,0x09,0xff,0xe1,0xac,0x89,
+ 0xe1,0xac,0xb5,0x00,0x09,0x00,0xd1,0x0f,0x10,0x0b,0x09,0xff,0xe1,0xac,0x8b,0xe1,
+ 0xac,0xb5,0x00,0x09,0x00,0x10,0x0b,0x09,0xff,0xe1,0xac,0x8d,0xe1,0xac,0xb5,0x00,
+ 0x09,0x00,0x93,0x17,0x92,0x13,0x51,0x04,0x09,0x00,0x10,0x0b,0x09,0xff,0xe1,0xac,
+ 0x91,0xe1,0xac,0xb5,0x00,0x09,0x00,0x09,0x00,0x09,0x00,0x54,0x04,0x09,0x00,0xd3,
+ 0x10,0x52,0x04,0x09,0x00,0x91,0x08,0x10,0x04,0x09,0x07,0x09,0x00,0x09,0x00,0xd2,
+ 0x13,0x51,0x04,0x09,0x00,0x10,0x04,0x09,0x00,0x09,0xff,0xe1,0xac,0xba,0xe1,0xac,
+ 0xb5,0x00,0x91,0x0f,0x10,0x04,0x09,0x00,0x09,0xff,0xe1,0xac,0xbc,0xe1,0xac,0xb5,
+ 0x00,0x09,0x00,0xcf,0x86,0xd5,0x3d,0x94,0x39,0xd3,0x31,0xd2,0x25,0xd1,0x16,0x10,
+ 0x0b,0x09,0xff,0xe1,0xac,0xbe,0xe1,0xac,0xb5,0x00,0x09,0xff,0xe1,0xac,0xbf,0xe1,
+ 0xac,0xb5,0x00,0x10,0x04,0x09,0x00,0x09,0xff,0xe1,0xad,0x82,0xe1,0xac,0xb5,0x00,
+ 0x91,0x08,0x10,0x04,0x09,0x09,0x09,0x00,0x09,0x00,0x12,0x04,0x09,0x00,0x00,0x00,
+ 0x09,0x00,0xd4,0x1c,0x53,0x04,0x09,0x00,0xd2,0x0c,0x51,0x04,0x09,0x00,0x10,0x04,
+ 0x09,0x00,0x09,0xe6,0x91,0x08,0x10,0x04,0x09,0xdc,0x09,0xe6,0x09,0xe6,0xd3,0x08,
+ 0x12,0x04,0x09,0xe6,0x09,0x00,0x52,0x04,0x09,0x00,0x91,0x08,0x10,0x04,0x09,0x00,
+ 0x00,0x00,0x00,0x00,0xd0,0x2e,0xcf,0x86,0x55,0x04,0x0a,0x00,0xd4,0x18,0x53,0x04,
+ 0x0a,0x00,0xd2,0x0c,0x51,0x04,0x0a,0x00,0x10,0x04,0x0a,0x09,0x0d,0x09,0x11,0x04,
+ 0x0d,0x00,0x0a,0x00,0x53,0x04,0x0a,0x00,0x92,0x08,0x11,0x04,0x0a,0x00,0x0d,0x00,
+ 0x0d,0x00,0xcf,0x86,0x55,0x04,0x0c,0x00,0xd4,0x14,0x93,0x10,0x52,0x04,0x0c,0x00,
+ 0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x07,0x0c,0x00,0x0c,0x00,0xd3,0x0c,0x92,0x08,
+ 0x11,0x04,0x0c,0x00,0x0c,0x09,0x00,0x00,0x12,0x04,0x00,0x00,0x0c,0x00,0xe3,0xb2,
+ 0x01,0xe2,0x09,0x01,0xd1,0x4c,0xd0,0x2a,0xcf,0x86,0x55,0x04,0x0a,0x00,0x54,0x04,
+ 0x0a,0x00,0xd3,0x10,0x52,0x04,0x0a,0x00,0x51,0x04,0x0a,0x00,0x10,0x04,0x0a,0x00,
+ 0x0a,0x07,0x92,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x0a,0x00,0x0a,0x00,
+ 0xcf,0x86,0x95,0x1c,0x94,0x18,0x53,0x04,0x0a,0x00,0xd2,0x08,0x11,0x04,0x0a,0x00,
+ 0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x0a,0x00,0x0a,0x00,0x0a,0x00,0x0a,0x00,
+ 0xd0,0x3a,0xcf,0x86,0xd5,0x18,0x94,0x14,0x53,0x04,0x12,0x00,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x54,0x04,0x14,0x00,
+ 0x53,0x04,0x14,0x00,0xd2,0x0c,0x51,0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x00,0x00,
+ 0x91,0x08,0x10,0x04,0x00,0x00,0x14,0x00,0x14,0x00,0xcf,0x86,0xd5,0x2c,0xd4,0x08,
+ 0x13,0x04,0x0d,0x00,0x00,0x00,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x0b,0xe6,0x10,0x04,
+ 0x0b,0xe6,0x0b,0x00,0x91,0x08,0x10,0x04,0x0b,0x01,0x0b,0xdc,0x0b,0xdc,0x92,0x08,
+ 0x11,0x04,0x0b,0xdc,0x0b,0xe6,0x0b,0xdc,0xd4,0x28,0xd3,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x0b,0xe6,0x0b,0x00,0x0b,0x01,0x0b,0x01,0xd2,0x0c,0x91,0x08,0x10,0x04,
+ 0x0b,0x01,0x0b,0x00,0x0b,0x00,0x91,0x08,0x10,0x04,0x0b,0x00,0x0b,0xdc,0x0b,0x00,
+ 0xd3,0x1c,0xd2,0x0c,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,0x0d,0x00,0xd1,0x08,
+ 0x10,0x04,0x0d,0xe6,0x0d,0x00,0x10,0x04,0x0d,0x00,0x13,0x00,0x92,0x0c,0x51,0x04,
+ 0x10,0xe6,0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0xd1,0x1c,0xd0,0x06,0xcf,0x06,
+ 0x07,0x00,0xcf,0x86,0x55,0x04,0x07,0x00,0x94,0x0c,0x53,0x04,0x07,0x00,0x12,0x04,
+ 0x07,0x00,0x08,0x00,0x08,0x00,0xd0,0x06,0xcf,0x06,0x08,0x00,0xcf,0x86,0xd5,0x40,
+ 0xd4,0x2c,0xd3,0x10,0x92,0x0c,0x51,0x04,0x08,0xe6,0x10,0x04,0x08,0xdc,0x08,0xe6,
+ 0x09,0xe6,0xd2,0x0c,0x51,0x04,0x09,0xe6,0x10,0x04,0x09,0xdc,0x0a,0xe6,0xd1,0x08,
+ 0x10,0x04,0x0a,0xe6,0x0a,0xea,0x10,0x04,0x0a,0xd6,0x0a,0xdc,0x93,0x10,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x0a,0xca,0x0a,0xe6,0x0a,0xe6,0x0a,0xe6,0x0a,0xe6,0xd4,0x14,
+ 0x93,0x10,0x52,0x04,0x0a,0xe6,0x51,0x04,0x0a,0xe6,0x10,0x04,0x0a,0xe6,0x10,0xe6,
+ 0x10,0xe6,0xd3,0x10,0x52,0x04,0x10,0xe6,0x51,0x04,0x10,0xe6,0x10,0x04,0x13,0xe8,
+ 0x13,0xe4,0xd2,0x10,0xd1,0x08,0x10,0x04,0x13,0xe4,0x13,0xdc,0x10,0x04,0x00,0x00,
+ 0x12,0xe6,0xd1,0x08,0x10,0x04,0x0c,0xe9,0x0b,0xdc,0x10,0x04,0x09,0xe6,0x09,0xdc,
+ 0xe2,0x80,0x08,0xe1,0x48,0x04,0xe0,0x1c,0x02,0xcf,0x86,0xe5,0x11,0x01,0xd4,0x84,
+ 0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x41,0xcc,0xa5,0x00,0x01,0xff,
+ 0x61,0xcc,0xa5,0x00,0x10,0x08,0x01,0xff,0x42,0xcc,0x87,0x00,0x01,0xff,0x62,0xcc,
+ 0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x42,0xcc,0xa3,0x00,0x01,0xff,0x62,0xcc,
+ 0xa3,0x00,0x10,0x08,0x01,0xff,0x42,0xcc,0xb1,0x00,0x01,0xff,0x62,0xcc,0xb1,0x00,
+ 0xd2,0x24,0xd1,0x14,0x10,0x0a,0x01,0xff,0x43,0xcc,0xa7,0xcc,0x81,0x00,0x01,0xff,
+ 0x63,0xcc,0xa7,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x44,0xcc,0x87,0x00,0x01,0xff,
+ 0x64,0xcc,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x44,0xcc,0xa3,0x00,0x01,0xff,
+ 0x64,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x44,0xcc,0xb1,0x00,0x01,0xff,0x64,0xcc,
+ 0xb1,0x00,0xd3,0x48,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x44,0xcc,0xa7,0x00,
+ 0x01,0xff,0x64,0xcc,0xa7,0x00,0x10,0x08,0x01,0xff,0x44,0xcc,0xad,0x00,0x01,0xff,
+ 0x64,0xcc,0xad,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x45,0xcc,0x84,0xcc,0x80,0x00,
+ 0x01,0xff,0x65,0xcc,0x84,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x45,0xcc,0x84,0xcc,
+ 0x81,0x00,0x01,0xff,0x65,0xcc,0x84,0xcc,0x81,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0x45,0xcc,0xad,0x00,0x01,0xff,0x65,0xcc,0xad,0x00,0x10,0x08,0x01,0xff,
+ 0x45,0xcc,0xb0,0x00,0x01,0xff,0x65,0xcc,0xb0,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,
+ 0x45,0xcc,0xa7,0xcc,0x86,0x00,0x01,0xff,0x65,0xcc,0xa7,0xcc,0x86,0x00,0x10,0x08,
+ 0x01,0xff,0x46,0xcc,0x87,0x00,0x01,0xff,0x66,0xcc,0x87,0x00,0xd4,0x84,0xd3,0x40,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x47,0xcc,0x84,0x00,0x01,0xff,0x67,0xcc,
+ 0x84,0x00,0x10,0x08,0x01,0xff,0x48,0xcc,0x87,0x00,0x01,0xff,0x68,0xcc,0x87,0x00,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x48,0xcc,0xa3,0x00,0x01,0xff,0x68,0xcc,0xa3,0x00,
+ 0x10,0x08,0x01,0xff,0x48,0xcc,0x88,0x00,0x01,0xff,0x68,0xcc,0x88,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0x48,0xcc,0xa7,0x00,0x01,0xff,0x68,0xcc,0xa7,0x00,
+ 0x10,0x08,0x01,0xff,0x48,0xcc,0xae,0x00,0x01,0xff,0x68,0xcc,0xae,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0x49,0xcc,0xb0,0x00,0x01,0xff,0x69,0xcc,0xb0,0x00,0x10,0x0a,
+ 0x01,0xff,0x49,0xcc,0x88,0xcc,0x81,0x00,0x01,0xff,0x69,0xcc,0x88,0xcc,0x81,0x00,
+ 0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x4b,0xcc,0x81,0x00,0x01,0xff,
+ 0x6b,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x4b,0xcc,0xa3,0x00,0x01,0xff,0x6b,0xcc,
+ 0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x4b,0xcc,0xb1,0x00,0x01,0xff,0x6b,0xcc,
+ 0xb1,0x00,0x10,0x08,0x01,0xff,0x4c,0xcc,0xa3,0x00,0x01,0xff,0x6c,0xcc,0xa3,0x00,
+ 0xd2,0x24,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4c,0xcc,0xa3,0xcc,0x84,0x00,0x01,0xff,
+ 0x6c,0xcc,0xa3,0xcc,0x84,0x00,0x10,0x08,0x01,0xff,0x4c,0xcc,0xb1,0x00,0x01,0xff,
+ 0x6c,0xcc,0xb1,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x4c,0xcc,0xad,0x00,0x01,0xff,
+ 0x6c,0xcc,0xad,0x00,0x10,0x08,0x01,0xff,0x4d,0xcc,0x81,0x00,0x01,0xff,0x6d,0xcc,
+ 0x81,0x00,0xcf,0x86,0xe5,0x15,0x01,0xd4,0x88,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x4d,0xcc,0x87,0x00,0x01,0xff,0x6d,0xcc,0x87,0x00,0x10,0x08,0x01,
+ 0xff,0x4d,0xcc,0xa3,0x00,0x01,0xff,0x6d,0xcc,0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x4e,0xcc,0x87,0x00,0x01,0xff,0x6e,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x4e,
+ 0xcc,0xa3,0x00,0x01,0xff,0x6e,0xcc,0xa3,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x4e,0xcc,0xb1,0x00,0x01,0xff,0x6e,0xcc,0xb1,0x00,0x10,0x08,0x01,0xff,0x4e,
+ 0xcc,0xad,0x00,0x01,0xff,0x6e,0xcc,0xad,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,
+ 0xcc,0x83,0xcc,0x81,0x00,0x01,0xff,0x6f,0xcc,0x83,0xcc,0x81,0x00,0x10,0x0a,0x01,
+ 0xff,0x4f,0xcc,0x83,0xcc,0x88,0x00,0x01,0xff,0x6f,0xcc,0x83,0xcc,0x88,0x00,0xd3,
+ 0x48,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x84,0xcc,0x80,0x00,0x01,
+ 0xff,0x6f,0xcc,0x84,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x84,0xcc,0x81,
+ 0x00,0x01,0xff,0x6f,0xcc,0x84,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x50,
+ 0xcc,0x81,0x00,0x01,0xff,0x70,0xcc,0x81,0x00,0x10,0x08,0x01,0xff,0x50,0xcc,0x87,
+ 0x00,0x01,0xff,0x70,0xcc,0x87,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x52,
+ 0xcc,0x87,0x00,0x01,0xff,0x72,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x52,0xcc,0xa3,
+ 0x00,0x01,0xff,0x72,0xcc,0xa3,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x52,0xcc,0xa3,
+ 0xcc,0x84,0x00,0x01,0xff,0x72,0xcc,0xa3,0xcc,0x84,0x00,0x10,0x08,0x01,0xff,0x52,
+ 0xcc,0xb1,0x00,0x01,0xff,0x72,0xcc,0xb1,0x00,0xd4,0x8c,0xd3,0x48,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0x53,0xcc,0x87,0x00,0x01,0xff,0x73,0xcc,0x87,0x00,0x10,
+ 0x08,0x01,0xff,0x53,0xcc,0xa3,0x00,0x01,0xff,0x73,0xcc,0xa3,0x00,0xd1,0x14,0x10,
+ 0x0a,0x01,0xff,0x53,0xcc,0x81,0xcc,0x87,0x00,0x01,0xff,0x73,0xcc,0x81,0xcc,0x87,
+ 0x00,0x10,0x0a,0x01,0xff,0x53,0xcc,0x8c,0xcc,0x87,0x00,0x01,0xff,0x73,0xcc,0x8c,
+ 0xcc,0x87,0x00,0xd2,0x24,0xd1,0x14,0x10,0x0a,0x01,0xff,0x53,0xcc,0xa3,0xcc,0x87,
+ 0x00,0x01,0xff,0x73,0xcc,0xa3,0xcc,0x87,0x00,0x10,0x08,0x01,0xff,0x54,0xcc,0x87,
+ 0x00,0x01,0xff,0x74,0xcc,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x54,0xcc,0xa3,
+ 0x00,0x01,0xff,0x74,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x54,0xcc,0xb1,0x00,0x01,
+ 0xff,0x74,0xcc,0xb1,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x54,
+ 0xcc,0xad,0x00,0x01,0xff,0x74,0xcc,0xad,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0xa4,
+ 0x00,0x01,0xff,0x75,0xcc,0xa4,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x55,0xcc,0xb0,
+ 0x00,0x01,0xff,0x75,0xcc,0xb0,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,0xad,0x00,0x01,
+ 0xff,0x75,0xcc,0xad,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x55,0xcc,0x83,
+ 0xcc,0x81,0x00,0x01,0xff,0x75,0xcc,0x83,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x55,
+ 0xcc,0x84,0xcc,0x88,0x00,0x01,0xff,0x75,0xcc,0x84,0xcc,0x88,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0x56,0xcc,0x83,0x00,0x01,0xff,0x76,0xcc,0x83,0x00,0x10,0x08,0x01,
+ 0xff,0x56,0xcc,0xa3,0x00,0x01,0xff,0x76,0xcc,0xa3,0x00,0xe0,0x10,0x02,0xcf,0x86,
+ 0xd5,0xe1,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x57,0xcc,
+ 0x80,0x00,0x01,0xff,0x77,0xcc,0x80,0x00,0x10,0x08,0x01,0xff,0x57,0xcc,0x81,0x00,
+ 0x01,0xff,0x77,0xcc,0x81,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x57,0xcc,0x88,0x00,
+ 0x01,0xff,0x77,0xcc,0x88,0x00,0x10,0x08,0x01,0xff,0x57,0xcc,0x87,0x00,0x01,0xff,
+ 0x77,0xcc,0x87,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x57,0xcc,0xa3,0x00,
+ 0x01,0xff,0x77,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x58,0xcc,0x87,0x00,0x01,0xff,
+ 0x78,0xcc,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x58,0xcc,0x88,0x00,0x01,0xff,
+ 0x78,0xcc,0x88,0x00,0x10,0x08,0x01,0xff,0x59,0xcc,0x87,0x00,0x01,0xff,0x79,0xcc,
+ 0x87,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x5a,0xcc,0x82,0x00,
+ 0x01,0xff,0x7a,0xcc,0x82,0x00,0x10,0x08,0x01,0xff,0x5a,0xcc,0xa3,0x00,0x01,0xff,
+ 0x7a,0xcc,0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x5a,0xcc,0xb1,0x00,0x01,0xff,
+ 0x7a,0xcc,0xb1,0x00,0x10,0x08,0x01,0xff,0x68,0xcc,0xb1,0x00,0x01,0xff,0x74,0xcc,
+ 0x88,0x00,0x92,0x1d,0xd1,0x10,0x10,0x08,0x01,0xff,0x77,0xcc,0x8a,0x00,0x01,0xff,
+ 0x79,0xcc,0x8a,0x00,0x10,0x04,0x01,0x00,0x02,0xff,0xc5,0xbf,0xcc,0x87,0x00,0x0a,
+ 0x00,0xd4,0x98,0xd3,0x48,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x41,0xcc,0xa3,
+ 0x00,0x01,0xff,0x61,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x41,0xcc,0x89,0x00,0x01,
+ 0xff,0x61,0xcc,0x89,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x41,0xcc,0x82,0xcc,0x81,
+ 0x00,0x01,0xff,0x61,0xcc,0x82,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0x82,
+ 0xcc,0x80,0x00,0x01,0xff,0x61,0xcc,0x82,0xcc,0x80,0x00,0xd2,0x28,0xd1,0x14,0x10,
+ 0x0a,0x01,0xff,0x41,0xcc,0x82,0xcc,0x89,0x00,0x01,0xff,0x61,0xcc,0x82,0xcc,0x89,
+ 0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0x82,0xcc,0x83,0x00,0x01,0xff,0x61,0xcc,0x82,
+ 0xcc,0x83,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x41,0xcc,0xa3,0xcc,0x82,0x00,0x01,
+ 0xff,0x61,0xcc,0xa3,0xcc,0x82,0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0x86,0xcc,0x81,
+ 0x00,0x01,0xff,0x61,0xcc,0x86,0xcc,0x81,0x00,0xd3,0x50,0xd2,0x28,0xd1,0x14,0x10,
+ 0x0a,0x01,0xff,0x41,0xcc,0x86,0xcc,0x80,0x00,0x01,0xff,0x61,0xcc,0x86,0xcc,0x80,
+ 0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0x86,0xcc,0x89,0x00,0x01,0xff,0x61,0xcc,0x86,
+ 0xcc,0x89,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x41,0xcc,0x86,0xcc,0x83,0x00,0x01,
+ 0xff,0x61,0xcc,0x86,0xcc,0x83,0x00,0x10,0x0a,0x01,0xff,0x41,0xcc,0xa3,0xcc,0x86,
+ 0x00,0x01,0xff,0x61,0xcc,0xa3,0xcc,0x86,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0x45,0xcc,0xa3,0x00,0x01,0xff,0x65,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x45,
+ 0xcc,0x89,0x00,0x01,0xff,0x65,0xcc,0x89,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x45,
+ 0xcc,0x83,0x00,0x01,0xff,0x65,0xcc,0x83,0x00,0x10,0x0a,0x01,0xff,0x45,0xcc,0x82,
+ 0xcc,0x81,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x81,0x00,0xcf,0x86,0xe5,0x31,0x01,
+ 0xd4,0x90,0xd3,0x50,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x45,0xcc,0x82,0xcc,
+ 0x80,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0x45,0xcc,
+ 0x82,0xcc,0x89,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x89,0x00,0xd1,0x14,0x10,0x0a,
+ 0x01,0xff,0x45,0xcc,0x82,0xcc,0x83,0x00,0x01,0xff,0x65,0xcc,0x82,0xcc,0x83,0x00,
+ 0x10,0x0a,0x01,0xff,0x45,0xcc,0xa3,0xcc,0x82,0x00,0x01,0xff,0x65,0xcc,0xa3,0xcc,
+ 0x82,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0x49,0xcc,0x89,0x00,0x01,0xff,
+ 0x69,0xcc,0x89,0x00,0x10,0x08,0x01,0xff,0x49,0xcc,0xa3,0x00,0x01,0xff,0x69,0xcc,
+ 0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0x4f,0xcc,0xa3,0x00,0x01,0xff,0x6f,0xcc,
+ 0xa3,0x00,0x10,0x08,0x01,0xff,0x4f,0xcc,0x89,0x00,0x01,0xff,0x6f,0xcc,0x89,0x00,
+ 0xd3,0x50,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x82,0xcc,0x81,0x00,
+ 0x01,0xff,0x6f,0xcc,0x82,0xcc,0x81,0x00,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x82,0xcc,
+ 0x80,0x00,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x80,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,
+ 0x4f,0xcc,0x82,0xcc,0x89,0x00,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x89,0x00,0x10,0x0a,
+ 0x01,0xff,0x4f,0xcc,0x82,0xcc,0x83,0x00,0x01,0xff,0x6f,0xcc,0x82,0xcc,0x83,0x00,
+ 0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,0xcc,0xa3,0xcc,0x82,0x00,0x01,0xff,
+ 0x6f,0xcc,0xa3,0xcc,0x82,0x00,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x9b,0xcc,0x81,0x00,
+ 0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x81,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,0xcc,
+ 0x9b,0xcc,0x80,0x00,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,
+ 0x4f,0xcc,0x9b,0xcc,0x89,0x00,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x89,0x00,0xd4,0x98,
+ 0xd3,0x48,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x9b,0xcc,0x83,0x00,
+ 0x01,0xff,0x6f,0xcc,0x9b,0xcc,0x83,0x00,0x10,0x0a,0x01,0xff,0x4f,0xcc,0x9b,0xcc,
+ 0xa3,0x00,0x01,0xff,0x6f,0xcc,0x9b,0xcc,0xa3,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0x55,0xcc,0xa3,0x00,0x01,0xff,0x75,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,0x55,0xcc,
+ 0x89,0x00,0x01,0xff,0x75,0xcc,0x89,0x00,0xd2,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,
+ 0x55,0xcc,0x9b,0xcc,0x81,0x00,0x01,0xff,0x75,0xcc,0x9b,0xcc,0x81,0x00,0x10,0x0a,
+ 0x01,0xff,0x55,0xcc,0x9b,0xcc,0x80,0x00,0x01,0xff,0x75,0xcc,0x9b,0xcc,0x80,0x00,
+ 0xd1,0x14,0x10,0x0a,0x01,0xff,0x55,0xcc,0x9b,0xcc,0x89,0x00,0x01,0xff,0x75,0xcc,
+ 0x9b,0xcc,0x89,0x00,0x10,0x0a,0x01,0xff,0x55,0xcc,0x9b,0xcc,0x83,0x00,0x01,0xff,
+ 0x75,0xcc,0x9b,0xcc,0x83,0x00,0xd3,0x44,0xd2,0x24,0xd1,0x14,0x10,0x0a,0x01,0xff,
+ 0x55,0xcc,0x9b,0xcc,0xa3,0x00,0x01,0xff,0x75,0xcc,0x9b,0xcc,0xa3,0x00,0x10,0x08,
+ 0x01,0xff,0x59,0xcc,0x80,0x00,0x01,0xff,0x79,0xcc,0x80,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0x59,0xcc,0xa3,0x00,0x01,0xff,0x79,0xcc,0xa3,0x00,0x10,0x08,0x01,0xff,
+ 0x59,0xcc,0x89,0x00,0x01,0xff,0x79,0xcc,0x89,0x00,0x92,0x14,0x91,0x10,0x10,0x08,
+ 0x01,0xff,0x59,0xcc,0x83,0x00,0x01,0xff,0x79,0xcc,0x83,0x00,0x0a,0x00,0x0a,0x00,
+ 0xe1,0xc0,0x04,0xe0,0x80,0x02,0xcf,0x86,0xe5,0x2d,0x01,0xd4,0xa8,0xd3,0x54,0xd2,
+ 0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x93,0x00,0x01,0xff,0xce,0xb1,
+ 0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,
+ 0xce,0xb1,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,
+ 0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,
+ 0xff,0xce,0xb1,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcd,0x82,
+ 0x00,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0x91,0xcc,0x93,0x00,0x01,0xff,
+ 0xce,0x91,0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0x91,0xcc,0x93,0xcc,0x80,0x00,
+ 0x01,0xff,0xce,0x91,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,
+ 0x91,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0x91,0xcc,0x94,0xcc,0x81,0x00,0x10,
+ 0x0b,0x01,0xff,0xce,0x91,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0x91,0xcc,0x94,
+ 0xcd,0x82,0x00,0xd3,0x42,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb5,0xcc,
+ 0x93,0x00,0x01,0xff,0xce,0xb5,0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0xb5,0xcc,
+ 0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0xb5,0xcc,0x94,0xcc,0x80,0x00,0x91,0x16,0x10,
+ 0x0b,0x01,0xff,0xce,0xb5,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0xb5,0xcc,0x94,
+ 0xcc,0x81,0x00,0x00,0x00,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0x95,0xcc,
+ 0x93,0x00,0x01,0xff,0xce,0x95,0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0x95,0xcc,
+ 0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0x95,0xcc,0x94,0xcc,0x80,0x00,0x91,0x16,0x10,
+ 0x0b,0x01,0xff,0xce,0x95,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0x95,0xcc,0x94,
+ 0xcc,0x81,0x00,0x00,0x00,0xd4,0xa8,0xd3,0x54,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,
+ 0xff,0xce,0xb7,0xcc,0x93,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0x00,0x10,0x0b,0x01,
+ 0xff,0xce,0xb7,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x80,
+ 0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,
+ 0xce,0xb7,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcd,
+ 0x82,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcd,0x82,0x00,0xd2,0x28,0xd1,0x12,0x10,
+ 0x09,0x01,0xff,0xce,0x97,0xcc,0x93,0x00,0x01,0xff,0xce,0x97,0xcc,0x94,0x00,0x10,
+ 0x0b,0x01,0xff,0xce,0x97,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0x97,0xcc,0x94,
+ 0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0x97,0xcc,0x93,0xcc,0x81,0x00,
+ 0x01,0xff,0xce,0x97,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,0xce,0x97,0xcc,
+ 0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0x97,0xcc,0x94,0xcd,0x82,0x00,0xd3,0x54,0xd2,
+ 0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb9,0xcc,0x93,0x00,0x01,0xff,0xce,0xb9,
+ 0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0xb9,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,
+ 0xce,0xb9,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb9,0xcc,
+ 0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0xb9,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,
+ 0xff,0xce,0xb9,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0xb9,0xcc,0x94,0xcd,0x82,
+ 0x00,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0x99,0xcc,0x93,0x00,0x01,0xff,
+ 0xce,0x99,0xcc,0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0x99,0xcc,0x93,0xcc,0x80,0x00,
+ 0x01,0xff,0xce,0x99,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,
+ 0x99,0xcc,0x93,0xcc,0x81,0x00,0x01,0xff,0xce,0x99,0xcc,0x94,0xcc,0x81,0x00,0x10,
+ 0x0b,0x01,0xff,0xce,0x99,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0x99,0xcc,0x94,
+ 0xcd,0x82,0x00,0xcf,0x86,0xe5,0x13,0x01,0xd4,0x84,0xd3,0x42,0xd2,0x28,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xce,0xbf,0xcc,0x93,0x00,0x01,0xff,0xce,0xbf,0xcc,0x94,0x00,
+ 0x10,0x0b,0x01,0xff,0xce,0xbf,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0xbf,0xcc,
+ 0x94,0xcc,0x80,0x00,0x91,0x16,0x10,0x0b,0x01,0xff,0xce,0xbf,0xcc,0x93,0xcc,0x81,
+ 0x00,0x01,0xff,0xce,0xbf,0xcc,0x94,0xcc,0x81,0x00,0x00,0x00,0xd2,0x28,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xce,0x9f,0xcc,0x93,0x00,0x01,0xff,0xce,0x9f,0xcc,0x94,0x00,
+ 0x10,0x0b,0x01,0xff,0xce,0x9f,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,0x9f,0xcc,
+ 0x94,0xcc,0x80,0x00,0x91,0x16,0x10,0x0b,0x01,0xff,0xce,0x9f,0xcc,0x93,0xcc,0x81,
+ 0x00,0x01,0xff,0xce,0x9f,0xcc,0x94,0xcc,0x81,0x00,0x00,0x00,0xd3,0x54,0xd2,0x28,
+ 0xd1,0x12,0x10,0x09,0x01,0xff,0xcf,0x85,0xcc,0x93,0x00,0x01,0xff,0xcf,0x85,0xcc,
+ 0x94,0x00,0x10,0x0b,0x01,0xff,0xcf,0x85,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xcf,
+ 0x85,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xcf,0x85,0xcc,0x93,
+ 0xcc,0x81,0x00,0x01,0xff,0xcf,0x85,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,
+ 0xcf,0x85,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xcf,0x85,0xcc,0x94,0xcd,0x82,0x00,
+ 0xd2,0x1c,0xd1,0x0d,0x10,0x04,0x00,0x00,0x01,0xff,0xce,0xa5,0xcc,0x94,0x00,0x10,
+ 0x04,0x00,0x00,0x01,0xff,0xce,0xa5,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x0f,0x10,0x04,
+ 0x00,0x00,0x01,0xff,0xce,0xa5,0xcc,0x94,0xcc,0x81,0x00,0x10,0x04,0x00,0x00,0x01,
+ 0xff,0xce,0xa5,0xcc,0x94,0xcd,0x82,0x00,0xd4,0xa8,0xd3,0x54,0xd2,0x28,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xcf,0x89,0xcc,0x93,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0x00,
+ 0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xcf,0x89,0xcc,
+ 0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcc,0x81,
+ 0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,0xcf,0x89,
+ 0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcd,0x82,0x00,0xd2,0x28,
+ 0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xa9,0xcc,0x93,0x00,0x01,0xff,0xce,0xa9,0xcc,
+ 0x94,0x00,0x10,0x0b,0x01,0xff,0xce,0xa9,0xcc,0x93,0xcc,0x80,0x00,0x01,0xff,0xce,
+ 0xa9,0xcc,0x94,0xcc,0x80,0x00,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xa9,0xcc,0x93,
+ 0xcc,0x81,0x00,0x01,0xff,0xce,0xa9,0xcc,0x94,0xcc,0x81,0x00,0x10,0x0b,0x01,0xff,
+ 0xce,0xa9,0xcc,0x93,0xcd,0x82,0x00,0x01,0xff,0xce,0xa9,0xcc,0x94,0xcd,0x82,0x00,
+ 0xd3,0x48,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x80,0x00,0x01,
+ 0xff,0xce,0xb1,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xce,0xb5,0xcc,0x80,0x00,0x01,
+ 0xff,0xce,0xb5,0xcc,0x81,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb7,0xcc,0x80,
+ 0x00,0x01,0xff,0xce,0xb7,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xce,0xb9,0xcc,0x80,
+ 0x00,0x01,0xff,0xce,0xb9,0xcc,0x81,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,
+ 0xce,0xbf,0xcc,0x80,0x00,0x01,0xff,0xce,0xbf,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,
+ 0xcf,0x85,0xcc,0x80,0x00,0x01,0xff,0xcf,0x85,0xcc,0x81,0x00,0x91,0x12,0x10,0x09,
+ 0x01,0xff,0xcf,0x89,0xcc,0x80,0x00,0x01,0xff,0xcf,0x89,0xcc,0x81,0x00,0x00,0x00,
+ 0xe0,0xe1,0x02,0xcf,0x86,0xe5,0x91,0x01,0xd4,0xc8,0xd3,0x64,0xd2,0x30,0xd1,0x16,
+ 0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcd,0x85,0x00,0x01,0xff,0xce,0xb1,0xcc,
+ 0x94,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcc,0x80,0xcd,0x85,
+ 0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcc,0x80,0xcd,0x85,0x00,0xd1,0x1a,0x10,0x0d,
+ 0x01,0xff,0xce,0xb1,0xcc,0x93,0xcc,0x81,0xcd,0x85,0x00,0x01,0xff,0xce,0xb1,0xcc,
+ 0x94,0xcc,0x81,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0xb1,0xcc,0x93,0xcd,0x82,
+ 0xcd,0x85,0x00,0x01,0xff,0xce,0xb1,0xcc,0x94,0xcd,0x82,0xcd,0x85,0x00,0xd2,0x30,
+ 0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0x91,0xcc,0x93,0xcd,0x85,0x00,0x01,0xff,0xce,
+ 0x91,0xcc,0x94,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0x91,0xcc,0x93,0xcc,0x80,
+ 0xcd,0x85,0x00,0x01,0xff,0xce,0x91,0xcc,0x94,0xcc,0x80,0xcd,0x85,0x00,0xd1,0x1a,
+ 0x10,0x0d,0x01,0xff,0xce,0x91,0xcc,0x93,0xcc,0x81,0xcd,0x85,0x00,0x01,0xff,0xce,
+ 0x91,0xcc,0x94,0xcc,0x81,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0x91,0xcc,0x93,
+ 0xcd,0x82,0xcd,0x85,0x00,0x01,0xff,0xce,0x91,0xcc,0x94,0xcd,0x82,0xcd,0x85,0x00,
+ 0xd3,0x64,0xd2,0x30,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcd,0x85,
+ 0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0xb7,
+ 0xcc,0x93,0xcc,0x80,0xcd,0x85,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x80,0xcd,
+ 0x85,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,0xce,0xb7,0xcc,0x93,0xcc,0x81,0xcd,0x85,
+ 0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcc,0x81,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,
+ 0xce,0xb7,0xcc,0x93,0xcd,0x82,0xcd,0x85,0x00,0x01,0xff,0xce,0xb7,0xcc,0x94,0xcd,
+ 0x82,0xcd,0x85,0x00,0xd2,0x30,0xd1,0x16,0x10,0x0b,0x01,0xff,0xce,0x97,0xcc,0x93,
+ 0xcd,0x85,0x00,0x01,0xff,0xce,0x97,0xcc,0x94,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,
+ 0xce,0x97,0xcc,0x93,0xcc,0x80,0xcd,0x85,0x00,0x01,0xff,0xce,0x97,0xcc,0x94,0xcc,
+ 0x80,0xcd,0x85,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,0xce,0x97,0xcc,0x93,0xcc,0x81,
+ 0xcd,0x85,0x00,0x01,0xff,0xce,0x97,0xcc,0x94,0xcc,0x81,0xcd,0x85,0x00,0x10,0x0d,
+ 0x01,0xff,0xce,0x97,0xcc,0x93,0xcd,0x82,0xcd,0x85,0x00,0x01,0xff,0xce,0x97,0xcc,
+ 0x94,0xcd,0x82,0xcd,0x85,0x00,0xd4,0xc8,0xd3,0x64,0xd2,0x30,0xd1,0x16,0x10,0x0b,
+ 0x01,0xff,0xcf,0x89,0xcc,0x93,0xcd,0x85,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcd,
+ 0x85,0x00,0x10,0x0d,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcc,0x80,0xcd,0x85,0x00,0x01,
+ 0xff,0xcf,0x89,0xcc,0x94,0xcc,0x80,0xcd,0x85,0x00,0xd1,0x1a,0x10,0x0d,0x01,0xff,
+ 0xcf,0x89,0xcc,0x93,0xcc,0x81,0xcd,0x85,0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcc,
+ 0x81,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xcf,0x89,0xcc,0x93,0xcd,0x82,0xcd,0x85,
+ 0x00,0x01,0xff,0xcf,0x89,0xcc,0x94,0xcd,0x82,0xcd,0x85,0x00,0xd2,0x30,0xd1,0x16,
+ 0x10,0x0b,0x01,0xff,0xce,0xa9,0xcc,0x93,0xcd,0x85,0x00,0x01,0xff,0xce,0xa9,0xcc,
+ 0x94,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0xa9,0xcc,0x93,0xcc,0x80,0xcd,0x85,
+ 0x00,0x01,0xff,0xce,0xa9,0xcc,0x94,0xcc,0x80,0xcd,0x85,0x00,0xd1,0x1a,0x10,0x0d,
+ 0x01,0xff,0xce,0xa9,0xcc,0x93,0xcc,0x81,0xcd,0x85,0x00,0x01,0xff,0xce,0xa9,0xcc,
+ 0x94,0xcc,0x81,0xcd,0x85,0x00,0x10,0x0d,0x01,0xff,0xce,0xa9,0xcc,0x93,0xcd,0x82,
+ 0xcd,0x85,0x00,0x01,0xff,0xce,0xa9,0xcc,0x94,0xcd,0x82,0xcd,0x85,0x00,0xd3,0x49,
+ 0xd2,0x26,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0xb1,0xcc,0x86,0x00,0x01,0xff,0xce,
+ 0xb1,0xcc,0x84,0x00,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,0x80,0xcd,0x85,0x00,0x01,
+ 0xff,0xce,0xb1,0xcd,0x85,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xce,0xb1,0xcc,0x81,
+ 0xcd,0x85,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xce,0xb1,0xcd,0x82,0x00,0x01,0xff,
+ 0xce,0xb1,0xcd,0x82,0xcd,0x85,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,
+ 0x91,0xcc,0x86,0x00,0x01,0xff,0xce,0x91,0xcc,0x84,0x00,0x10,0x09,0x01,0xff,0xce,
+ 0x91,0xcc,0x80,0x00,0x01,0xff,0xce,0x91,0xcc,0x81,0x00,0xd1,0x0d,0x10,0x09,0x01,
+ 0xff,0xce,0x91,0xcd,0x85,0x00,0x01,0x00,0x10,0x07,0x01,0xff,0xce,0xb9,0x00,0x01,
+ 0x00,0xcf,0x86,0xe5,0x16,0x01,0xd4,0x8f,0xd3,0x44,0xd2,0x21,0xd1,0x0d,0x10,0x04,
+ 0x01,0x00,0x01,0xff,0xc2,0xa8,0xcd,0x82,0x00,0x10,0x0b,0x01,0xff,0xce,0xb7,0xcc,
+ 0x80,0xcd,0x85,0x00,0x01,0xff,0xce,0xb7,0xcd,0x85,0x00,0xd1,0x0f,0x10,0x0b,0x01,
+ 0xff,0xce,0xb7,0xcc,0x81,0xcd,0x85,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xce,0xb7,
+ 0xcd,0x82,0x00,0x01,0xff,0xce,0xb7,0xcd,0x82,0xcd,0x85,0x00,0xd2,0x24,0xd1,0x12,
+ 0x10,0x09,0x01,0xff,0xce,0x95,0xcc,0x80,0x00,0x01,0xff,0xce,0x95,0xcc,0x81,0x00,
+ 0x10,0x09,0x01,0xff,0xce,0x97,0xcc,0x80,0x00,0x01,0xff,0xce,0x97,0xcc,0x81,0x00,
+ 0xd1,0x13,0x10,0x09,0x01,0xff,0xce,0x97,0xcd,0x85,0x00,0x01,0xff,0xe1,0xbe,0xbf,
+ 0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0xe1,0xbe,0xbf,0xcc,0x81,0x00,0x01,0xff,0xe1,
+ 0xbe,0xbf,0xcd,0x82,0x00,0xd3,0x40,0xd2,0x28,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,
+ 0xb9,0xcc,0x86,0x00,0x01,0xff,0xce,0xb9,0xcc,0x84,0x00,0x10,0x0b,0x01,0xff,0xce,
+ 0xb9,0xcc,0x88,0xcc,0x80,0x00,0x01,0xff,0xce,0xb9,0xcc,0x88,0xcc,0x81,0x00,0x51,
+ 0x04,0x00,0x00,0x10,0x09,0x01,0xff,0xce,0xb9,0xcd,0x82,0x00,0x01,0xff,0xce,0xb9,
+ 0xcc,0x88,0xcd,0x82,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,0x99,0xcc,
+ 0x86,0x00,0x01,0xff,0xce,0x99,0xcc,0x84,0x00,0x10,0x09,0x01,0xff,0xce,0x99,0xcc,
+ 0x80,0x00,0x01,0xff,0xce,0x99,0xcc,0x81,0x00,0xd1,0x0e,0x10,0x04,0x00,0x00,0x01,
+ 0xff,0xe1,0xbf,0xbe,0xcc,0x80,0x00,0x10,0x0a,0x01,0xff,0xe1,0xbf,0xbe,0xcc,0x81,
+ 0x00,0x01,0xff,0xe1,0xbf,0xbe,0xcd,0x82,0x00,0xd4,0x93,0xd3,0x4e,0xd2,0x28,0xd1,
+ 0x12,0x10,0x09,0x01,0xff,0xcf,0x85,0xcc,0x86,0x00,0x01,0xff,0xcf,0x85,0xcc,0x84,
+ 0x00,0x10,0x0b,0x01,0xff,0xcf,0x85,0xcc,0x88,0xcc,0x80,0x00,0x01,0xff,0xcf,0x85,
+ 0xcc,0x88,0xcc,0x81,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xcf,0x81,0xcc,0x93,0x00,
+ 0x01,0xff,0xcf,0x81,0xcc,0x94,0x00,0x10,0x09,0x01,0xff,0xcf,0x85,0xcd,0x82,0x00,
+ 0x01,0xff,0xcf,0x85,0xcc,0x88,0xcd,0x82,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,
+ 0xff,0xce,0xa5,0xcc,0x86,0x00,0x01,0xff,0xce,0xa5,0xcc,0x84,0x00,0x10,0x09,0x01,
+ 0xff,0xce,0xa5,0xcc,0x80,0x00,0x01,0xff,0xce,0xa5,0xcc,0x81,0x00,0xd1,0x12,0x10,
+ 0x09,0x01,0xff,0xce,0xa1,0xcc,0x94,0x00,0x01,0xff,0xc2,0xa8,0xcc,0x80,0x00,0x10,
+ 0x09,0x01,0xff,0xc2,0xa8,0xcc,0x81,0x00,0x01,0xff,0x60,0x00,0xd3,0x3b,0xd2,0x18,
+ 0x51,0x04,0x00,0x00,0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x80,0xcd,0x85,0x00,0x01,
+ 0xff,0xcf,0x89,0xcd,0x85,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xcf,0x89,0xcc,0x81,
+ 0xcd,0x85,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xcf,0x89,0xcd,0x82,0x00,0x01,0xff,
+ 0xcf,0x89,0xcd,0x82,0xcd,0x85,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xce,
+ 0x9f,0xcc,0x80,0x00,0x01,0xff,0xce,0x9f,0xcc,0x81,0x00,0x10,0x09,0x01,0xff,0xce,
+ 0xa9,0xcc,0x80,0x00,0x01,0xff,0xce,0xa9,0xcc,0x81,0x00,0xd1,0x10,0x10,0x09,0x01,
+ 0xff,0xce,0xa9,0xcd,0x85,0x00,0x01,0xff,0xc2,0xb4,0x00,0x10,0x04,0x01,0x00,0x00,
+ 0x00,0xe0,0x7e,0x0c,0xcf,0x86,0xe5,0xbb,0x08,0xe4,0x14,0x06,0xe3,0xf7,0x02,0xe2,
+ 0xbd,0x01,0xd1,0xd0,0xd0,0x4f,0xcf,0x86,0xd5,0x2e,0x94,0x2a,0xd3,0x18,0x92,0x14,
+ 0x91,0x10,0x10,0x08,0x01,0xff,0xe2,0x80,0x82,0x00,0x01,0xff,0xe2,0x80,0x83,0x00,
+ 0x01,0x00,0x01,0x00,0x92,0x0d,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,
+ 0x00,0x01,0xff,0x00,0x01,0x00,0x94,0x1b,0x53,0x04,0x01,0x00,0xd2,0x09,0x11,0x04,
+ 0x01,0x00,0x01,0xff,0x00,0x51,0x05,0x01,0xff,0x00,0x10,0x05,0x01,0xff,0x00,0x04,
+ 0x00,0x01,0x00,0xcf,0x86,0xd5,0x48,0xd4,0x1c,0xd3,0x10,0x52,0x04,0x01,0x00,0x51,
+ 0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x06,0x00,0x52,0x04,0x04,0x00,0x11,0x04,0x04,
+ 0x00,0x06,0x00,0xd3,0x1c,0xd2,0x0c,0x51,0x04,0x06,0x00,0x10,0x04,0x06,0x00,0x07,
+ 0x00,0xd1,0x08,0x10,0x04,0x07,0x00,0x08,0x00,0x10,0x04,0x08,0x00,0x06,0x00,0x52,
+ 0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x06,0x00,0xd4,0x23,0xd3,
+ 0x14,0x52,0x05,0x06,0xff,0x00,0x91,0x0a,0x10,0x05,0x0a,0xff,0x00,0x00,0xff,0x00,
+ 0x0f,0xff,0x00,0x92,0x0a,0x11,0x05,0x0f,0xff,0x00,0x01,0xff,0x00,0x01,0xff,0x00,
+ 0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x06,0x00,0x00,0x00,0x01,0x00,
+ 0x01,0x00,0xd0,0x7e,0xcf,0x86,0xd5,0x34,0xd4,0x14,0x53,0x04,0x01,0x00,0x52,0x04,
+ 0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0xd3,0x10,0x52,0x04,
+ 0x08,0x00,0x91,0x08,0x10,0x04,0x08,0x00,0x0c,0x00,0x0c,0x00,0x52,0x04,0x0c,0x00,
+ 0x91,0x08,0x10,0x04,0x0c,0x00,0x00,0x00,0x00,0x00,0xd4,0x1c,0x53,0x04,0x01,0x00,
+ 0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x02,0x00,0x91,0x08,0x10,0x04,
+ 0x03,0x00,0x04,0x00,0x04,0x00,0xd3,0x10,0xd2,0x08,0x11,0x04,0x06,0x00,0x08,0x00,
+ 0x11,0x04,0x08,0x00,0x0b,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x0b,0x00,0x0c,0x00,
+ 0x10,0x04,0x0e,0x00,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x11,0x00,0x13,0x00,
+ 0xcf,0x86,0xd5,0x28,0x54,0x04,0x00,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,0x01,0xe6,
+ 0x01,0x01,0x01,0xe6,0xd2,0x0c,0x51,0x04,0x01,0x01,0x10,0x04,0x01,0x01,0x01,0xe6,
+ 0x91,0x08,0x10,0x04,0x01,0xe6,0x01,0x00,0x01,0x00,0xd4,0x30,0xd3,0x1c,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x01,0x00,0x01,0xe6,0x04,0x00,0xd1,0x08,0x10,0x04,0x06,0x00,
+ 0x06,0x01,0x10,0x04,0x06,0x01,0x06,0xe6,0x92,0x10,0xd1,0x08,0x10,0x04,0x06,0xdc,
+ 0x06,0xe6,0x10,0x04,0x06,0x01,0x08,0x01,0x09,0xdc,0x93,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x0a,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x81,0xd0,0x4f,
+ 0xcf,0x86,0x55,0x04,0x01,0x00,0xd4,0x29,0xd3,0x13,0x52,0x04,0x01,0x00,0x51,0x04,
+ 0x01,0x00,0x10,0x07,0x01,0xff,0xce,0xa9,0x00,0x01,0x00,0x92,0x12,0x51,0x04,0x01,
+ 0x00,0x10,0x06,0x01,0xff,0x4b,0x00,0x01,0xff,0x41,0xcc,0x8a,0x00,0x01,0x00,0x53,
+ 0x04,0x01,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x01,0x00,0x04,0x00,0x10,0x04,0x04,
+ 0x00,0x07,0x00,0x91,0x08,0x10,0x04,0x08,0x00,0x06,0x00,0x06,0x00,0xcf,0x86,0x95,
+ 0x2c,0xd4,0x18,0x53,0x04,0x06,0x00,0x52,0x04,0x06,0x00,0xd1,0x08,0x10,0x04,0x08,
+ 0x00,0x09,0x00,0x10,0x04,0x09,0x00,0x0a,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,0x0b,
+ 0x00,0x10,0x04,0x0b,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd0,0x68,0xcf,
+ 0x86,0xd5,0x48,0xd4,0x28,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x04,0x00,0x91,0x08,0x10,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x0a,0x00,0x0b,0x00,0x11,0x00,0x00,0x00,0x53,0x04,0x01,0x00,0x92,
+ 0x18,0x51,0x04,0x01,0x00,0x10,0x0a,0x01,0xff,0xe2,0x86,0x90,0xcc,0xb8,0x00,0x01,
+ 0xff,0xe2,0x86,0x92,0xcc,0xb8,0x00,0x01,0x00,0x94,0x1a,0x53,0x04,0x01,0x00,0x52,
+ 0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x0a,0x01,0xff,0xe2,0x86,0x94,0xcc,0xb8,
+ 0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0x2e,0x94,0x2a,0x53,0x04,0x01,0x00,0x52,
+ 0x04,0x01,0x00,0xd1,0x0e,0x10,0x04,0x01,0x00,0x01,0xff,0xe2,0x87,0x90,0xcc,0xb8,
+ 0x00,0x10,0x0a,0x01,0xff,0xe2,0x87,0x94,0xcc,0xb8,0x00,0x01,0xff,0xe2,0x87,0x92,
+ 0xcc,0xb8,0x00,0x01,0x00,0xd4,0x14,0x53,0x04,0x01,0x00,0x92,0x0c,0x51,0x04,0x01,
+ 0x00,0x10,0x04,0x01,0x00,0x04,0x00,0x04,0x00,0x93,0x08,0x12,0x04,0x04,0x00,0x06,
+ 0x00,0x06,0x00,0xe2,0x38,0x02,0xe1,0x3f,0x01,0xd0,0x68,0xcf,0x86,0xd5,0x3e,0x94,
+ 0x3a,0xd3,0x16,0x52,0x04,0x01,0x00,0x91,0x0e,0x10,0x0a,0x01,0xff,0xe2,0x88,0x83,
+ 0xcc,0xb8,0x00,0x01,0x00,0x01,0x00,0xd2,0x12,0x91,0x0e,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0xe2,0x88,0x88,0xcc,0xb8,0x00,0x01,0x00,0x91,0x0e,0x10,0x0a,0x01,0xff,0xe2,
+ 0x88,0x8b,0xcc,0xb8,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x94,0x24,0x93,0x20,0x52,
+ 0x04,0x01,0x00,0xd1,0x0e,0x10,0x0a,0x01,0xff,0xe2,0x88,0xa3,0xcc,0xb8,0x00,0x01,
+ 0x00,0x10,0x0a,0x01,0xff,0xe2,0x88,0xa5,0xcc,0xb8,0x00,0x01,0x00,0x01,0x00,0x01,
+ 0x00,0xcf,0x86,0xd5,0x48,0x94,0x44,0xd3,0x2e,0xd2,0x12,0x91,0x0e,0x10,0x04,0x01,
+ 0x00,0x01,0xff,0xe2,0x88,0xbc,0xcc,0xb8,0x00,0x01,0x00,0xd1,0x0e,0x10,0x0a,0x01,
+ 0xff,0xe2,0x89,0x83,0xcc,0xb8,0x00,0x01,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xe2,
+ 0x89,0x85,0xcc,0xb8,0x00,0x92,0x12,0x91,0x0e,0x10,0x04,0x01,0x00,0x01,0xff,0xe2,
+ 0x89,0x88,0xcc,0xb8,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x40,0xd3,0x1e,0x92,
+ 0x1a,0xd1,0x0c,0x10,0x08,0x01,0xff,0x3d,0xcc,0xb8,0x00,0x01,0x00,0x10,0x0a,0x01,
+ 0xff,0xe2,0x89,0xa1,0xcc,0xb8,0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0xd1,
+ 0x0e,0x10,0x04,0x01,0x00,0x01,0xff,0xe2,0x89,0x8d,0xcc,0xb8,0x00,0x10,0x08,0x01,
+ 0xff,0x3c,0xcc,0xb8,0x00,0x01,0xff,0x3e,0xcc,0xb8,0x00,0xd3,0x30,0xd2,0x18,0x91,
+ 0x14,0x10,0x0a,0x01,0xff,0xe2,0x89,0xa4,0xcc,0xb8,0x00,0x01,0xff,0xe2,0x89,0xa5,
+ 0xcc,0xb8,0x00,0x01,0x00,0x91,0x14,0x10,0x0a,0x01,0xff,0xe2,0x89,0xb2,0xcc,0xb8,
+ 0x00,0x01,0xff,0xe2,0x89,0xb3,0xcc,0xb8,0x00,0x01,0x00,0x92,0x18,0x91,0x14,0x10,
+ 0x0a,0x01,0xff,0xe2,0x89,0xb6,0xcc,0xb8,0x00,0x01,0xff,0xe2,0x89,0xb7,0xcc,0xb8,
+ 0x00,0x01,0x00,0x01,0x00,0xd0,0x86,0xcf,0x86,0xd5,0x50,0x94,0x4c,0xd3,0x30,0xd2,
+ 0x18,0x91,0x14,0x10,0x0a,0x01,0xff,0xe2,0x89,0xba,0xcc,0xb8,0x00,0x01,0xff,0xe2,
+ 0x89,0xbb,0xcc,0xb8,0x00,0x01,0x00,0x91,0x14,0x10,0x0a,0x01,0xff,0xe2,0x8a,0x82,
+ 0xcc,0xb8,0x00,0x01,0xff,0xe2,0x8a,0x83,0xcc,0xb8,0x00,0x01,0x00,0x92,0x18,0x91,
+ 0x14,0x10,0x0a,0x01,0xff,0xe2,0x8a,0x86,0xcc,0xb8,0x00,0x01,0xff,0xe2,0x8a,0x87,
+ 0xcc,0xb8,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x94,0x30,0x53,0x04,0x01,0x00,0x52,
+ 0x04,0x01,0x00,0xd1,0x14,0x10,0x0a,0x01,0xff,0xe2,0x8a,0xa2,0xcc,0xb8,0x00,0x01,
+ 0xff,0xe2,0x8a,0xa8,0xcc,0xb8,0x00,0x10,0x0a,0x01,0xff,0xe2,0x8a,0xa9,0xcc,0xb8,
+ 0x00,0x01,0xff,0xe2,0x8a,0xab,0xcc,0xb8,0x00,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,
+ 0x00,0xd4,0x5c,0xd3,0x2c,0x92,0x28,0xd1,0x14,0x10,0x0a,0x01,0xff,0xe2,0x89,0xbc,
+ 0xcc,0xb8,0x00,0x01,0xff,0xe2,0x89,0xbd,0xcc,0xb8,0x00,0x10,0x0a,0x01,0xff,0xe2,
+ 0x8a,0x91,0xcc,0xb8,0x00,0x01,0xff,0xe2,0x8a,0x92,0xcc,0xb8,0x00,0x01,0x00,0xd2,
+ 0x18,0x51,0x04,0x01,0x00,0x10,0x0a,0x01,0xff,0xe2,0x8a,0xb2,0xcc,0xb8,0x00,0x01,
+ 0xff,0xe2,0x8a,0xb3,0xcc,0xb8,0x00,0x91,0x14,0x10,0x0a,0x01,0xff,0xe2,0x8a,0xb4,
+ 0xcc,0xb8,0x00,0x01,0xff,0xe2,0x8a,0xb5,0xcc,0xb8,0x00,0x01,0x00,0x93,0x0c,0x92,
+ 0x08,0x11,0x04,0x01,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0xd1,0x64,0xd0,0x3e,0xcf,
+ 0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,0x00,0x04,
+ 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x94,0x20,0x53,0x04,0x01,0x00,0x92,
+ 0x18,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0xe3,0x80,0x88,0x00,0x10,0x08,0x01,
+ 0xff,0xe3,0x80,0x89,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,
+ 0x00,0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x04,0x00,0x91,0x08,0x10,0x04,0x06,0x00,0x04,0x00,0x04,0x00,0xd0,
+ 0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,0x04,0x00,0x53,0x04,0x04,0x00,0x92,0x0c,0x51,
+ 0x04,0x04,0x00,0x10,0x04,0x04,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0xcf,0x86,0xd5,
+ 0x2c,0xd4,0x14,0x53,0x04,0x06,0x00,0x52,0x04,0x06,0x00,0x51,0x04,0x06,0x00,0x10,
+ 0x04,0x06,0x00,0x07,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x07,0x00,0x08,
+ 0x00,0x08,0x00,0x08,0x00,0x12,0x04,0x08,0x00,0x09,0x00,0xd4,0x14,0x53,0x04,0x09,
+ 0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x0b,0x00,0x0c,0x00,0x0c,0x00,0x0c,0x00,0xd3,
+ 0x08,0x12,0x04,0x0c,0x00,0x10,0x00,0xd2,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,0x10,
+ 0x00,0x12,0x00,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x13,0x00,0xd3,0xa6,0xd2,
+ 0x74,0xd1,0x40,0xd0,0x22,0xcf,0x86,0x55,0x04,0x01,0x00,0x94,0x18,0x93,0x14,0x52,
+ 0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x04,0x00,0x10,0x04,0x04,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0x95,0x18,0x94,0x14,0x53,0x04,0x01,0x00,0x92,
+ 0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+ 0x00,0xd0,0x06,0xcf,0x06,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,0x00,0xd4,0x14,0x53,
+ 0x04,0x01,0x00,0x92,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x06,0x00,0x06,
+ 0x00,0x53,0x04,0x06,0x00,0x52,0x04,0x06,0x00,0x51,0x04,0x06,0x00,0x10,0x04,0x06,
+ 0x00,0x07,0x00,0xd1,0x06,0xcf,0x06,0x01,0x00,0xd0,0x1a,0xcf,0x86,0x95,0x14,0x54,
+ 0x04,0x01,0x00,0x93,0x0c,0x52,0x04,0x01,0x00,0x11,0x04,0x01,0x00,0x06,0x00,0x06,
+ 0x00,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,0x00,0x54,0x04,0x01,0x00,0x13,0x04,0x04,
+ 0x00,0x06,0x00,0xd2,0xdc,0xd1,0x48,0xd0,0x26,0xcf,0x86,0x95,0x20,0x54,0x04,0x01,
+ 0x00,0xd3,0x0c,0x52,0x04,0x01,0x00,0x11,0x04,0x07,0x00,0x06,0x00,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x08,0x00,0x04,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0x55,
+ 0x04,0x01,0x00,0x54,0x04,0x01,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,0x04,0x00,0x06,
+ 0x00,0x06,0x00,0x52,0x04,0x06,0x00,0x11,0x04,0x06,0x00,0x08,0x00,0xd0,0x5e,0xcf,
+ 0x86,0xd5,0x2c,0xd4,0x10,0x53,0x04,0x06,0x00,0x92,0x08,0x11,0x04,0x06,0x00,0x07,
+ 0x00,0x07,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,0x07,0x00,0x08,0x00,0x08,0x00,0x52,
+ 0x04,0x08,0x00,0x91,0x08,0x10,0x04,0x08,0x00,0x0a,0x00,0x0b,0x00,0xd4,0x10,0x93,
+ 0x0c,0x92,0x08,0x11,0x04,0x07,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0xd3,0x10,0x92,
+ 0x0c,0x51,0x04,0x08,0x00,0x10,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,0x52,0x04,0x0a,
+ 0x00,0x91,0x08,0x10,0x04,0x0a,0x00,0x0b,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x1c,0x94,
+ 0x18,0xd3,0x08,0x12,0x04,0x0a,0x00,0x0b,0x00,0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,
+ 0x00,0x10,0x04,0x0c,0x00,0x0b,0x00,0x0b,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x51,
+ 0x04,0x0b,0x00,0x10,0x04,0x0c,0x00,0x0b,0x00,0x0c,0x00,0x0b,0x00,0x0b,0x00,0xd1,
+ 0xa8,0xd0,0x42,0xcf,0x86,0xd5,0x28,0x94,0x24,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,
+ 0x04,0x10,0x00,0x01,0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x0c,0x00,0x01,
+ 0x00,0x92,0x08,0x11,0x04,0x01,0x00,0x0c,0x00,0x01,0x00,0x01,0x00,0x94,0x14,0x53,
+ 0x04,0x01,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x0c,0x00,0x01,0x00,0x01,0x00,0x01,
+ 0x00,0x01,0x00,0xcf,0x86,0xd5,0x40,0xd4,0x18,0x53,0x04,0x01,0x00,0x52,0x04,0x01,
+ 0x00,0xd1,0x08,0x10,0x04,0x0c,0x00,0x01,0x00,0x10,0x04,0x0c,0x00,0x01,0x00,0xd3,
+ 0x18,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x0c,0x00,0x51,0x04,0x0c,
+ 0x00,0x10,0x04,0x01,0x00,0x0b,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x0c,0x00,0xd4,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0c,
+ 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x06,0x00,0x93,0x0c,0x52,0x04,0x06,0x00,0x11,
+ 0x04,0x06,0x00,0x01,0x00,0x01,0x00,0xd0,0x3e,0xcf,0x86,0xd5,0x18,0x54,0x04,0x01,
+ 0x00,0x93,0x10,0x52,0x04,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x0c,0x00,0x0c,
+ 0x00,0x01,0x00,0x54,0x04,0x01,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0c,
+ 0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,
+ 0x04,0x01,0x00,0x0c,0x00,0xcf,0x86,0xd5,0x2c,0x94,0x28,0xd3,0x10,0x52,0x04,0x08,
+ 0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x09,0x00,0xd2,0x0c,0x51,0x04,0x09,
+ 0x00,0x10,0x04,0x09,0x00,0x0d,0x00,0x91,0x08,0x10,0x04,0x0a,0x00,0x0d,0x00,0x0c,
+ 0x00,0x06,0x00,0x94,0x0c,0x53,0x04,0x06,0x00,0x12,0x04,0x06,0x00,0x0a,0x00,0x06,
+ 0x00,0xe4,0x39,0x01,0xd3,0x0c,0xd2,0x06,0xcf,0x06,0x04,0x00,0xcf,0x06,0x06,0x00,
+ 0xd2,0x30,0xd1,0x06,0xcf,0x06,0x06,0x00,0xd0,0x06,0xcf,0x06,0x06,0x00,0xcf,0x86,
+ 0x95,0x1e,0x54,0x04,0x06,0x00,0x53,0x04,0x06,0x00,0x52,0x04,0x06,0x00,0x91,0x0e,
+ 0x10,0x0a,0x06,0xff,0xe2,0xab,0x9d,0xcc,0xb8,0x00,0x06,0x00,0x06,0x00,0x06,0x00,
+ 0xd1,0x80,0xd0,0x3a,0xcf,0x86,0xd5,0x28,0xd4,0x10,0x53,0x04,0x07,0x00,0x52,0x04,
+ 0x07,0x00,0x11,0x04,0x07,0x00,0x08,0x00,0xd3,0x08,0x12,0x04,0x08,0x00,0x09,0x00,
+ 0x92,0x0c,0x51,0x04,0x09,0x00,0x10,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,0x94,0x0c,
+ 0x93,0x08,0x12,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,0x0a,0x00,0xcf,0x86,0xd5,0x30,
+ 0xd4,0x14,0x53,0x04,0x0a,0x00,0x52,0x04,0x0a,0x00,0x91,0x08,0x10,0x04,0x0a,0x00,
+ 0x10,0x00,0x10,0x00,0xd3,0x10,0x52,0x04,0x0a,0x00,0x91,0x08,0x10,0x04,0x0a,0x00,
+ 0x0b,0x00,0x0b,0x00,0x92,0x08,0x11,0x04,0x0b,0x00,0x10,0x00,0x10,0x00,0x54,0x04,
+ 0x10,0x00,0x93,0x0c,0x52,0x04,0x10,0x00,0x11,0x04,0x00,0x00,0x10,0x00,0x10,0x00,
+ 0xd0,0x32,0xcf,0x86,0xd5,0x14,0x54,0x04,0x10,0x00,0x93,0x0c,0x52,0x04,0x10,0x00,
+ 0x11,0x04,0x10,0x00,0x00,0x00,0x10,0x00,0x54,0x04,0x10,0x00,0x53,0x04,0x10,0x00,
+ 0xd2,0x08,0x11,0x04,0x10,0x00,0x14,0x00,0x91,0x08,0x10,0x04,0x14,0x00,0x10,0x00,
+ 0x10,0x00,0xcf,0x86,0xd5,0x28,0xd4,0x14,0x53,0x04,0x10,0x00,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x10,0x00,0x15,0x00,0x10,0x00,0x10,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,
+ 0x10,0x00,0x10,0x04,0x13,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0xd4,0x0c,0x53,0x04,
+ 0x14,0x00,0x12,0x04,0x14,0x00,0x11,0x00,0x53,0x04,0x14,0x00,0x52,0x04,0x14,0x00,
+ 0x51,0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x15,0x00,0xe3,0xb9,0x01,0xd2,0xac,0xd1,
+ 0x68,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x08,0x00,0x94,0x14,0x53,0x04,0x08,0x00,0x52,
+ 0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,0x00,0x08,0x00,0xcf,
+ 0x86,0xd5,0x18,0x54,0x04,0x08,0x00,0x53,0x04,0x08,0x00,0x52,0x04,0x08,0x00,0x51,
+ 0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,0x00,0xd4,0x14,0x53,0x04,0x09,0x00,0x52,
+ 0x04,0x09,0x00,0x91,0x08,0x10,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,0xd3,0x10,0x92,
+ 0x0c,0x91,0x08,0x10,0x04,0x0b,0x00,0x0a,0x00,0x0a,0x00,0x09,0x00,0x52,0x04,0x0a,
+ 0x00,0x11,0x04,0x0a,0x00,0x0b,0x00,0xd0,0x06,0xcf,0x06,0x08,0x00,0xcf,0x86,0x55,
+ 0x04,0x08,0x00,0xd4,0x1c,0x53,0x04,0x08,0x00,0xd2,0x0c,0x51,0x04,0x08,0x00,0x10,
+ 0x04,0x08,0x00,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,0x0b,0xe6,0xd3,
+ 0x0c,0x92,0x08,0x11,0x04,0x0b,0xe6,0x0d,0x00,0x00,0x00,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0xd1,0x6c,0xd0,0x2a,0xcf,0x86,0x55,
+ 0x04,0x08,0x00,0x94,0x20,0xd3,0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,
+ 0x04,0x00,0x00,0x0d,0x00,0x52,0x04,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x0d,
+ 0x00,0x00,0x00,0x08,0x00,0xcf,0x86,0x55,0x04,0x08,0x00,0xd4,0x1c,0xd3,0x0c,0x52,
+ 0x04,0x08,0x00,0x11,0x04,0x08,0x00,0x0d,0x00,0x52,0x04,0x00,0x00,0x51,0x04,0x00,
+ 0x00,0x10,0x04,0x00,0x00,0x08,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0c,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,
+ 0x04,0x00,0x00,0x0c,0x09,0xd0,0x5a,0xcf,0x86,0xd5,0x18,0x54,0x04,0x08,0x00,0x93,
+ 0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,0x00,0x00,
+ 0x00,0xd4,0x20,0xd3,0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,
+ 0x00,0x00,0x00,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,
+ 0x00,0xd3,0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,
+ 0x00,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,0x00,0xcf,
+ 0x86,0x95,0x40,0xd4,0x20,0xd3,0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,
+ 0x04,0x08,0x00,0x00,0x00,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,
+ 0x00,0x00,0x00,0xd3,0x10,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,
+ 0x00,0x00,0x00,0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x00,
+ 0x00,0x0a,0xe6,0xd2,0x9c,0xd1,0x68,0xd0,0x32,0xcf,0x86,0xd5,0x14,0x54,0x04,0x08,
+ 0x00,0x53,0x04,0x08,0x00,0x52,0x04,0x0a,0x00,0x11,0x04,0x08,0x00,0x0a,0x00,0x54,
+ 0x04,0x0a,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0a,0x00,0x0b,0x00,0x0d,
+ 0x00,0x0d,0x00,0x12,0x04,0x0d,0x00,0x10,0x00,0xcf,0x86,0x95,0x30,0x94,0x2c,0xd3,
+ 0x18,0xd2,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x12,0x00,0x91,0x08,0x10,
+ 0x04,0x12,0x00,0x13,0x00,0x13,0x00,0xd2,0x08,0x11,0x04,0x13,0x00,0x14,0x00,0x51,
+ 0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0xd0,0x1e,0xcf,
+ 0x86,0x95,0x18,0x54,0x04,0x04,0x00,0x53,0x04,0x04,0x00,0x92,0x0c,0x51,0x04,0x04,
+ 0x00,0x10,0x04,0x00,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0xcf,0x86,0x55,0x04,0x04,
+ 0x00,0x54,0x04,0x04,0x00,0x93,0x08,0x12,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0xd1,
+ 0x06,0xcf,0x06,0x04,0x00,0xd0,0x06,0xcf,0x06,0x04,0x00,0xcf,0x86,0xd5,0x14,0x54,
+ 0x04,0x04,0x00,0x93,0x0c,0x52,0x04,0x04,0x00,0x11,0x04,0x04,0x00,0x00,0x00,0x00,
+ 0x00,0x54,0x04,0x00,0x00,0x53,0x04,0x04,0x00,0x12,0x04,0x04,0x00,0x00,0x00,0xcf,
+ 0x86,0xe5,0xa6,0x05,0xe4,0x9f,0x05,0xe3,0x96,0x04,0xe2,0xe4,0x03,0xe1,0xc0,0x01,
+ 0xd0,0x3e,0xcf,0x86,0x55,0x04,0x01,0x00,0xd4,0x1c,0x53,0x04,0x01,0x00,0xd2,0x0c,
+ 0x51,0x04,0x01,0x00,0x10,0x04,0x01,0xda,0x01,0xe4,0x91,0x08,0x10,0x04,0x01,0xe8,
+ 0x01,0xde,0x01,0xe0,0x53,0x04,0x01,0x00,0xd2,0x0c,0x51,0x04,0x04,0x00,0x10,0x04,
+ 0x04,0x00,0x06,0x00,0x51,0x04,0x06,0x00,0x10,0x04,0x04,0x00,0x01,0x00,0xcf,0x86,
+ 0xd5,0xaa,0xd4,0x32,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,
+ 0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x81,
+ 0x8b,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x81,0x8d,0xe3,0x82,
+ 0x99,0x00,0x01,0x00,0xd3,0x3c,0xd2,0x1e,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x81,
+ 0x8f,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x81,0x91,0xe3,0x82,
+ 0x99,0x00,0x01,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x81,0x93,0xe3,0x82,0x99,
+ 0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x81,0x95,0xe3,0x82,0x99,0x00,0x01,0x00,
+ 0xd2,0x1e,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x81,0x97,0xe3,0x82,0x99,0x00,0x01,
+ 0x00,0x10,0x0b,0x01,0xff,0xe3,0x81,0x99,0xe3,0x82,0x99,0x00,0x01,0x00,0xd1,0x0f,
+ 0x10,0x0b,0x01,0xff,0xe3,0x81,0x9b,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x0b,0x01,
+ 0xff,0xe3,0x81,0x9d,0xe3,0x82,0x99,0x00,0x01,0x00,0xd4,0x53,0xd3,0x3c,0xd2,0x1e,
+ 0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x81,0x9f,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,
+ 0x0b,0x01,0xff,0xe3,0x81,0xa1,0xe3,0x82,0x99,0x00,0x01,0x00,0xd1,0x0f,0x10,0x04,
+ 0x01,0x00,0x01,0xff,0xe3,0x81,0xa4,0xe3,0x82,0x99,0x00,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0xe3,0x81,0xa6,0xe3,0x82,0x99,0x00,0x92,0x13,0x91,0x0f,0x10,0x04,0x01,0x00,
+ 0x01,0xff,0xe3,0x81,0xa8,0xe3,0x82,0x99,0x00,0x01,0x00,0x01,0x00,0xd3,0x4a,0xd2,
+ 0x25,0xd1,0x16,0x10,0x0b,0x01,0xff,0xe3,0x81,0xaf,0xe3,0x82,0x99,0x00,0x01,0xff,
+ 0xe3,0x81,0xaf,0xe3,0x82,0x9a,0x00,0x10,0x04,0x01,0x00,0x01,0xff,0xe3,0x81,0xb2,
+ 0xe3,0x82,0x99,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x81,0xb2,0xe3,0x82,0x9a,
+ 0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x81,0xb5,0xe3,0x82,0x99,0x00,0x01,0xff,
+ 0xe3,0x81,0xb5,0xe3,0x82,0x9a,0x00,0xd2,0x1e,0xd1,0x0f,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0xe3,0x81,0xb8,0xe3,0x82,0x99,0x00,0x10,0x0b,0x01,0xff,0xe3,0x81,0xb8,0xe3,
+ 0x82,0x9a,0x00,0x01,0x00,0x91,0x16,0x10,0x0b,0x01,0xff,0xe3,0x81,0xbb,0xe3,0x82,
+ 0x99,0x00,0x01,0xff,0xe3,0x81,0xbb,0xe3,0x82,0x9a,0x00,0x01,0x00,0xd0,0xee,0xcf,
+ 0x86,0xd5,0x42,0x54,0x04,0x01,0x00,0xd3,0x1b,0x52,0x04,0x01,0x00,0xd1,0x0f,0x10,
+ 0x0b,0x01,0xff,0xe3,0x81,0x86,0xe3,0x82,0x99,0x00,0x06,0x00,0x10,0x04,0x06,0x00,
+ 0x00,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,0x00,0x01,0x08,0x10,0x04,0x01,0x08,
+ 0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x82,0x9d,0xe3,0x82,0x99,
+ 0x00,0x06,0x00,0xd4,0x32,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x06,0x00,0x01,
+ 0x00,0x01,0x00,0x01,0x00,0x52,0x04,0x01,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,
+ 0x82,0xab,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x82,0xad,0xe3,
+ 0x82,0x99,0x00,0x01,0x00,0xd3,0x3c,0xd2,0x1e,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,
+ 0x82,0xaf,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x82,0xb1,0xe3,
+ 0x82,0x99,0x00,0x01,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x82,0xb3,0xe3,0x82,
+ 0x99,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x82,0xb5,0xe3,0x82,0x99,0x00,0x01,
+ 0x00,0xd2,0x1e,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x82,0xb7,0xe3,0x82,0x99,0x00,
+ 0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x82,0xb9,0xe3,0x82,0x99,0x00,0x01,0x00,0xd1,
+ 0x0f,0x10,0x0b,0x01,0xff,0xe3,0x82,0xbb,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x0b,
+ 0x01,0xff,0xe3,0x82,0xbd,0xe3,0x82,0x99,0x00,0x01,0x00,0xcf,0x86,0xd5,0xd5,0xd4,
+ 0x53,0xd3,0x3c,0xd2,0x1e,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,0x82,0xbf,0xe3,0x82,
+ 0x99,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x83,0x81,0xe3,0x82,0x99,0x00,0x01,
+ 0x00,0xd1,0x0f,0x10,0x04,0x01,0x00,0x01,0xff,0xe3,0x83,0x84,0xe3,0x82,0x99,0x00,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0xe3,0x83,0x86,0xe3,0x82,0x99,0x00,0x92,0x13,0x91,
+ 0x0f,0x10,0x04,0x01,0x00,0x01,0xff,0xe3,0x83,0x88,0xe3,0x82,0x99,0x00,0x01,0x00,
+ 0x01,0x00,0xd3,0x4a,0xd2,0x25,0xd1,0x16,0x10,0x0b,0x01,0xff,0xe3,0x83,0x8f,0xe3,
+ 0x82,0x99,0x00,0x01,0xff,0xe3,0x83,0x8f,0xe3,0x82,0x9a,0x00,0x10,0x04,0x01,0x00,
+ 0x01,0xff,0xe3,0x83,0x92,0xe3,0x82,0x99,0x00,0xd1,0x0f,0x10,0x0b,0x01,0xff,0xe3,
+ 0x83,0x92,0xe3,0x82,0x9a,0x00,0x01,0x00,0x10,0x0b,0x01,0xff,0xe3,0x83,0x95,0xe3,
+ 0x82,0x99,0x00,0x01,0xff,0xe3,0x83,0x95,0xe3,0x82,0x9a,0x00,0xd2,0x1e,0xd1,0x0f,
+ 0x10,0x04,0x01,0x00,0x01,0xff,0xe3,0x83,0x98,0xe3,0x82,0x99,0x00,0x10,0x0b,0x01,
+ 0xff,0xe3,0x83,0x98,0xe3,0x82,0x9a,0x00,0x01,0x00,0x91,0x16,0x10,0x0b,0x01,0xff,
+ 0xe3,0x83,0x9b,0xe3,0x82,0x99,0x00,0x01,0xff,0xe3,0x83,0x9b,0xe3,0x82,0x9a,0x00,
+ 0x01,0x00,0x54,0x04,0x01,0x00,0xd3,0x22,0x52,0x04,0x01,0x00,0xd1,0x0f,0x10,0x0b,
+ 0x01,0xff,0xe3,0x82,0xa6,0xe3,0x82,0x99,0x00,0x01,0x00,0x10,0x04,0x01,0x00,0x01,
+ 0xff,0xe3,0x83,0xaf,0xe3,0x82,0x99,0x00,0xd2,0x25,0xd1,0x16,0x10,0x0b,0x01,0xff,
+ 0xe3,0x83,0xb0,0xe3,0x82,0x99,0x00,0x01,0xff,0xe3,0x83,0xb1,0xe3,0x82,0x99,0x00,
+ 0x10,0x0b,0x01,0xff,0xe3,0x83,0xb2,0xe3,0x82,0x99,0x00,0x01,0x00,0x51,0x04,0x01,
+ 0x00,0x10,0x0b,0x01,0xff,0xe3,0x83,0xbd,0xe3,0x82,0x99,0x00,0x06,0x00,0xd1,0x65,
+ 0xd0,0x46,0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x52,0x04,0x00,0x00,0x91,0x08,
+ 0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd4,0x18,0x53,0x04,
+ 0x01,0x00,0x52,0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x0a,0x00,0x10,0x04,
+ 0x13,0x00,0x14,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,
+ 0x01,0x00,0x01,0x00,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,0x00,0x94,0x15,0x93,0x11,
+ 0x52,0x04,0x01,0x00,0x91,0x09,0x10,0x05,0x01,0xff,0x00,0x01,0x00,0x01,0x00,0x01,
+ 0x00,0x01,0x00,0xd0,0x32,0xcf,0x86,0xd5,0x18,0x94,0x14,0x53,0x04,0x01,0x00,0x52,
+ 0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x54,
+ 0x04,0x04,0x00,0x53,0x04,0x04,0x00,0x92,0x0c,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,
+ 0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,0x08,0x14,0x04,0x08,0x00,0x0a,0x00,0x94,
+ 0x0c,0x93,0x08,0x12,0x04,0x0a,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0xd2,0xa4,0xd1,
+ 0x5c,0xd0,0x22,0xcf,0x86,0x95,0x1c,0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,0x52,
+ 0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x07,0x00,0x10,0x04,0x07,0x00,0x00,
+ 0x00,0x01,0x00,0xcf,0x86,0xd5,0x20,0xd4,0x0c,0x93,0x08,0x12,0x04,0x01,0x00,0x0b,
+ 0x00,0x0b,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x07,0x00,0x06,0x00,0x06,
+ 0x00,0x06,0x00,0x06,0x00,0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,0x52,0x04,0x01,
+ 0x00,0x51,0x04,0x07,0x00,0x10,0x04,0x08,0x00,0x01,0x00,0xd0,0x1e,0xcf,0x86,0x55,
+ 0x04,0x01,0x00,0x54,0x04,0x01,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x01,
+ 0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0xcf,0x86,0xd5,0x10,0x94,0x0c,0x53,
+ 0x04,0x01,0x00,0x12,0x04,0x01,0x00,0x07,0x00,0x01,0x00,0x54,0x04,0x01,0x00,0x53,
+ 0x04,0x01,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x16,
+ 0x00,0xd1,0x30,0xd0,0x06,0xcf,0x06,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,0x00,0x54,
+ 0x04,0x01,0x00,0xd3,0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x07,0x00,0x92,0x0c,0x51,0x04,0x07,0x00,0x10,0x04,0x07,0x00,0x01,0x00,0x01,
+ 0x00,0xd0,0x06,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x14,0x54,0x04,0x01,0x00,0x53,
+ 0x04,0x01,0x00,0x52,0x04,0x01,0x00,0x11,0x04,0x01,0x00,0x07,0x00,0x54,0x04,0x01,
+ 0x00,0x53,0x04,0x01,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x07,0x00,0xcf,0x06,0x04,0x00,0xcf,0x06,0x04,0x00,0xd1,0x48,0xd0,0x40,0xcf,
+ 0x86,0xd5,0x06,0xcf,0x06,0x04,0x00,0xd4,0x06,0xcf,0x06,0x04,0x00,0xd3,0x2c,0xd2,
+ 0x06,0xcf,0x06,0x04,0x00,0xd1,0x06,0xcf,0x06,0x04,0x00,0xd0,0x1a,0xcf,0x86,0x55,
+ 0x04,0x04,0x00,0x54,0x04,0x04,0x00,0x93,0x0c,0x52,0x04,0x04,0x00,0x11,0x04,0x04,
+ 0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x07,0x00,0xcf,0x06,0x01,0x00,0xcf,0x86,0xcf,
+ 0x06,0x01,0x00,0xcf,0x86,0xcf,0x06,0x01,0x00,0xe2,0x71,0x05,0xd1,0x8c,0xd0,0x08,
+ 0xcf,0x86,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x01,0x00,0xd4,0x06,
+ 0xcf,0x06,0x01,0x00,0xd3,0x06,0xcf,0x06,0x01,0x00,0xd2,0x06,0xcf,0x06,0x01,0x00,
+ 0xd1,0x06,0xcf,0x06,0x01,0x00,0xd0,0x22,0xcf,0x86,0x55,0x04,0x01,0x00,0xd4,0x10,
+ 0x93,0x0c,0x52,0x04,0x01,0x00,0x11,0x04,0x01,0x00,0x08,0x00,0x08,0x00,0x53,0x04,
+ 0x08,0x00,0x12,0x04,0x08,0x00,0x0a,0x00,0xcf,0x86,0xd5,0x28,0xd4,0x18,0xd3,0x08,
+ 0x12,0x04,0x0a,0x00,0x0b,0x00,0x52,0x04,0x0b,0x00,0x91,0x08,0x10,0x04,0x0d,0x00,
+ 0x11,0x00,0x11,0x00,0x93,0x0c,0x52,0x04,0x11,0x00,0x11,0x04,0x11,0x00,0x13,0x00,
+ 0x13,0x00,0x94,0x14,0x53,0x04,0x13,0x00,0x92,0x0c,0x51,0x04,0x13,0x00,0x10,0x04,
+ 0x13,0x00,0x14,0x00,0x14,0x00,0x00,0x00,0xe0,0xdb,0x04,0xcf,0x86,0xe5,0xdf,0x01,
+ 0xd4,0x06,0xcf,0x06,0x04,0x00,0xd3,0x74,0xd2,0x6e,0xd1,0x06,0xcf,0x06,0x04,0x00,
+ 0xd0,0x3e,0xcf,0x86,0xd5,0x18,0x94,0x14,0x53,0x04,0x04,0x00,0x52,0x04,0x04,0x00,
+ 0x91,0x08,0x10,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xd4,0x10,0x93,0x0c,
+ 0x92,0x08,0x11,0x04,0x04,0x00,0x06,0x00,0x04,0x00,0x04,0x00,0x93,0x10,0x52,0x04,
+ 0x04,0x00,0x91,0x08,0x10,0x04,0x06,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0xcf,0x86,
+ 0x95,0x24,0x94,0x20,0x93,0x1c,0xd2,0x0c,0x91,0x08,0x10,0x04,0x04,0x00,0x06,0x00,
+ 0x04,0x00,0xd1,0x08,0x10,0x04,0x04,0x00,0x06,0x00,0x10,0x04,0x04,0x00,0x00,0x00,
+ 0x00,0x00,0x0b,0x00,0x0b,0x00,0xcf,0x06,0x0a,0x00,0xd2,0x84,0xd1,0x4c,0xd0,0x16,
+ 0xcf,0x86,0x55,0x04,0x0a,0x00,0x94,0x0c,0x53,0x04,0x0a,0x00,0x12,0x04,0x0a,0x00,
+ 0x00,0x00,0x00,0x00,0xcf,0x86,0x55,0x04,0x0a,0x00,0xd4,0x1c,0xd3,0x0c,0x92,0x08,
+ 0x11,0x04,0x0c,0x00,0x0a,0x00,0x0a,0x00,0x52,0x04,0x0a,0x00,0x51,0x04,0x0a,0x00,
+ 0x10,0x04,0x0a,0x00,0x0a,0xe6,0xd3,0x08,0x12,0x04,0x0a,0x00,0x0d,0xe6,0x52,0x04,
+ 0x0d,0xe6,0x11,0x04,0x0a,0xe6,0x0a,0x00,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,
+ 0x0a,0x00,0x53,0x04,0x0a,0x00,0x52,0x04,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,
+ 0x11,0xe6,0x0d,0xe6,0x0b,0x00,0xcf,0x86,0x55,0x04,0x0b,0x00,0x54,0x04,0x0b,0x00,
+ 0x93,0x0c,0x92,0x08,0x11,0x04,0x0b,0xe6,0x0b,0x00,0x0b,0x00,0x00,0x00,0xd1,0x40,
+ 0xd0,0x3a,0xcf,0x86,0xd5,0x24,0x54,0x04,0x08,0x00,0xd3,0x10,0x52,0x04,0x08,0x00,
+ 0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x09,0x00,0x92,0x0c,0x51,0x04,0x09,0x00,
+ 0x10,0x04,0x09,0x00,0x0a,0x00,0x0a,0x00,0x94,0x10,0x93,0x0c,0x92,0x08,0x11,0x04,
+ 0x09,0x00,0x0a,0x00,0x0a,0x00,0x0a,0x00,0x0a,0x00,0xcf,0x06,0x0a,0x00,0xd0,0x5e,
+ 0xcf,0x86,0xd5,0x28,0xd4,0x18,0x53,0x04,0x0a,0x00,0x52,0x04,0x0a,0x00,0xd1,0x08,
+ 0x10,0x04,0x0a,0x00,0x0c,0x00,0x10,0x04,0x0c,0x00,0x11,0x00,0x93,0x0c,0x92,0x08,
+ 0x11,0x04,0x0c,0x00,0x0d,0x00,0x10,0x00,0x10,0x00,0xd4,0x1c,0x53,0x04,0x0c,0x00,
+ 0xd2,0x0c,0x51,0x04,0x0c,0x00,0x10,0x04,0x0d,0x00,0x10,0x00,0x51,0x04,0x10,0x00,
+ 0x10,0x04,0x12,0x00,0x14,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,0x10,0x00,0x11,0x00,
+ 0x11,0x00,0x92,0x08,0x11,0x04,0x14,0x00,0x15,0x00,0x15,0x00,0xcf,0x86,0xd5,0x1c,
+ 0x94,0x18,0x93,0x14,0xd2,0x08,0x11,0x04,0x00,0x00,0x15,0x00,0x51,0x04,0x15,0x00,
+ 0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x04,0x00,0x00,0xd3,0x10,
+ 0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x92,0x0c,
+ 0x51,0x04,0x0d,0x00,0x10,0x04,0x0c,0x00,0x0a,0x00,0x0a,0x00,0xe4,0xf2,0x02,0xe3,
+ 0x65,0x01,0xd2,0x98,0xd1,0x48,0xd0,0x36,0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,
+ 0x52,0x04,0x08,0x00,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x09,0x08,0x00,0x08,0x00,
+ 0x08,0x00,0xd4,0x0c,0x53,0x04,0x08,0x00,0x12,0x04,0x08,0x00,0x00,0x00,0x53,0x04,
+ 0x0b,0x00,0x92,0x08,0x11,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0x55,0x04,
+ 0x09,0x00,0x54,0x04,0x09,0x00,0x13,0x04,0x09,0x00,0x00,0x00,0xd0,0x06,0xcf,0x06,
+ 0x0a,0x00,0xcf,0x86,0xd5,0x2c,0xd4,0x1c,0xd3,0x10,0x52,0x04,0x0a,0x00,0x91,0x08,
+ 0x10,0x04,0x0a,0x09,0x12,0x00,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,
+ 0x0a,0x00,0x53,0x04,0x0a,0x00,0x92,0x08,0x11,0x04,0x0a,0x00,0x00,0x00,0x00,0x00,
+ 0x54,0x04,0x0b,0xe6,0xd3,0x0c,0x92,0x08,0x11,0x04,0x0b,0xe6,0x0b,0x00,0x0b,0x00,
+ 0x52,0x04,0x0b,0x00,0x11,0x04,0x11,0x00,0x14,0x00,0xd1,0x60,0xd0,0x22,0xcf,0x86,
+ 0x55,0x04,0x0a,0x00,0x94,0x18,0x53,0x04,0x0a,0x00,0xd2,0x0c,0x51,0x04,0x0a,0x00,
+ 0x10,0x04,0x0a,0x00,0x0a,0xdc,0x11,0x04,0x0a,0xdc,0x0a,0x00,0x0a,0x00,0xcf,0x86,
+ 0xd5,0x24,0x54,0x04,0x0a,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x0a,0x00,0x10,0x04,
+ 0x0a,0x00,0x0a,0x09,0x00,0x00,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,
+ 0x00,0x00,0x0a,0x00,0x54,0x04,0x0b,0x00,0x53,0x04,0x0b,0x00,0x52,0x04,0x0b,0x00,
+ 0x91,0x08,0x10,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0xd0,0x1e,0xcf,0x86,0x55,0x04,
+ 0x0b,0x00,0x54,0x04,0x0b,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,0x0b,0x00,0x10,0x04,
+ 0x0b,0x00,0x0b,0x07,0x0b,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x34,0xd4,0x20,0xd3,0x10,
+ 0x92,0x0c,0x91,0x08,0x10,0x04,0x0b,0x09,0x0b,0x00,0x0b,0x00,0x0b,0x00,0x52,0x04,
+ 0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x00,0x00,0x0b,0x00,0x53,0x04,0x0b,0x00,
+ 0xd2,0x08,0x11,0x04,0x0b,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x0b,0x00,0x54,0x04,
+ 0x10,0x00,0x53,0x04,0x10,0x00,0x52,0x04,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,
+ 0x10,0x00,0x00,0x00,0xd2,0xd0,0xd1,0x50,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x0a,0x00,
+ 0x54,0x04,0x0a,0x00,0x93,0x10,0x52,0x04,0x0a,0x00,0x51,0x04,0x0a,0x00,0x10,0x04,
+ 0x0a,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,0x20,0xd4,0x10,0x53,0x04,0x0a,0x00,
+ 0x52,0x04,0x0a,0x00,0x11,0x04,0x0a,0x00,0x00,0x00,0x53,0x04,0x0a,0x00,0x92,0x08,
+ 0x11,0x04,0x0a,0x00,0x00,0x00,0x0a,0x00,0x54,0x04,0x0b,0x00,0x53,0x04,0x0b,0x00,
+ 0x12,0x04,0x0b,0x00,0x10,0x00,0xd0,0x3a,0xcf,0x86,0x55,0x04,0x0b,0x00,0x54,0x04,
+ 0x0b,0x00,0xd3,0x1c,0xd2,0x0c,0x91,0x08,0x10,0x04,0x0b,0xe6,0x0b,0x00,0x0b,0xe6,
+ 0xd1,0x08,0x10,0x04,0x0b,0xdc,0x0b,0x00,0x10,0x04,0x0b,0x00,0x0b,0xe6,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x0b,0xe6,0x0b,0x00,0x0b,0x00,0x11,0x04,0x0b,0x00,0x0b,0xe6,
+ 0xcf,0x86,0xd5,0x2c,0xd4,0x18,0x93,0x14,0x92,0x10,0xd1,0x08,0x10,0x04,0x0b,0x00,
+ 0x0b,0xe6,0x10,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x00,0x00,
+ 0x92,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x0b,0x00,0x0b,0x00,0x54,0x04,
+ 0x0d,0x00,0x93,0x10,0x52,0x04,0x0d,0x00,0x51,0x04,0x0d,0x00,0x10,0x04,0x0d,0x09,
+ 0x00,0x00,0x00,0x00,0xd1,0x8c,0xd0,0x72,0xcf,0x86,0xd5,0x4c,0xd4,0x30,0xd3,0x18,
+ 0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0c,0x00,0x0c,0x00,0x51,0x04,0x0c,0x00,
+ 0x10,0x04,0x0c,0x00,0x00,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0c,0x00,
+ 0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,0x00,0x00,0x93,0x18,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x00,0x00,0x0c,0x00,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,
+ 0x0c,0x00,0x00,0x00,0x00,0x00,0x94,0x20,0xd3,0x10,0x52,0x04,0x0c,0x00,0x51,0x04,
+ 0x0c,0x00,0x10,0x04,0x0c,0x00,0x00,0x00,0x52,0x04,0x0c,0x00,0x51,0x04,0x0c,0x00,
+ 0x10,0x04,0x0c,0x00,0x00,0x00,0x10,0x00,0xcf,0x86,0x55,0x04,0x10,0x00,0x94,0x10,
+ 0x93,0x0c,0x52,0x04,0x11,0x00,0x11,0x04,0x10,0x00,0x15,0x00,0x00,0x00,0x11,0x00,
+ 0xd0,0x06,0xcf,0x06,0x11,0x00,0xcf,0x86,0x55,0x04,0x0b,0x00,0xd4,0x14,0x53,0x04,
+ 0x0b,0x00,0x52,0x04,0x0b,0x00,0x91,0x08,0x10,0x04,0x0b,0x00,0x0b,0x09,0x00,0x00,
+ 0x53,0x04,0x0b,0x00,0x92,0x08,0x11,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,
+ 0x02,0xff,0xff,0xcf,0x86,0xcf,0x06,0x02,0xff,0xff,0xd1,0x76,0xd0,0x09,0xcf,0x86,
+ 0xcf,0x06,0x02,0xff,0xff,0xcf,0x86,0x85,0xd4,0x07,0xcf,0x06,0x02,0xff,0xff,0xd3,
+ 0x07,0xcf,0x06,0x02,0xff,0xff,0xd2,0x07,0xcf,0x06,0x02,0xff,0xff,0xd1,0x07,0xcf,
+ 0x06,0x02,0xff,0xff,0xd0,0x18,0xcf,0x86,0x55,0x05,0x02,0xff,0xff,0x94,0x0d,0x93,
+ 0x09,0x12,0x05,0x02,0xff,0xff,0x00,0x00,0x00,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x24,
+ 0x94,0x20,0xd3,0x10,0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,
+ 0x00,0x00,0x92,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x0b,0x00,0x0b,0x00,
+ 0x0b,0x00,0x54,0x04,0x0b,0x00,0x53,0x04,0x0b,0x00,0x12,0x04,0x0b,0x00,0x00,0x00,
+ 0xd0,0x08,0xcf,0x86,0xcf,0x06,0x01,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x01,0x00,
+ 0xe4,0x9c,0x10,0xe3,0x16,0x08,0xd2,0x06,0xcf,0x06,0x01,0x00,0xe1,0x08,0x04,0xe0,
+ 0x04,0x02,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe8,0xb1,0x88,0x00,0x01,0xff,0xe6,0x9b,0xb4,0x00,0x10,0x08,0x01,
+ 0xff,0xe8,0xbb,0x8a,0x00,0x01,0xff,0xe8,0xb3,0x88,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0xe6,0xbb,0x91,0x00,0x01,0xff,0xe4,0xb8,0xb2,0x00,0x10,0x08,0x01,0xff,0xe5,
+ 0x8f,0xa5,0x00,0x01,0xff,0xe9,0xbe,0x9c,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0xe9,0xbe,0x9c,0x00,0x01,0xff,0xe5,0xa5,0x91,0x00,0x10,0x08,0x01,0xff,0xe9,
+ 0x87,0x91,0x00,0x01,0xff,0xe5,0x96,0x87,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe5,
+ 0xa5,0x88,0x00,0x01,0xff,0xe6,0x87,0xb6,0x00,0x10,0x08,0x01,0xff,0xe7,0x99,0xa9,
+ 0x00,0x01,0xff,0xe7,0xbe,0x85,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0xe8,0x98,0xbf,0x00,0x01,0xff,0xe8,0x9e,0xba,0x00,0x10,0x08,0x01,0xff,0xe8,
+ 0xa3,0xb8,0x00,0x01,0xff,0xe9,0x82,0x8f,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,
+ 0xa8,0x82,0x00,0x01,0xff,0xe6,0xb4,0x9b,0x00,0x10,0x08,0x01,0xff,0xe7,0x83,0x99,
+ 0x00,0x01,0xff,0xe7,0x8f,0x9e,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe8,
+ 0x90,0xbd,0x00,0x01,0xff,0xe9,0x85,0xaa,0x00,0x10,0x08,0x01,0xff,0xe9,0xa7,0xb1,
+ 0x00,0x01,0xff,0xe4,0xba,0x82,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe5,0x8d,0xb5,
+ 0x00,0x01,0xff,0xe6,0xac,0x84,0x00,0x10,0x08,0x01,0xff,0xe7,0x88,0x9b,0x00,0x01,
+ 0xff,0xe8,0x98,0xad,0x00,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0xe9,0xb8,0x9e,0x00,0x01,0xff,0xe5,0xb5,0x90,0x00,0x10,0x08,0x01,0xff,0xe6,
+ 0xbf,0xab,0x00,0x01,0xff,0xe8,0x97,0x8d,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe8,
+ 0xa5,0xa4,0x00,0x01,0xff,0xe6,0x8b,0x89,0x00,0x10,0x08,0x01,0xff,0xe8,0x87,0x98,
+ 0x00,0x01,0xff,0xe8,0xa0,0x9f,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe5,
+ 0xbb,0x8a,0x00,0x01,0xff,0xe6,0x9c,0x97,0x00,0x10,0x08,0x01,0xff,0xe6,0xb5,0xaa,
+ 0x00,0x01,0xff,0xe7,0x8b,0xbc,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe9,0x83,0x8e,
+ 0x00,0x01,0xff,0xe4,0xbe,0x86,0x00,0x10,0x08,0x01,0xff,0xe5,0x86,0xb7,0x00,0x01,
+ 0xff,0xe5,0x8b,0x9e,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,
+ 0x93,0x84,0x00,0x01,0xff,0xe6,0xab,0x93,0x00,0x10,0x08,0x01,0xff,0xe7,0x88,0x90,
+ 0x00,0x01,0xff,0xe7,0x9b,0xa7,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe8,0x80,0x81,
+ 0x00,0x01,0xff,0xe8,0x98,0x86,0x00,0x10,0x08,0x01,0xff,0xe8,0x99,0x9c,0x00,0x01,
+ 0xff,0xe8,0xb7,0xaf,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe9,0x9c,0xb2,
+ 0x00,0x01,0xff,0xe9,0xad,0xaf,0x00,0x10,0x08,0x01,0xff,0xe9,0xb7,0xba,0x00,0x01,
+ 0xff,0xe7,0xa2,0x8c,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe7,0xa5,0xbf,0x00,0x01,
+ 0xff,0xe7,0xb6,0xa0,0x00,0x10,0x08,0x01,0xff,0xe8,0x8f,0x89,0x00,0x01,0xff,0xe9,
+ 0x8c,0x84,0x00,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe9,0xb9,0xbf,0x00,0x01,0xff,0xe8,0xab,0x96,0x00,0x10,0x08,
+ 0x01,0xff,0xe5,0xa3,0x9f,0x00,0x01,0xff,0xe5,0xbc,0x84,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe7,0xb1,0xa0,0x00,0x01,0xff,0xe8,0x81,0xbe,0x00,0x10,0x08,0x01,0xff,
+ 0xe7,0x89,0xa2,0x00,0x01,0xff,0xe7,0xa3,0x8a,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe8,0xb3,0x82,0x00,0x01,0xff,0xe9,0x9b,0xb7,0x00,0x10,0x08,0x01,0xff,
+ 0xe5,0xa3,0x98,0x00,0x01,0xff,0xe5,0xb1,0xa2,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe6,0xa8,0x93,0x00,0x01,0xff,0xe6,0xb7,0x9a,0x00,0x10,0x08,0x01,0xff,0xe6,0xbc,
+ 0x8f,0x00,0x01,0xff,0xe7,0xb4,0xaf,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe7,0xb8,0xb7,0x00,0x01,0xff,0xe9,0x99,0x8b,0x00,0x10,0x08,0x01,0xff,
+ 0xe5,0x8b,0x92,0x00,0x01,0xff,0xe8,0x82,0x8b,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe5,0x87,0x9c,0x00,0x01,0xff,0xe5,0x87,0x8c,0x00,0x10,0x08,0x01,0xff,0xe7,0xa8,
+ 0x9c,0x00,0x01,0xff,0xe7,0xb6,0xbe,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe8,0x8f,0xb1,0x00,0x01,0xff,0xe9,0x99,0xb5,0x00,0x10,0x08,0x01,0xff,0xe8,0xae,
+ 0x80,0x00,0x01,0xff,0xe6,0x8b,0x8f,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,0xa8,
+ 0x82,0x00,0x01,0xff,0xe8,0xab,0xbe,0x00,0x10,0x08,0x01,0xff,0xe4,0xb8,0xb9,0x00,
+ 0x01,0xff,0xe5,0xaf,0xa7,0x00,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe6,0x80,0x92,0x00,0x01,0xff,0xe7,0x8e,0x87,0x00,0x10,0x08,0x01,0xff,
+ 0xe7,0x95,0xb0,0x00,0x01,0xff,0xe5,0x8c,0x97,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe7,0xa3,0xbb,0x00,0x01,0xff,0xe4,0xbe,0xbf,0x00,0x10,0x08,0x01,0xff,0xe5,0xbe,
+ 0xa9,0x00,0x01,0xff,0xe4,0xb8,0x8d,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe6,0xb3,0x8c,0x00,0x01,0xff,0xe6,0x95,0xb8,0x00,0x10,0x08,0x01,0xff,0xe7,0xb4,
+ 0xa2,0x00,0x01,0xff,0xe5,0x8f,0x83,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe5,0xa1,
+ 0x9e,0x00,0x01,0xff,0xe7,0x9c,0x81,0x00,0x10,0x08,0x01,0xff,0xe8,0x91,0x89,0x00,
+ 0x01,0xff,0xe8,0xaa,0xaa,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe6,0xae,0xba,0x00,0x01,0xff,0xe8,0xbe,0xb0,0x00,0x10,0x08,0x01,0xff,0xe6,0xb2,
+ 0x88,0x00,0x01,0xff,0xe6,0x8b,0xbe,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe8,0x8b,
+ 0xa5,0x00,0x01,0xff,0xe6,0x8e,0xa0,0x00,0x10,0x08,0x01,0xff,0xe7,0x95,0xa5,0x00,
+ 0x01,0xff,0xe4,0xba,0xae,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe5,0x85,
+ 0xa9,0x00,0x01,0xff,0xe5,0x87,0x89,0x00,0x10,0x08,0x01,0xff,0xe6,0xa2,0x81,0x00,
+ 0x01,0xff,0xe7,0xb3,0xa7,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe8,0x89,0xaf,0x00,
+ 0x01,0xff,0xe8,0xab,0x92,0x00,0x10,0x08,0x01,0xff,0xe9,0x87,0x8f,0x00,0x01,0xff,
+ 0xe5,0x8b,0xb5,0x00,0xe0,0x04,0x02,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,0x40,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe5,0x91,0x82,0x00,0x01,0xff,0xe5,0xa5,
+ 0xb3,0x00,0x10,0x08,0x01,0xff,0xe5,0xbb,0xac,0x00,0x01,0xff,0xe6,0x97,0x85,0x00,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,0xbf,0xbe,0x00,0x01,0xff,0xe7,0xa4,0xaa,0x00,
+ 0x10,0x08,0x01,0xff,0xe9,0x96,0xad,0x00,0x01,0xff,0xe9,0xa9,0xaa,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0xe9,0xba,0x97,0x00,0x01,0xff,0xe9,0xbb,0x8e,0x00,
+ 0x10,0x08,0x01,0xff,0xe5,0x8a,0x9b,0x00,0x01,0xff,0xe6,0x9b,0x86,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe6,0xad,0xb7,0x00,0x01,0xff,0xe8,0xbd,0xa2,0x00,0x10,0x08,
+ 0x01,0xff,0xe5,0xb9,0xb4,0x00,0x01,0xff,0xe6,0x86,0x90,0x00,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,0x88,0x80,0x00,0x01,0xff,0xe6,0x92,0x9a,0x00,
+ 0x10,0x08,0x01,0xff,0xe6,0xbc,0xa3,0x00,0x01,0xff,0xe7,0x85,0x89,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe7,0x92,0x89,0x00,0x01,0xff,0xe7,0xa7,0x8a,0x00,0x10,0x08,
+ 0x01,0xff,0xe7,0xb7,0xb4,0x00,0x01,0xff,0xe8,0x81,0xaf,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe8,0xbc,0xa6,0x00,0x01,0xff,0xe8,0x93,0xae,0x00,0x10,0x08,
+ 0x01,0xff,0xe9,0x80,0xa3,0x00,0x01,0xff,0xe9,0x8d,0x8a,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe5,0x88,0x97,0x00,0x01,0xff,0xe5,0x8a,0xa3,0x00,0x10,0x08,0x01,0xff,
+ 0xe5,0x92,0xbd,0x00,0x01,0xff,0xe7,0x83,0x88,0x00,0xd4,0x80,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0xe8,0xa3,0x82,0x00,0x01,0xff,0xe8,0xaa,0xaa,0x00,
+ 0x10,0x08,0x01,0xff,0xe5,0xbb,0x89,0x00,0x01,0xff,0xe5,0xbf,0xb5,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe6,0x8d,0xbb,0x00,0x01,0xff,0xe6,0xae,0xae,0x00,0x10,0x08,
+ 0x01,0xff,0xe7,0xb0,0xbe,0x00,0x01,0xff,0xe7,0x8d,0xb5,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe4,0xbb,0xa4,0x00,0x01,0xff,0xe5,0x9b,0xb9,0x00,0x10,0x08,
+ 0x01,0xff,0xe5,0xaf,0xa7,0x00,0x01,0xff,0xe5,0xb6,0xba,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe6,0x80,0x9c,0x00,0x01,0xff,0xe7,0x8e,0xb2,0x00,0x10,0x08,0x01,0xff,
+ 0xe7,0x91,0xa9,0x00,0x01,0xff,0xe7,0xbe,0x9a,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe8,0x81,0x86,0x00,0x01,0xff,0xe9,0x88,0xb4,0x00,0x10,0x08,
+ 0x01,0xff,0xe9,0x9b,0xb6,0x00,0x01,0xff,0xe9,0x9d,0x88,0x00,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe9,0xa0,0x98,0x00,0x01,0xff,0xe4,0xbe,0x8b,0x00,0x10,0x08,0x01,0xff,
+ 0xe7,0xa6,0xae,0x00,0x01,0xff,0xe9,0x86,0xb4,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe9,0x9a,0xb8,0x00,0x01,0xff,0xe6,0x83,0xa1,0x00,0x10,0x08,0x01,0xff,
+ 0xe4,0xba,0x86,0x00,0x01,0xff,0xe5,0x83,0x9a,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe5,0xaf,0xae,0x00,0x01,0xff,0xe5,0xb0,0xbf,0x00,0x10,0x08,0x01,0xff,0xe6,0x96,
+ 0x99,0x00,0x01,0xff,0xe6,0xa8,0x82,0x00,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe7,0x87,0x8e,0x00,0x01,0xff,0xe7,
+ 0x99,0x82,0x00,0x10,0x08,0x01,0xff,0xe8,0x93,0xbc,0x00,0x01,0xff,0xe9,0x81,0xbc,
+ 0x00,0xd1,0x10,0x10,0x08,0x01,0xff,0xe9,0xbe,0x8d,0x00,0x01,0xff,0xe6,0x9a,0x88,
+ 0x00,0x10,0x08,0x01,0xff,0xe9,0x98,0xae,0x00,0x01,0xff,0xe5,0x8a,0x89,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,0x9d,0xbb,0x00,0x01,0xff,0xe6,0x9f,0xb3,
+ 0x00,0x10,0x08,0x01,0xff,0xe6,0xb5,0x81,0x00,0x01,0xff,0xe6,0xba,0x9c,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe7,0x90,0x89,0x00,0x01,0xff,0xe7,0x95,0x99,0x00,0x10,
+ 0x08,0x01,0xff,0xe7,0xa1,0xab,0x00,0x01,0xff,0xe7,0xb4,0x90,0x00,0xd3,0x40,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe9,0xa1,0x9e,0x00,0x01,0xff,0xe5,0x85,0xad,
+ 0x00,0x10,0x08,0x01,0xff,0xe6,0x88,0xae,0x00,0x01,0xff,0xe9,0x99,0xb8,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe5,0x80,0xab,0x00,0x01,0xff,0xe5,0xb4,0x99,0x00,0x10,
+ 0x08,0x01,0xff,0xe6,0xb7,0xaa,0x00,0x01,0xff,0xe8,0xbc,0xaa,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe5,0xbe,0x8b,0x00,0x01,0xff,0xe6,0x85,0x84,0x00,0x10,
+ 0x08,0x01,0xff,0xe6,0xa0,0x97,0x00,0x01,0xff,0xe7,0x8e,0x87,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe9,0x9a,0x86,0x00,0x01,0xff,0xe5,0x88,0xa9,0x00,0x10,0x08,0x01,
+ 0xff,0xe5,0x90,0x8f,0x00,0x01,0xff,0xe5,0xb1,0xa5,0x00,0xd4,0x80,0xd3,0x40,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x01,0xff,0xe6,0x98,0x93,0x00,0x01,0xff,0xe6,0x9d,0x8e,
+ 0x00,0x10,0x08,0x01,0xff,0xe6,0xa2,0xa8,0x00,0x01,0xff,0xe6,0xb3,0xa5,0x00,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe7,0x90,0x86,0x00,0x01,0xff,0xe7,0x97,0xa2,0x00,0x10,
+ 0x08,0x01,0xff,0xe7,0xbd,0xb9,0x00,0x01,0xff,0xe8,0xa3,0x8f,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe8,0xa3,0xa1,0x00,0x01,0xff,0xe9,0x87,0x8c,0x00,0x10,
+ 0x08,0x01,0xff,0xe9,0x9b,0xa2,0x00,0x01,0xff,0xe5,0x8c,0xbf,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe6,0xba,0xba,0x00,0x01,0xff,0xe5,0x90,0x9d,0x00,0x10,0x08,0x01,
+ 0xff,0xe7,0x87,0x90,0x00,0x01,0xff,0xe7,0x92,0x98,0x00,0xd3,0x40,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x01,0xff,0xe8,0x97,0xba,0x00,0x01,0xff,0xe9,0x9a,0xa3,0x00,0x10,
+ 0x08,0x01,0xff,0xe9,0xb1,0x97,0x00,0x01,0xff,0xe9,0xba,0x9f,0x00,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe6,0x9e,0x97,0x00,0x01,0xff,0xe6,0xb7,0x8b,0x00,0x10,0x08,0x01,
+ 0xff,0xe8,0x87,0xa8,0x00,0x01,0xff,0xe7,0xab,0x8b,0x00,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x01,0xff,0xe7,0xac,0xa0,0x00,0x01,0xff,0xe7,0xb2,0x92,0x00,0x10,0x08,0x01,
+ 0xff,0xe7,0x8b,0x80,0x00,0x01,0xff,0xe7,0x82,0x99,0x00,0xd1,0x10,0x10,0x08,0x01,
+ 0xff,0xe8,0xad,0x98,0x00,0x01,0xff,0xe4,0xbb,0x80,0x00,0x10,0x08,0x01,0xff,0xe8,
+ 0x8c,0xb6,0x00,0x01,0xff,0xe5,0x88,0xba,0x00,0xe2,0xad,0x06,0xe1,0xc4,0x03,0xe0,
+ 0xcb,0x01,0xcf,0x86,0xd5,0xe4,0xd4,0x74,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x01,0xff,0xe5,0x88,0x87,0x00,0x01,0xff,0xe5,0xba,0xa6,0x00,0x10,0x08,0x01,0xff,
+ 0xe6,0x8b,0x93,0x00,0x01,0xff,0xe7,0xb3,0x96,0x00,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe5,0xae,0x85,0x00,0x01,0xff,0xe6,0xb4,0x9e,0x00,0x10,0x08,0x01,0xff,0xe6,0x9a,
+ 0xb4,0x00,0x01,0xff,0xe8,0xbc,0xbb,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x01,0xff,
+ 0xe8,0xa1,0x8c,0x00,0x01,0xff,0xe9,0x99,0x8d,0x00,0x10,0x08,0x01,0xff,0xe8,0xa6,
+ 0x8b,0x00,0x01,0xff,0xe5,0xbb,0x93,0x00,0x91,0x10,0x10,0x08,0x01,0xff,0xe5,0x85,
+ 0x80,0x00,0x01,0xff,0xe5,0x97,0x80,0x00,0x01,0x00,0xd3,0x34,0xd2,0x18,0xd1,0x0c,
+ 0x10,0x08,0x01,0xff,0xe5,0xa1,0x9a,0x00,0x01,0x00,0x10,0x08,0x01,0xff,0xe6,0x99,
+ 0xb4,0x00,0x01,0x00,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0xe5,0x87,0x9e,0x00,
+ 0x10,0x08,0x01,0xff,0xe7,0x8c,0xaa,0x00,0x01,0xff,0xe7,0x9b,0x8a,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x01,0xff,0xe7,0xa4,0xbc,0x00,0x01,0xff,0xe7,0xa5,0x9e,0x00,
+ 0x10,0x08,0x01,0xff,0xe7,0xa5,0xa5,0x00,0x01,0xff,0xe7,0xa6,0x8f,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe9,0x9d,0x96,0x00,0x01,0xff,0xe7,0xb2,0xbe,0x00,0x10,0x08,
+ 0x01,0xff,0xe7,0xbe,0xbd,0x00,0x01,0x00,0xd4,0x64,0xd3,0x30,0xd2,0x18,0xd1,0x0c,
+ 0x10,0x08,0x01,0xff,0xe8,0x98,0x92,0x00,0x01,0x00,0x10,0x08,0x01,0xff,0xe8,0xab,
+ 0xb8,0x00,0x01,0x00,0xd1,0x0c,0x10,0x04,0x01,0x00,0x01,0xff,0xe9,0x80,0xb8,0x00,
+ 0x10,0x08,0x01,0xff,0xe9,0x83,0xbd,0x00,0x01,0x00,0xd2,0x14,0x51,0x04,0x01,0x00,
+ 0x10,0x08,0x01,0xff,0xe9,0xa3,0xaf,0x00,0x01,0xff,0xe9,0xa3,0xbc,0x00,0xd1,0x10,
+ 0x10,0x08,0x01,0xff,0xe9,0xa4,0xa8,0x00,0x01,0xff,0xe9,0xb6,0xb4,0x00,0x10,0x08,
+ 0x0d,0xff,0xe9,0x83,0x9e,0x00,0x0d,0xff,0xe9,0x9a,0xb7,0x00,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x06,0xff,0xe4,0xbe,0xae,0x00,0x06,0xff,0xe5,0x83,0xa7,0x00,
+ 0x10,0x08,0x06,0xff,0xe5,0x85,0x8d,0x00,0x06,0xff,0xe5,0x8b,0x89,0x00,0xd1,0x10,
+ 0x10,0x08,0x06,0xff,0xe5,0x8b,0xa4,0x00,0x06,0xff,0xe5,0x8d,0x91,0x00,0x10,0x08,
+ 0x06,0xff,0xe5,0x96,0x9d,0x00,0x06,0xff,0xe5,0x98,0x86,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x06,0xff,0xe5,0x99,0xa8,0x00,0x06,0xff,0xe5,0xa1,0x80,0x00,0x10,0x08,
+ 0x06,0xff,0xe5,0xa2,0xa8,0x00,0x06,0xff,0xe5,0xb1,0xa4,0x00,0xd1,0x10,0x10,0x08,
+ 0x06,0xff,0xe5,0xb1,0xae,0x00,0x06,0xff,0xe6,0x82,0x94,0x00,0x10,0x08,0x06,0xff,
+ 0xe6,0x85,0xa8,0x00,0x06,0xff,0xe6,0x86,0x8e,0x00,0xcf,0x86,0xe5,0x01,0x01,0xd4,
+ 0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x06,0xff,0xe6,0x87,0xb2,0x00,0x06,
+ 0xff,0xe6,0x95,0x8f,0x00,0x10,0x08,0x06,0xff,0xe6,0x97,0xa2,0x00,0x06,0xff,0xe6,
+ 0x9a,0x91,0x00,0xd1,0x10,0x10,0x08,0x06,0xff,0xe6,0xa2,0x85,0x00,0x06,0xff,0xe6,
+ 0xb5,0xb7,0x00,0x10,0x08,0x06,0xff,0xe6,0xb8,0x9a,0x00,0x06,0xff,0xe6,0xbc,0xa2,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x06,0xff,0xe7,0x85,0xae,0x00,0x06,0xff,0xe7,
+ 0x88,0xab,0x00,0x10,0x08,0x06,0xff,0xe7,0x90,0xa2,0x00,0x06,0xff,0xe7,0xa2,0x91,
+ 0x00,0xd1,0x10,0x10,0x08,0x06,0xff,0xe7,0xa4,0xbe,0x00,0x06,0xff,0xe7,0xa5,0x89,
+ 0x00,0x10,0x08,0x06,0xff,0xe7,0xa5,0x88,0x00,0x06,0xff,0xe7,0xa5,0x90,0x00,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x06,0xff,0xe7,0xa5,0x96,0x00,0x06,0xff,0xe7,
+ 0xa5,0x9d,0x00,0x10,0x08,0x06,0xff,0xe7,0xa6,0x8d,0x00,0x06,0xff,0xe7,0xa6,0x8e,
+ 0x00,0xd1,0x10,0x10,0x08,0x06,0xff,0xe7,0xa9,0x80,0x00,0x06,0xff,0xe7,0xaa,0x81,
+ 0x00,0x10,0x08,0x06,0xff,0xe7,0xaf,0x80,0x00,0x06,0xff,0xe7,0xb7,0xb4,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x06,0xff,0xe7,0xb8,0x89,0x00,0x06,0xff,0xe7,0xb9,0x81,
+ 0x00,0x10,0x08,0x06,0xff,0xe7,0xbd,0xb2,0x00,0x06,0xff,0xe8,0x80,0x85,0x00,0xd1,
+ 0x10,0x10,0x08,0x06,0xff,0xe8,0x87,0xad,0x00,0x06,0xff,0xe8,0x89,0xb9,0x00,0x10,
+ 0x08,0x06,0xff,0xe8,0x89,0xb9,0x00,0x06,0xff,0xe8,0x91,0x97,0x00,0xd4,0x75,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x06,0xff,0xe8,0xa4,0x90,0x00,0x06,0xff,0xe8,
+ 0xa6,0x96,0x00,0x10,0x08,0x06,0xff,0xe8,0xac,0x81,0x00,0x06,0xff,0xe8,0xac,0xb9,
+ 0x00,0xd1,0x10,0x10,0x08,0x06,0xff,0xe8,0xb3,0x93,0x00,0x06,0xff,0xe8,0xb4,0x88,
+ 0x00,0x10,0x08,0x06,0xff,0xe8,0xbe,0xb6,0x00,0x06,0xff,0xe9,0x80,0xb8,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x06,0xff,0xe9,0x9b,0xa3,0x00,0x06,0xff,0xe9,0x9f,0xbf,
+ 0x00,0x10,0x08,0x06,0xff,0xe9,0xa0,0xbb,0x00,0x0b,0xff,0xe6,0x81,0xb5,0x00,0x91,
+ 0x11,0x10,0x09,0x0b,0xff,0xf0,0xa4,0x8b,0xae,0x00,0x0b,0xff,0xe8,0x88,0x98,0x00,
+ 0x00,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,0xe4,0xb8,0xa6,0x00,
+ 0x08,0xff,0xe5,0x86,0xb5,0x00,0x10,0x08,0x08,0xff,0xe5,0x85,0xa8,0x00,0x08,0xff,
+ 0xe4,0xbe,0x80,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe5,0x85,0x85,0x00,0x08,0xff,
+ 0xe5,0x86,0x80,0x00,0x10,0x08,0x08,0xff,0xe5,0x8b,0x87,0x00,0x08,0xff,0xe5,0x8b,
+ 0xba,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,0xe5,0x96,0x9d,0x00,0x08,0xff,
+ 0xe5,0x95,0x95,0x00,0x10,0x08,0x08,0xff,0xe5,0x96,0x99,0x00,0x08,0xff,0xe5,0x97,
+ 0xa2,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe5,0xa1,0x9a,0x00,0x08,0xff,0xe5,0xa2,
+ 0xb3,0x00,0x10,0x08,0x08,0xff,0xe5,0xa5,0x84,0x00,0x08,0xff,0xe5,0xa5,0x94,0x00,
+ 0xe0,0x04,0x02,0xcf,0x86,0xe5,0x01,0x01,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x08,0xff,0xe5,0xa9,0xa2,0x00,0x08,0xff,0xe5,0xac,0xa8,0x00,0x10,0x08,
+ 0x08,0xff,0xe5,0xbb,0x92,0x00,0x08,0xff,0xe5,0xbb,0x99,0x00,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe5,0xbd,0xa9,0x00,0x08,0xff,0xe5,0xbe,0xad,0x00,0x10,0x08,0x08,0xff,
+ 0xe6,0x83,0x98,0x00,0x08,0xff,0xe6,0x85,0x8e,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe6,0x84,0x88,0x00,0x08,0xff,0xe6,0x86,0x8e,0x00,0x10,0x08,0x08,0xff,
+ 0xe6,0x85,0xa0,0x00,0x08,0xff,0xe6,0x87,0xb2,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe6,0x88,0xb4,0x00,0x08,0xff,0xe6,0x8f,0x84,0x00,0x10,0x08,0x08,0xff,0xe6,0x90,
+ 0x9c,0x00,0x08,0xff,0xe6,0x91,0x92,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe6,0x95,0x96,0x00,0x08,0xff,0xe6,0x99,0xb4,0x00,0x10,0x08,0x08,0xff,
+ 0xe6,0x9c,0x97,0x00,0x08,0xff,0xe6,0x9c,0x9b,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe6,0x9d,0x96,0x00,0x08,0xff,0xe6,0xad,0xb9,0x00,0x10,0x08,0x08,0xff,0xe6,0xae,
+ 0xba,0x00,0x08,0xff,0xe6,0xb5,0x81,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe6,0xbb,0x9b,0x00,0x08,0xff,0xe6,0xbb,0x8b,0x00,0x10,0x08,0x08,0xff,0xe6,0xbc,
+ 0xa2,0x00,0x08,0xff,0xe7,0x80,0x9e,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe7,0x85,
+ 0xae,0x00,0x08,0xff,0xe7,0x9e,0xa7,0x00,0x10,0x08,0x08,0xff,0xe7,0x88,0xb5,0x00,
+ 0x08,0xff,0xe7,0x8a,0xaf,0x00,0xd4,0x80,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe7,0x8c,0xaa,0x00,0x08,0xff,0xe7,0x91,0xb1,0x00,0x10,0x08,0x08,0xff,
+ 0xe7,0x94,0x86,0x00,0x08,0xff,0xe7,0x94,0xbb,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe7,0x98,0x9d,0x00,0x08,0xff,0xe7,0x98,0x9f,0x00,0x10,0x08,0x08,0xff,0xe7,0x9b,
+ 0x8a,0x00,0x08,0xff,0xe7,0x9b,0x9b,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe7,0x9b,0xb4,0x00,0x08,0xff,0xe7,0x9d,0x8a,0x00,0x10,0x08,0x08,0xff,0xe7,0x9d,
+ 0x80,0x00,0x08,0xff,0xe7,0xa3,0x8c,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe7,0xaa,
+ 0xb1,0x00,0x08,0xff,0xe7,0xaf,0x80,0x00,0x10,0x08,0x08,0xff,0xe7,0xb1,0xbb,0x00,
+ 0x08,0xff,0xe7,0xb5,0x9b,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe7,0xb7,0xb4,0x00,0x08,0xff,0xe7,0xbc,0xbe,0x00,0x10,0x08,0x08,0xff,0xe8,0x80,
+ 0x85,0x00,0x08,0xff,0xe8,0x8d,0x92,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe8,0x8f,
+ 0xaf,0x00,0x08,0xff,0xe8,0x9d,0xb9,0x00,0x10,0x08,0x08,0xff,0xe8,0xa5,0x81,0x00,
+ 0x08,0xff,0xe8,0xa6,0x86,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x08,0xff,0xe8,0xa6,
+ 0x96,0x00,0x08,0xff,0xe8,0xaa,0xbf,0x00,0x10,0x08,0x08,0xff,0xe8,0xab,0xb8,0x00,
+ 0x08,0xff,0xe8,0xab,0x8b,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,0xe8,0xac,0x81,0x00,
+ 0x08,0xff,0xe8,0xab,0xbe,0x00,0x10,0x08,0x08,0xff,0xe8,0xab,0xad,0x00,0x08,0xff,
+ 0xe8,0xac,0xb9,0x00,0xcf,0x86,0x95,0xde,0xd4,0x81,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x08,0xff,0xe8,0xae,0x8a,0x00,0x08,0xff,0xe8,0xb4,0x88,0x00,0x10,0x08,
+ 0x08,0xff,0xe8,0xbc,0xb8,0x00,0x08,0xff,0xe9,0x81,0xb2,0x00,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe9,0x86,0x99,0x00,0x08,0xff,0xe9,0x89,0xb6,0x00,0x10,0x08,0x08,0xff,
+ 0xe9,0x99,0xbc,0x00,0x08,0xff,0xe9,0x9b,0xa3,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x08,0xff,0xe9,0x9d,0x96,0x00,0x08,0xff,0xe9,0x9f,0x9b,0x00,0x10,0x08,0x08,0xff,
+ 0xe9,0x9f,0xbf,0x00,0x08,0xff,0xe9,0xa0,0x8b,0x00,0xd1,0x10,0x10,0x08,0x08,0xff,
+ 0xe9,0xa0,0xbb,0x00,0x08,0xff,0xe9,0xac,0x92,0x00,0x10,0x08,0x08,0xff,0xe9,0xbe,
+ 0x9c,0x00,0x08,0xff,0xf0,0xa2,0xa1,0x8a,0x00,0xd3,0x45,0xd2,0x22,0xd1,0x12,0x10,
+ 0x09,0x08,0xff,0xf0,0xa2,0xa1,0x84,0x00,0x08,0xff,0xf0,0xa3,0x8f,0x95,0x00,0x10,
+ 0x08,0x08,0xff,0xe3,0xae,0x9d,0x00,0x08,0xff,0xe4,0x80,0x98,0x00,0xd1,0x11,0x10,
+ 0x08,0x08,0xff,0xe4,0x80,0xb9,0x00,0x08,0xff,0xf0,0xa5,0x89,0x89,0x00,0x10,0x09,
+ 0x08,0xff,0xf0,0xa5,0xb3,0x90,0x00,0x08,0xff,0xf0,0xa7,0xbb,0x93,0x00,0x92,0x14,
+ 0x91,0x10,0x10,0x08,0x08,0xff,0xe9,0xbd,0x83,0x00,0x08,0xff,0xe9,0xbe,0x8e,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xe1,0x94,0x01,0xe0,0x08,0x01,0xcf,0x86,0xd5,0x42,
+ 0xd4,0x14,0x93,0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,
+ 0x00,0x00,0x00,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,
+ 0x01,0x00,0x01,0x00,0x52,0x04,0x00,0x00,0xd1,0x0d,0x10,0x04,0x00,0x00,0x04,0xff,
+ 0xd7,0x99,0xd6,0xb4,0x00,0x10,0x04,0x01,0x1a,0x01,0xff,0xd7,0xb2,0xd6,0xb7,0x00,
+ 0xd4,0x42,0x53,0x04,0x01,0x00,0xd2,0x16,0x51,0x04,0x01,0x00,0x10,0x09,0x01,0xff,
+ 0xd7,0xa9,0xd7,0x81,0x00,0x01,0xff,0xd7,0xa9,0xd7,0x82,0x00,0xd1,0x16,0x10,0x0b,
+ 0x01,0xff,0xd7,0xa9,0xd6,0xbc,0xd7,0x81,0x00,0x01,0xff,0xd7,0xa9,0xd6,0xbc,0xd7,
+ 0x82,0x00,0x10,0x09,0x01,0xff,0xd7,0x90,0xd6,0xb7,0x00,0x01,0xff,0xd7,0x90,0xd6,
+ 0xb8,0x00,0xd3,0x43,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xd7,0x90,0xd6,0xbc,
+ 0x00,0x01,0xff,0xd7,0x91,0xd6,0xbc,0x00,0x10,0x09,0x01,0xff,0xd7,0x92,0xd6,0xbc,
+ 0x00,0x01,0xff,0xd7,0x93,0xd6,0xbc,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xd7,0x94,
+ 0xd6,0xbc,0x00,0x01,0xff,0xd7,0x95,0xd6,0xbc,0x00,0x10,0x09,0x01,0xff,0xd7,0x96,
+ 0xd6,0xbc,0x00,0x00,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xd7,0x98,0xd6,
+ 0xbc,0x00,0x01,0xff,0xd7,0x99,0xd6,0xbc,0x00,0x10,0x09,0x01,0xff,0xd7,0x9a,0xd6,
+ 0xbc,0x00,0x01,0xff,0xd7,0x9b,0xd6,0xbc,0x00,0xd1,0x0d,0x10,0x09,0x01,0xff,0xd7,
+ 0x9c,0xd6,0xbc,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xd7,0x9e,0xd6,0xbc,0x00,0x00,
+ 0x00,0xcf,0x86,0x95,0x85,0x94,0x81,0xd3,0x3e,0xd2,0x1f,0xd1,0x12,0x10,0x09,0x01,
+ 0xff,0xd7,0xa0,0xd6,0xbc,0x00,0x01,0xff,0xd7,0xa1,0xd6,0xbc,0x00,0x10,0x04,0x00,
+ 0x00,0x01,0xff,0xd7,0xa3,0xd6,0xbc,0x00,0xd1,0x0d,0x10,0x09,0x01,0xff,0xd7,0xa4,
+ 0xd6,0xbc,0x00,0x00,0x00,0x10,0x09,0x01,0xff,0xd7,0xa6,0xd6,0xbc,0x00,0x01,0xff,
+ 0xd7,0xa7,0xd6,0xbc,0x00,0xd2,0x24,0xd1,0x12,0x10,0x09,0x01,0xff,0xd7,0xa8,0xd6,
+ 0xbc,0x00,0x01,0xff,0xd7,0xa9,0xd6,0xbc,0x00,0x10,0x09,0x01,0xff,0xd7,0xaa,0xd6,
+ 0xbc,0x00,0x01,0xff,0xd7,0x95,0xd6,0xb9,0x00,0xd1,0x12,0x10,0x09,0x01,0xff,0xd7,
+ 0x91,0xd6,0xbf,0x00,0x01,0xff,0xd7,0x9b,0xd6,0xbf,0x00,0x10,0x09,0x01,0xff,0xd7,
+ 0xa4,0xd6,0xbf,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd0,0x1a,0xcf,0x86,0x55,0x04,
+ 0x01,0x00,0x54,0x04,0x01,0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x01,0x00,0x0c,0x00,
+ 0x0c,0x00,0x0c,0x00,0xcf,0x86,0x95,0x24,0xd4,0x10,0x93,0x0c,0x92,0x08,0x11,0x04,
+ 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,0x00,0x00,
+ 0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xd3,0x5a,0xd2,0x06,
+ 0xcf,0x06,0x01,0x00,0xd1,0x14,0xd0,0x06,0xcf,0x06,0x01,0x00,0xcf,0x86,0x95,0x08,
+ 0x14,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd0,0x1a,0xcf,0x86,0x95,0x14,0x54,0x04,
+ 0x01,0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0xcf,0x86,0xd5,0x0c,0x94,0x08,0x13,0x04,0x01,0x00,0x00,0x00,0x05,0x00,
+ 0x54,0x04,0x05,0x00,0x53,0x04,0x01,0x00,0x52,0x04,0x01,0x00,0x91,0x08,0x10,0x04,
+ 0x06,0x00,0x07,0x00,0x00,0x00,0xd2,0xce,0xd1,0xa5,0xd0,0x37,0xcf,0x86,0xd5,0x15,
+ 0x54,0x05,0x06,0xff,0x00,0x53,0x04,0x08,0x00,0x92,0x08,0x11,0x04,0x08,0x00,0x00,
+ 0x00,0x00,0x00,0x94,0x1c,0xd3,0x10,0x52,0x04,0x01,0xe6,0x51,0x04,0x0a,0xe6,0x10,
+ 0x04,0x0a,0xe6,0x10,0xdc,0x52,0x04,0x10,0xdc,0x11,0x04,0x10,0xdc,0x11,0xe6,0x01,
+ 0x00,0xcf,0x86,0xd5,0x38,0xd4,0x24,0xd3,0x14,0x52,0x04,0x01,0x00,0xd1,0x08,0x10,
+ 0x04,0x01,0x00,0x06,0x00,0x10,0x04,0x06,0x00,0x07,0x00,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x07,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,0x01,
+ 0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0xd4,0x18,0xd3,0x10,0x52,
+ 0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x12,0x04,0x01,
+ 0x00,0x00,0x00,0x93,0x18,0xd2,0x0c,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x06,
+ 0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0xd0,0x06,0xcf,
+ 0x06,0x01,0x00,0xcf,0x86,0x55,0x04,0x01,0x00,0x54,0x04,0x01,0x00,0x53,0x04,0x01,
+ 0x00,0x52,0x04,0x01,0x00,0xd1,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x10,0x04,0x00,
+ 0x00,0x01,0xff,0x00,0xd1,0x50,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x94,0x14,0x93,0x10,
+ 0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0x01,0x00,0xcf,0x86,0xd5,0x18,0x54,0x04,0x01,0x00,0x53,0x04,0x01,0x00,
+ 0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x06,0x00,0x94,0x14,
+ 0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x06,0x00,0x01,0x00,0x01,0x00,0x01,0x00,
+ 0x01,0x00,0x01,0x00,0xd0,0x2f,0xcf,0x86,0x55,0x04,0x01,0x00,0xd4,0x15,0x93,0x11,
+ 0x92,0x0d,0x91,0x09,0x10,0x05,0x01,0xff,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,
+ 0x00,0x53,0x04,0x01,0x00,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,
+ 0x00,0x00,0x00,0xcf,0x86,0xd5,0x38,0xd4,0x18,0xd3,0x0c,0x92,0x08,0x11,0x04,0x00,
+ 0x00,0x01,0x00,0x01,0x00,0x92,0x08,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd3,
+ 0x0c,0x92,0x08,0x11,0x04,0x00,0x00,0x01,0x00,0x01,0x00,0xd2,0x08,0x11,0x04,0x00,
+ 0x00,0x01,0x00,0x91,0x08,0x10,0x04,0x01,0x00,0x00,0x00,0x00,0x00,0xd4,0x20,0xd3,
+ 0x10,0x52,0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x52,
+ 0x04,0x01,0x00,0x51,0x04,0x01,0x00,0x10,0x04,0x01,0x00,0x00,0x00,0x53,0x05,0x00,
+ 0xff,0x00,0xd2,0x0d,0x91,0x09,0x10,0x05,0x00,0xff,0x00,0x04,0x00,0x04,0x00,0x91,
+ 0x08,0x10,0x04,0x03,0x00,0x01,0x00,0x01,0x00,0x83,0xe2,0x46,0x3e,0xe1,0x1f,0x3b,
+ 0xe0,0x9c,0x39,0xcf,0x86,0xe5,0x40,0x26,0xc4,0xe3,0x16,0x14,0xe2,0xef,0x11,0xe1,
+ 0xd0,0x10,0xe0,0x60,0x07,0xcf,0x86,0xe5,0x53,0x03,0xe4,0x4c,0x02,0xe3,0x3d,0x01,
+ 0xd2,0x94,0xd1,0x70,0xd0,0x4a,0xcf,0x86,0xd5,0x18,0x94,0x14,0x53,0x04,0x07,0x00,
+ 0x52,0x04,0x07,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,
+ 0xd4,0x14,0x93,0x10,0x52,0x04,0x07,0x00,0x51,0x04,0x07,0x00,0x10,0x04,0x07,0x00,
+ 0x00,0x00,0x07,0x00,0x53,0x04,0x07,0x00,0xd2,0x0c,0x51,0x04,0x07,0x00,0x10,0x04,
+ 0x07,0x00,0x00,0x00,0x51,0x04,0x07,0x00,0x10,0x04,0x00,0x00,0x07,0x00,0xcf,0x86,
+ 0x95,0x20,0xd4,0x10,0x53,0x04,0x07,0x00,0x52,0x04,0x07,0x00,0x11,0x04,0x07,0x00,
+ 0x00,0x00,0x53,0x04,0x07,0x00,0x52,0x04,0x07,0x00,0x11,0x04,0x07,0x00,0x00,0x00,
+ 0x00,0x00,0xd0,0x06,0xcf,0x06,0x07,0x00,0xcf,0x86,0x55,0x04,0x07,0x00,0x54,0x04,
+ 0x07,0x00,0x53,0x04,0x07,0x00,0x92,0x0c,0x51,0x04,0x07,0x00,0x10,0x04,0x07,0x00,
+ 0x00,0x00,0x00,0x00,0xd1,0x40,0xd0,0x3a,0xcf,0x86,0xd5,0x20,0x94,0x1c,0x93,0x18,
+ 0xd2,0x0c,0x51,0x04,0x07,0x00,0x10,0x04,0x07,0x00,0x00,0x00,0x51,0x04,0x00,0x00,
+ 0x10,0x04,0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x54,0x04,0x07,0x00,0x93,0x10,
+ 0x52,0x04,0x07,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x07,0x00,0x07,0x00,
+ 0xcf,0x06,0x08,0x00,0xd0,0x46,0xcf,0x86,0xd5,0x2c,0xd4,0x20,0x53,0x04,0x08,0x00,
+ 0xd2,0x0c,0x51,0x04,0x08,0x00,0x10,0x04,0x08,0x00,0x10,0x00,0xd1,0x08,0x10,0x04,
+ 0x10,0x00,0x12,0x00,0x10,0x04,0x12,0x00,0x00,0x00,0x53,0x04,0x0a,0x00,0x12,0x04,
+ 0x0a,0x00,0x00,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x10,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,0x08,0x14,0x04,
+ 0x00,0x00,0x0a,0x00,0x54,0x04,0x0a,0x00,0x53,0x04,0x0a,0x00,0x52,0x04,0x0a,0x00,
+ 0x91,0x08,0x10,0x04,0x0a,0x00,0x0a,0xdc,0x00,0x00,0xd2,0x5e,0xd1,0x06,0xcf,0x06,
+ 0x00,0x00,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,0x0a,0x00,0x53,0x04,0x0a,0x00,
+ 0x52,0x04,0x0a,0x00,0x91,0x08,0x10,0x04,0x0a,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,
+ 0xcf,0x86,0xd5,0x18,0x54,0x04,0x0a,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd4,0x14,0x93,0x10,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x10,0xdc,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x53,0x04,
+ 0x10,0x00,0x12,0x04,0x10,0x00,0x00,0x00,0xd1,0x70,0xd0,0x36,0xcf,0x86,0xd5,0x18,
+ 0x54,0x04,0x05,0x00,0x53,0x04,0x05,0x00,0x52,0x04,0x05,0x00,0x51,0x04,0x05,0x00,
+ 0x10,0x04,0x05,0x00,0x10,0x00,0x94,0x18,0xd3,0x08,0x12,0x04,0x05,0x00,0x00,0x00,
+ 0x52,0x04,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x13,0x00,0x13,0x00,0x05,0x00,
+ 0xcf,0x86,0xd5,0x18,0x94,0x14,0x53,0x04,0x05,0x00,0x92,0x0c,0x51,0x04,0x05,0x00,
+ 0x10,0x04,0x05,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x54,0x04,0x10,0x00,0xd3,0x0c,
+ 0x52,0x04,0x10,0x00,0x11,0x04,0x10,0x00,0x10,0xe6,0x92,0x0c,0x51,0x04,0x10,0xe6,
+ 0x10,0x04,0x10,0xe6,0x00,0x00,0x00,0x00,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,
+ 0x07,0x00,0x53,0x04,0x07,0x00,0x52,0x04,0x07,0x00,0x51,0x04,0x07,0x00,0x10,0x04,
+ 0x00,0x00,0x07,0x00,0x08,0x00,0xcf,0x86,0x95,0x1c,0xd4,0x0c,0x93,0x08,0x12,0x04,
+ 0x08,0x00,0x00,0x00,0x08,0x00,0x93,0x0c,0x52,0x04,0x08,0x00,0x11,0x04,0x08,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xd3,0xba,0xd2,0x80,0xd1,0x34,0xd0,0x1a,0xcf,0x86,
+ 0x55,0x04,0x05,0x00,0x94,0x10,0x93,0x0c,0x52,0x04,0x05,0x00,0x11,0x04,0x05,0x00,
+ 0x07,0x00,0x05,0x00,0x05,0x00,0xcf,0x86,0x95,0x14,0x94,0x10,0x53,0x04,0x05,0x00,
+ 0x52,0x04,0x05,0x00,0x11,0x04,0x05,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0xd0,0x2a,
+ 0xcf,0x86,0xd5,0x14,0x54,0x04,0x07,0x00,0x53,0x04,0x07,0x00,0x52,0x04,0x07,0x00,
+ 0x11,0x04,0x07,0x00,0x00,0x00,0x94,0x10,0x53,0x04,0x07,0x00,0x92,0x08,0x11,0x04,
+ 0x07,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0xcf,0x86,0xd5,0x10,0x54,0x04,0x12,0x00,
+ 0x93,0x08,0x12,0x04,0x12,0x00,0x00,0x00,0x12,0x00,0x54,0x04,0x12,0x00,0x53,0x04,
+ 0x12,0x00,0x12,0x04,0x12,0x00,0x00,0x00,0xd1,0x34,0xd0,0x12,0xcf,0x86,0x55,0x04,
+ 0x10,0x00,0x94,0x08,0x13,0x04,0x10,0x00,0x00,0x00,0x10,0x00,0xcf,0x86,0x55,0x04,
+ 0x10,0x00,0x94,0x18,0xd3,0x08,0x12,0x04,0x10,0x00,0x00,0x00,0x52,0x04,0x00,0x00,
+ 0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,
+ 0xd2,0x06,0xcf,0x06,0x10,0x00,0xd1,0x40,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x10,0x00,
+ 0x54,0x04,0x10,0x00,0x93,0x10,0x52,0x04,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,
+ 0x10,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,0x14,0x54,0x04,0x10,0x00,0x93,0x0c,
+ 0x52,0x04,0x10,0x00,0x11,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x94,0x08,0x13,0x04,
+ 0x10,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xe4,0xce,0x02,0xe3,0x45,0x01,
+ 0xd2,0xd0,0xd1,0x70,0xd0,0x52,0xcf,0x86,0xd5,0x20,0x94,0x1c,0xd3,0x0c,0x52,0x04,
+ 0x07,0x00,0x11,0x04,0x07,0x00,0x00,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x07,0x00,
+ 0x00,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x54,0x04,0x07,0x00,0xd3,0x10,0x52,0x04,
+ 0x07,0x00,0x51,0x04,0x07,0x00,0x10,0x04,0x00,0x00,0x07,0x00,0xd2,0x0c,0x91,0x08,
+ 0x10,0x04,0x07,0x00,0x00,0x00,0x00,0x00,0xd1,0x08,0x10,0x04,0x07,0x00,0x00,0x00,
+ 0x10,0x04,0x00,0x00,0x07,0x00,0xcf,0x86,0x95,0x18,0x54,0x04,0x0b,0x00,0x93,0x10,
+ 0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x00,0x00,0x0b,0x00,0x0b,0x00,
+ 0x10,0x00,0xd0,0x32,0xcf,0x86,0xd5,0x18,0x54,0x04,0x10,0x00,0x53,0x04,0x10,0x00,
+ 0x52,0x04,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x00,0x00,0x94,0x14,
+ 0x93,0x10,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,
+ 0x10,0x00,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x11,0x00,0xd3,0x14,
+ 0xd2,0x0c,0x51,0x04,0x11,0x00,0x10,0x04,0x11,0x00,0x00,0x00,0x11,0x04,0x11,0x00,
+ 0x00,0x00,0x92,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x11,0x00,0x11,0x00,
+ 0xd1,0x40,0xd0,0x3a,0xcf,0x86,0xd5,0x1c,0x54,0x04,0x09,0x00,0x53,0x04,0x09,0x00,
+ 0xd2,0x08,0x11,0x04,0x09,0x00,0x0b,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,
+ 0x09,0x00,0x54,0x04,0x0a,0x00,0x53,0x04,0x0a,0x00,0xd2,0x08,0x11,0x04,0x0a,0x00,
+ 0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x0a,0x00,0xcf,0x06,0x00,0x00,
+ 0xd0,0x1a,0xcf,0x86,0x55,0x04,0x0d,0x00,0x54,0x04,0x0d,0x00,0x53,0x04,0x0d,0x00,
+ 0x52,0x04,0x00,0x00,0x11,0x04,0x11,0x00,0x0d,0x00,0xcf,0x86,0x95,0x14,0x54,0x04,
+ 0x11,0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x00,0x00,0x11,0x00,0x11,0x00,0x11,0x00,
+ 0x11,0x00,0xd2,0xec,0xd1,0xa4,0xd0,0x76,0xcf,0x86,0xd5,0x48,0xd4,0x28,0xd3,0x14,
+ 0x52,0x04,0x08,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,0x08,0x00,0x10,0x04,0x08,0x00,
+ 0x00,0x00,0x52,0x04,0x00,0x00,0xd1,0x08,0x10,0x04,0x08,0x00,0x08,0xdc,0x10,0x04,
+ 0x08,0x00,0x08,0xe6,0xd3,0x10,0x52,0x04,0x08,0x00,0x91,0x08,0x10,0x04,0x00,0x00,
+ 0x08,0x00,0x08,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x08,0x00,0x08,0x00,
+ 0x08,0x00,0x54,0x04,0x08,0x00,0xd3,0x0c,0x52,0x04,0x08,0x00,0x11,0x04,0x14,0x00,
+ 0x00,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x08,0xe6,0x08,0x01,0x10,0x04,0x08,0xdc,
+ 0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x08,0x09,0xcf,0x86,0x95,0x28,
+ 0xd4,0x14,0x53,0x04,0x08,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x53,0x04,0x08,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x08,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0xd0,0x0a,0xcf,0x86,0x15,0x04,0x10,0x00,
+ 0x00,0x00,0xcf,0x86,0x55,0x04,0x10,0x00,0xd4,0x24,0xd3,0x14,0x52,0x04,0x10,0x00,
+ 0xd1,0x08,0x10,0x04,0x10,0x00,0x10,0xe6,0x10,0x04,0x10,0xdc,0x00,0x00,0x92,0x0c,
+ 0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0x93,0x10,0x52,0x04,
+ 0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xd1,0x54,
+ 0xd0,0x26,0xcf,0x86,0x55,0x04,0x0b,0x00,0x54,0x04,0x0b,0x00,0xd3,0x0c,0x52,0x04,
+ 0x0b,0x00,0x11,0x04,0x0b,0x00,0x00,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,
+ 0x0b,0x00,0x0b,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x14,0x54,0x04,0x0b,0x00,0x93,0x0c,
+ 0x52,0x04,0x0b,0x00,0x11,0x04,0x0b,0x00,0x00,0x00,0x0b,0x00,0x54,0x04,0x0b,0x00,
+ 0x93,0x10,0x92,0x0c,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,
+ 0x0b,0x00,0xd0,0x42,0xcf,0x86,0xd5,0x28,0x54,0x04,0x10,0x00,0xd3,0x0c,0x92,0x08,
+ 0x11,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,
+ 0x10,0x00,0x10,0x00,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x94,0x14,
+ 0x53,0x04,0x00,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,
+ 0x10,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd3,0x96,0xd2,0x68,0xd1,0x24,0xd0,0x06,
+ 0xcf,0x06,0x0b,0x00,0xcf,0x86,0x95,0x18,0x94,0x14,0x53,0x04,0x0b,0x00,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xd0,0x1e,0xcf,0x86,0x55,0x04,0x11,0x00,0x54,0x04,0x11,0x00,0x93,0x10,0x92,0x0c,
+ 0x51,0x04,0x11,0x00,0x10,0x04,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,
+ 0x55,0x04,0x11,0x00,0x54,0x04,0x11,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x11,0x00,
+ 0x10,0x04,0x11,0x00,0x00,0x00,0x00,0x00,0x92,0x08,0x11,0x04,0x00,0x00,0x11,0x00,
+ 0x11,0x00,0xd1,0x28,0xd0,0x22,0xcf,0x86,0x55,0x04,0x14,0x00,0xd4,0x0c,0x93,0x08,
+ 0x12,0x04,0x14,0x00,0x14,0xe6,0x00,0x00,0x53,0x04,0x14,0x00,0x92,0x08,0x11,0x04,
+ 0x14,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xd2,0x2a,
+ 0xd1,0x24,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,
+ 0x0b,0x00,0x53,0x04,0x0b,0x00,0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,
+ 0x0b,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd1,0x58,0xd0,0x12,0xcf,0x86,0x55,0x04,
+ 0x14,0x00,0x94,0x08,0x13,0x04,0x14,0x00,0x00,0x00,0x14,0x00,0xcf,0x86,0x95,0x40,
+ 0xd4,0x24,0xd3,0x0c,0x52,0x04,0x14,0x00,0x11,0x04,0x14,0x00,0x14,0xdc,0xd2,0x0c,
+ 0x51,0x04,0x14,0xe6,0x10,0x04,0x14,0xe6,0x14,0xdc,0x91,0x08,0x10,0x04,0x14,0xe6,
+ 0x14,0xdc,0x14,0xdc,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,0xdc,0x14,0x00,
+ 0x14,0x00,0x14,0x00,0x92,0x08,0x11,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x15,0x00,
+ 0x93,0x10,0x52,0x04,0x15,0x00,0x51,0x04,0x15,0x00,0x10,0x04,0x15,0x00,0x00,0x00,
+ 0x00,0x00,0xcf,0x86,0xe5,0x0f,0x06,0xe4,0xf8,0x03,0xe3,0x02,0x02,0xd2,0xfb,0xd1,
+ 0x4c,0xd0,0x06,0xcf,0x06,0x0c,0x00,0xcf,0x86,0xd5,0x2c,0xd4,0x1c,0xd3,0x10,0x52,
+ 0x04,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x09,0x0c,0x00,0x52,0x04,0x0c,
+ 0x00,0x11,0x04,0x0c,0x00,0x00,0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x00,0x00,0x0c,
+ 0x00,0x0c,0x00,0x0c,0x00,0x54,0x04,0x0c,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,
+ 0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x09,0xd0,0x69,0xcf,0x86,0xd5,
+ 0x32,0x54,0x04,0x0b,0x00,0x53,0x04,0x0b,0x00,0xd2,0x15,0x51,0x04,0x0b,0x00,0x10,
+ 0x0d,0x0b,0xff,0xf0,0x91,0x82,0x99,0xf0,0x91,0x82,0xba,0x00,0x0b,0x00,0x91,0x11,
+ 0x10,0x0d,0x0b,0xff,0xf0,0x91,0x82,0x9b,0xf0,0x91,0x82,0xba,0x00,0x0b,0x00,0x0b,
+ 0x00,0xd4,0x1d,0x53,0x04,0x0b,0x00,0x92,0x15,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,
+ 0x00,0x0b,0xff,0xf0,0x91,0x82,0xa5,0xf0,0x91,0x82,0xba,0x00,0x0b,0x00,0x53,0x04,
+ 0x0b,0x00,0x92,0x10,0xd1,0x08,0x10,0x04,0x0b,0x00,0x0b,0x09,0x10,0x04,0x0b,0x07,
+ 0x0b,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x20,0x94,0x1c,0xd3,0x0c,0x92,0x08,0x11,0x04,
+ 0x0b,0x00,0x00,0x00,0x00,0x00,0x52,0x04,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,
+ 0x14,0x00,0x00,0x00,0x0d,0x00,0xd4,0x14,0x53,0x04,0x0d,0x00,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x0d,0x00,0x92,0x08,
+ 0x11,0x04,0x0d,0x00,0x00,0x00,0x00,0x00,0xd1,0x96,0xd0,0x5c,0xcf,0x86,0xd5,0x18,
+ 0x94,0x14,0x93,0x10,0x92,0x0c,0x51,0x04,0x0d,0xe6,0x10,0x04,0x0d,0xe6,0x0d,0x00,
+ 0x0d,0x00,0x0d,0x00,0x0d,0x00,0xd4,0x26,0x53,0x04,0x0d,0x00,0x52,0x04,0x0d,0x00,
+ 0x51,0x04,0x0d,0x00,0x10,0x0d,0x0d,0xff,0xf0,0x91,0x84,0xb1,0xf0,0x91,0x84,0xa7,
+ 0x00,0x0d,0xff,0xf0,0x91,0x84,0xb2,0xf0,0x91,0x84,0xa7,0x00,0x93,0x18,0xd2,0x0c,
+ 0x51,0x04,0x0d,0x00,0x10,0x04,0x0d,0x00,0x0d,0x09,0x91,0x08,0x10,0x04,0x0d,0x09,
+ 0x00,0x00,0x0d,0x00,0x0d,0x00,0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x52,0x04,
+ 0x0d,0x00,0x51,0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x10,0x00,
+ 0x54,0x04,0x10,0x00,0x93,0x18,0xd2,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,
+ 0x10,0x07,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xd0,0x06,
+ 0xcf,0x06,0x0d,0x00,0xcf,0x86,0xd5,0x40,0xd4,0x2c,0xd3,0x10,0x92,0x0c,0x91,0x08,
+ 0x10,0x04,0x0d,0x09,0x0d,0x00,0x0d,0x00,0x0d,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,
+ 0x0d,0x00,0x11,0x00,0x10,0x04,0x11,0x07,0x11,0x00,0x91,0x08,0x10,0x04,0x11,0x00,
+ 0x10,0x00,0x00,0x00,0x53,0x04,0x0d,0x00,0x92,0x0c,0x51,0x04,0x0d,0x00,0x10,0x04,
+ 0x10,0x00,0x11,0x00,0x11,0x00,0xd4,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x93,0x10,0x52,0x04,0x10,0x00,
+ 0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd2,0xc8,0xd1,0x48,
+ 0xd0,0x42,0xcf,0x86,0xd5,0x18,0x54,0x04,0x10,0x00,0x93,0x10,0x92,0x0c,0x51,0x04,
+ 0x10,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x54,0x04,0x10,0x00,
+ 0xd3,0x14,0x52,0x04,0x10,0x00,0xd1,0x08,0x10,0x04,0x10,0x00,0x10,0x09,0x10,0x04,
+ 0x10,0x07,0x10,0x00,0x52,0x04,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x12,0x00,
+ 0x00,0x00,0xcf,0x06,0x00,0x00,0xd0,0x52,0xcf,0x86,0xd5,0x3c,0xd4,0x28,0xd3,0x10,
+ 0x52,0x04,0x11,0x00,0x51,0x04,0x11,0x00,0x10,0x04,0x11,0x00,0x00,0x00,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x11,0x00,0x00,0x00,0x11,0x00,0x51,0x04,0x11,0x00,0x10,0x04,
+ 0x00,0x00,0x11,0x00,0x53,0x04,0x11,0x00,0x52,0x04,0x11,0x00,0x51,0x04,0x11,0x00,
+ 0x10,0x04,0x00,0x00,0x11,0x00,0x94,0x10,0x53,0x04,0x11,0x00,0x92,0x08,0x11,0x04,
+ 0x11,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0xcf,0x86,0x55,0x04,0x10,0x00,0xd4,0x18,
+ 0x53,0x04,0x10,0x00,0x92,0x10,0xd1,0x08,0x10,0x04,0x10,0x00,0x10,0x07,0x10,0x04,
+ 0x10,0x09,0x00,0x00,0x00,0x00,0x53,0x04,0x10,0x00,0x92,0x08,0x11,0x04,0x10,0x00,
+ 0x00,0x00,0x00,0x00,0xe1,0x27,0x01,0xd0,0x8a,0xcf,0x86,0xd5,0x44,0xd4,0x2c,0xd3,
+ 0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x11,0x00,0x10,0x00,0x10,0x00,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x10,0x00,0x10,0x00,0x52,0x04,0x10,0x00,0xd1,0x08,0x10,0x04,0x10,
+ 0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x93,0x14,0x92,0x10,0xd1,0x08,0x10,
+ 0x04,0x10,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0xd4,
+ 0x14,0x53,0x04,0x10,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x10,
+ 0x00,0x10,0x00,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x10,
+ 0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0xd2,0x0c,0x51,0x04,0x10,
+ 0x00,0x10,0x04,0x00,0x00,0x14,0x07,0x91,0x08,0x10,0x04,0x10,0x07,0x10,0x00,0x10,
+ 0x00,0xcf,0x86,0xd5,0x6a,0xd4,0x42,0xd3,0x14,0x52,0x04,0x10,0x00,0xd1,0x08,0x10,
+ 0x04,0x10,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0xd2,0x19,0xd1,0x08,0x10,
+ 0x04,0x10,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0xff,0xf0,0x91,0x8d,0x87,0xf0,
+ 0x91,0x8c,0xbe,0x00,0x91,0x11,0x10,0x0d,0x10,0xff,0xf0,0x91,0x8d,0x87,0xf0,0x91,
+ 0x8d,0x97,0x00,0x10,0x09,0x00,0x00,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x11,
+ 0x00,0x00,0x00,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x52,
+ 0x04,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0xd4,0x1c,0xd3,
+ 0x0c,0x52,0x04,0x10,0x00,0x11,0x04,0x00,0x00,0x10,0xe6,0x52,0x04,0x10,0xe6,0x91,
+ 0x08,0x10,0x04,0x10,0xe6,0x00,0x00,0x00,0x00,0x93,0x10,0x52,0x04,0x10,0xe6,0x91,
+ 0x08,0x10,0x04,0x10,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xe3,
+ 0x30,0x01,0xd2,0xb7,0xd1,0x48,0xd0,0x06,0xcf,0x06,0x12,0x00,0xcf,0x86,0x95,0x3c,
+ 0xd4,0x1c,0x93,0x18,0xd2,0x0c,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x09,0x12,0x00,
+ 0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x07,0x12,0x00,0x12,0x00,0x53,0x04,0x12,0x00,
+ 0xd2,0x0c,0x51,0x04,0x12,0x00,0x10,0x04,0x00,0x00,0x12,0x00,0xd1,0x08,0x10,0x04,
+ 0x00,0x00,0x12,0x00,0x10,0x04,0x14,0xe6,0x15,0x00,0x00,0x00,0xd0,0x45,0xcf,0x86,
+ 0x55,0x04,0x10,0x00,0x54,0x04,0x10,0x00,0x53,0x04,0x10,0x00,0xd2,0x15,0x51,0x04,
+ 0x10,0x00,0x10,0x04,0x10,0x00,0x10,0xff,0xf0,0x91,0x92,0xb9,0xf0,0x91,0x92,0xba,
+ 0x00,0xd1,0x11,0x10,0x0d,0x10,0xff,0xf0,0x91,0x92,0xb9,0xf0,0x91,0x92,0xb0,0x00,
+ 0x10,0x00,0x10,0x0d,0x10,0xff,0xf0,0x91,0x92,0xb9,0xf0,0x91,0x92,0xbd,0x00,0x10,
+ 0x00,0xcf,0x86,0x95,0x24,0xd4,0x14,0x93,0x10,0x92,0x0c,0x51,0x04,0x10,0x00,0x10,
+ 0x04,0x10,0x09,0x10,0x07,0x10,0x00,0x00,0x00,0x53,0x04,0x10,0x00,0x92,0x08,0x11,
+ 0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,
+ 0x40,0xcf,0x86,0x55,0x04,0x10,0x00,0x54,0x04,0x10,0x00,0xd3,0x0c,0x52,0x04,0x10,
+ 0x00,0x11,0x04,0x10,0x00,0x00,0x00,0xd2,0x1e,0x51,0x04,0x10,0x00,0x10,0x0d,0x10,
+ 0xff,0xf0,0x91,0x96,0xb8,0xf0,0x91,0x96,0xaf,0x00,0x10,0xff,0xf0,0x91,0x96,0xb9,
+ 0xf0,0x91,0x96,0xaf,0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x10,0x09,0xcf,
+ 0x86,0x95,0x2c,0xd4,0x1c,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x10,0x07,0x10,
+ 0x00,0x10,0x00,0x10,0x00,0x92,0x08,0x11,0x04,0x10,0x00,0x11,0x00,0x11,0x00,0x53,
+ 0x04,0x11,0x00,0x52,0x04,0x11,0x00,0x11,0x04,0x11,0x00,0x00,0x00,0x00,0x00,0xd2,
+ 0xa0,0xd1,0x5c,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x10,0x00,0x54,0x04,0x10,0x00,0x53,
+ 0x04,0x10,0x00,0x52,0x04,0x10,0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x10,
+ 0x09,0xcf,0x86,0xd5,0x24,0xd4,0x14,0x93,0x10,0x52,0x04,0x10,0x00,0x91,0x08,0x10,
+ 0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x10,0x00,0x92,0x08,0x11,
+ 0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x94,0x14,0x53,0x04,0x12,0x00,0x52,0x04,0x12,
+ 0x00,0x91,0x08,0x10,0x04,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd0,0x2a,0xcf,
+ 0x86,0x55,0x04,0x0d,0x00,0x54,0x04,0x0d,0x00,0xd3,0x10,0x52,0x04,0x0d,0x00,0x51,
+ 0x04,0x0d,0x00,0x10,0x04,0x0d,0x09,0x0d,0x07,0x92,0x0c,0x91,0x08,0x10,0x04,0x15,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0x95,0x14,0x94,0x10,0x53,0x04,0x0d,
+ 0x00,0x92,0x08,0x11,0x04,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,
+ 0x40,0xd0,0x3a,0xcf,0x86,0xd5,0x20,0x54,0x04,0x11,0x00,0x53,0x04,0x11,0x00,0xd2,
+ 0x0c,0x51,0x04,0x11,0x00,0x10,0x04,0x14,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x00,
+ 0x00,0x11,0x00,0x11,0x00,0x94,0x14,0x53,0x04,0x11,0x00,0x92,0x0c,0x51,0x04,0x11,
+ 0x00,0x10,0x04,0x11,0x00,0x11,0x09,0x00,0x00,0x11,0x00,0xcf,0x06,0x00,0x00,0xcf,
+ 0x06,0x00,0x00,0xe4,0x59,0x01,0xd3,0xb2,0xd2,0x5c,0xd1,0x28,0xd0,0x22,0xcf,0x86,
+ 0x55,0x04,0x14,0x00,0x54,0x04,0x14,0x00,0x53,0x04,0x14,0x00,0x92,0x10,0xd1,0x08,
+ 0x10,0x04,0x14,0x00,0x14,0x09,0x10,0x04,0x14,0x07,0x14,0x00,0x00,0x00,0xcf,0x06,
+ 0x00,0x00,0xd0,0x0a,0xcf,0x86,0x15,0x04,0x00,0x00,0x10,0x00,0xcf,0x86,0x55,0x04,
+ 0x10,0x00,0x54,0x04,0x10,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,
+ 0x10,0x00,0x00,0x00,0x00,0x00,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,
+ 0x00,0x00,0x10,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x1a,0xcf,0x86,0x55,0x04,
+ 0x00,0x00,0x94,0x10,0x53,0x04,0x15,0x00,0x92,0x08,0x11,0x04,0x00,0x00,0x15,0x00,
+ 0x15,0x00,0x15,0x00,0xcf,0x86,0xd5,0x14,0x54,0x04,0x15,0x00,0x53,0x04,0x15,0x00,
+ 0x92,0x08,0x11,0x04,0x00,0x00,0x15,0x00,0x15,0x00,0x94,0x1c,0x93,0x18,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x15,0x09,0x15,0x00,0x15,0x00,0x91,0x08,0x10,0x04,0x15,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd2,0xa0,0xd1,0x3c,0xd0,0x1e,0xcf,0x86,
+ 0x55,0x04,0x13,0x00,0x54,0x04,0x13,0x00,0x93,0x10,0x52,0x04,0x13,0x00,0x91,0x08,
+ 0x10,0x04,0x13,0x09,0x13,0x00,0x13,0x00,0x13,0x00,0xcf,0x86,0x95,0x18,0x94,0x14,
+ 0x93,0x10,0x52,0x04,0x13,0x00,0x51,0x04,0x13,0x00,0x10,0x04,0x13,0x00,0x13,0x09,
+ 0x00,0x00,0x13,0x00,0x13,0x00,0xd0,0x46,0xcf,0x86,0xd5,0x2c,0xd4,0x10,0x93,0x0c,
+ 0x52,0x04,0x13,0x00,0x11,0x04,0x15,0x00,0x13,0x00,0x13,0x00,0x53,0x04,0x13,0x00,
+ 0xd2,0x0c,0x91,0x08,0x10,0x04,0x13,0x00,0x13,0x09,0x13,0x00,0x91,0x08,0x10,0x04,
+ 0x13,0x00,0x14,0x00,0x13,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x51,0x04,0x13,0x00,
+ 0x10,0x04,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0x55,0x04,
+ 0x10,0x00,0x54,0x04,0x10,0x00,0x53,0x04,0x10,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xe3,0xa9,0x01,0xd2,
+ 0xb0,0xd1,0x6c,0xd0,0x3e,0xcf,0x86,0xd5,0x18,0x94,0x14,0x53,0x04,0x12,0x00,0x92,
+ 0x0c,0x91,0x08,0x10,0x04,0x12,0x00,0x00,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x54,
+ 0x04,0x12,0x00,0xd3,0x10,0x52,0x04,0x12,0x00,0x51,0x04,0x12,0x00,0x10,0x04,0x12,
+ 0x00,0x00,0x00,0x52,0x04,0x12,0x00,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x12,
+ 0x09,0xcf,0x86,0xd5,0x14,0x94,0x10,0x93,0x0c,0x52,0x04,0x12,0x00,0x11,0x04,0x12,
+ 0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x94,0x14,0x53,0x04,0x12,0x00,0x52,0x04,0x12,
+ 0x00,0x91,0x08,0x10,0x04,0x12,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0xd0,0x3e,0xcf,
+ 0x86,0xd5,0x14,0x54,0x04,0x12,0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x00,0x00,0x12,
+ 0x00,0x12,0x00,0x12,0x00,0xd4,0x14,0x53,0x04,0x12,0x00,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x93,0x10,0x52,0x04,0x12,0x00,0x51,
+ 0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd1,
+ 0xa0,0xd0,0x52,0xcf,0x86,0xd5,0x24,0x94,0x20,0xd3,0x10,0x52,0x04,0x13,0x00,0x51,
+ 0x04,0x13,0x00,0x10,0x04,0x13,0x00,0x00,0x00,0x92,0x0c,0x51,0x04,0x13,0x00,0x10,
+ 0x04,0x00,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x54,0x04,0x13,0x00,0xd3,0x10,0x52,
+ 0x04,0x13,0x00,0x51,0x04,0x13,0x00,0x10,0x04,0x13,0x00,0x00,0x00,0xd2,0x0c,0x51,
+ 0x04,0x00,0x00,0x10,0x04,0x13,0x00,0x00,0x00,0x51,0x04,0x13,0x00,0x10,0x04,0x00,
+ 0x00,0x13,0x00,0xcf,0x86,0xd5,0x28,0xd4,0x18,0x93,0x14,0xd2,0x0c,0x51,0x04,0x13,
+ 0x00,0x10,0x04,0x13,0x07,0x13,0x00,0x11,0x04,0x13,0x09,0x13,0x00,0x00,0x00,0x53,
+ 0x04,0x13,0x00,0x92,0x08,0x11,0x04,0x13,0x00,0x00,0x00,0x00,0x00,0x94,0x20,0xd3,
+ 0x10,0x52,0x04,0x14,0x00,0x51,0x04,0x14,0x00,0x10,0x04,0x00,0x00,0x14,0x00,0x92,
+ 0x0c,0x91,0x08,0x10,0x04,0x14,0x00,0x00,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0xd0,
+ 0x52,0xcf,0x86,0xd5,0x3c,0xd4,0x14,0x53,0x04,0x14,0x00,0x52,0x04,0x14,0x00,0x51,
+ 0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x00,0x00,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x14,
+ 0x00,0x10,0x04,0x00,0x00,0x14,0x00,0x51,0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x14,
+ 0x09,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,
+ 0x10,0x53,0x04,0x14,0x00,0x92,0x08,0x11,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0xcf,0x06,0x00,0x00,0xd2,0x2a,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,
+ 0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x14,0x00,0x53,0x04,0x14,
+ 0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd1,
+ 0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x15,
+ 0x00,0x54,0x04,0x15,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,0x15,0x00,0x00,0x00,0x00,
+ 0x00,0x52,0x04,0x00,0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x15,0x00,0xd0,
+ 0xca,0xcf,0x86,0xd5,0xc2,0xd4,0x54,0xd3,0x06,0xcf,0x06,0x09,0x00,0xd2,0x06,0xcf,
+ 0x06,0x09,0x00,0xd1,0x24,0xd0,0x06,0xcf,0x06,0x09,0x00,0xcf,0x86,0x55,0x04,0x09,
+ 0x00,0x94,0x14,0x53,0x04,0x09,0x00,0x52,0x04,0x09,0x00,0x51,0x04,0x09,0x00,0x10,
+ 0x04,0x09,0x00,0x10,0x00,0x10,0x00,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,0x10,
+ 0x00,0x53,0x04,0x10,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x10,0x00,0x11,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd3,0x68,0xd2,0x46,0xd1,0x40,0xd0,
+ 0x06,0xcf,0x06,0x09,0x00,0xcf,0x86,0x55,0x04,0x09,0x00,0xd4,0x20,0xd3,0x10,0x92,
+ 0x0c,0x51,0x04,0x09,0x00,0x10,0x04,0x09,0x00,0x10,0x00,0x10,0x00,0x52,0x04,0x10,
+ 0x00,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x00,0x00,0x93,0x10,0x52,0x04,0x09,
+ 0x00,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x11,
+ 0x00,0xd1,0x1c,0xd0,0x06,0xcf,0x06,0x11,0x00,0xcf,0x86,0x95,0x10,0x94,0x0c,0x93,
+ 0x08,0x12,0x04,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,
+ 0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x86,0xd5,0x4c,0xd4,0x06,0xcf,
+ 0x06,0x0b,0x00,0xd3,0x40,0xd2,0x3a,0xd1,0x34,0xd0,0x2e,0xcf,0x86,0x55,0x04,0x0b,
+ 0x00,0xd4,0x14,0x53,0x04,0x0b,0x00,0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,
+ 0x04,0x0b,0x00,0x00,0x00,0x53,0x04,0x15,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x15,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,
+ 0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xd1,0x4c,0xd0,0x44,0xcf,
+ 0x86,0xd5,0x3c,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,0x11,0x00,0xd2,
+ 0x2a,0xd1,0x24,0xd0,0x06,0xcf,0x06,0x11,0x00,0xcf,0x86,0x95,0x18,0x94,0x14,0x93,
+ 0x10,0x52,0x04,0x11,0x00,0x51,0x04,0x11,0x00,0x10,0x04,0x11,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,
+ 0x00,0xcf,0x86,0xcf,0x06,0x00,0x00,0xe0,0xd2,0x01,0xcf,0x86,0xd5,0x06,0xcf,0x06,
+ 0x00,0x00,0xe4,0x0b,0x01,0xd3,0x06,0xcf,0x06,0x0c,0x00,0xd2,0x84,0xd1,0x50,0xd0,
+ 0x1e,0xcf,0x86,0x55,0x04,0x0c,0x00,0x54,0x04,0x0c,0x00,0x53,0x04,0x0c,0x00,0x92,
+ 0x0c,0x91,0x08,0x10,0x04,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,
+ 0x18,0x54,0x04,0x10,0x00,0x53,0x04,0x10,0x00,0x52,0x04,0x10,0x00,0x51,0x04,0x10,
+ 0x00,0x10,0x04,0x10,0x00,0x00,0x00,0x94,0x14,0x53,0x04,0x10,0x00,0xd2,0x08,0x11,
+ 0x04,0x10,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x10,0x00,0x00,0x00,0xd0,0x06,0xcf,
+ 0x06,0x00,0x00,0xcf,0x86,0xd5,0x08,0x14,0x04,0x00,0x00,0x10,0x00,0xd4,0x10,0x53,
+ 0x04,0x10,0x00,0x52,0x04,0x10,0x00,0x11,0x04,0x10,0x00,0x00,0x00,0x93,0x10,0x52,
+ 0x04,0x10,0x01,0x91,0x08,0x10,0x04,0x10,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0xd1,
+ 0x6c,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x10,0x00,0x54,0x04,0x10,0x00,0x93,0x10,0x52,
+ 0x04,0x10,0xe6,0x51,0x04,0x10,0xe6,0x10,0x04,0x10,0xe6,0x10,0x00,0x10,0x00,0xcf,
+ 0x86,0xd5,0x24,0xd4,0x10,0x93,0x0c,0x52,0x04,0x10,0x00,0x11,0x04,0x10,0x00,0x00,
+ 0x00,0x00,0x00,0x53,0x04,0x10,0x00,0x92,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,0x00,
+ 0x00,0x10,0x00,0x10,0x00,0xd4,0x14,0x93,0x10,0x92,0x0c,0x51,0x04,0x10,0x00,0x10,
+ 0x04,0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x53,0x04,0x10,0x00,0x52,0x04,0x00,
+ 0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x10,0x00,0x10,0x00,0xd0,0x0e,0xcf,0x86,0x95,
+ 0x08,0x14,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd3,0x06,0xcf,
+ 0x06,0x00,0x00,0xd2,0x30,0xd1,0x0c,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x06,0x14,
+ 0x00,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,0x14,0x00,0x53,0x04,0x14,0x00,0x92,
+ 0x0c,0x51,0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,
+ 0x06,0x00,0x00,0xd1,0x4c,0xd0,0x06,0xcf,0x06,0x0d,0x00,0xcf,0x86,0xd5,0x2c,0x94,
+ 0x28,0xd3,0x10,0x52,0x04,0x0d,0x00,0x91,0x08,0x10,0x04,0x0d,0x00,0x15,0x00,0x15,
+ 0x00,0xd2,0x0c,0x51,0x04,0x15,0x00,0x10,0x04,0x15,0x00,0x00,0x00,0x51,0x04,0x00,
+ 0x00,0x10,0x04,0x00,0x00,0x15,0x00,0x0d,0x00,0x54,0x04,0x0d,0x00,0x53,0x04,0x0d,
+ 0x00,0x52,0x04,0x0d,0x00,0x51,0x04,0x0d,0x00,0x10,0x04,0x0d,0x00,0x15,0x00,0xd0,
+ 0x1e,0xcf,0x86,0x95,0x18,0x94,0x14,0x53,0x04,0x15,0x00,0x52,0x04,0x00,0x00,0x51,
+ 0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x0d,0x00,0x0d,0x00,0x00,0x00,0xcf,0x86,0x55,
+ 0x04,0x00,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x12,0x00,0x13,
+ 0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xcf,0x06,0x12,0x00,0xe2,
+ 0xc6,0x01,0xd1,0x8e,0xd0,0x86,0xcf,0x86,0xd5,0x48,0xd4,0x06,0xcf,0x06,0x12,0x00,
+ 0xd3,0x06,0xcf,0x06,0x12,0x00,0xd2,0x06,0xcf,0x06,0x12,0x00,0xd1,0x06,0xcf,0x06,
+ 0x12,0x00,0xd0,0x06,0xcf,0x06,0x12,0x00,0xcf,0x86,0x55,0x04,0x12,0x00,0xd4,0x14,
+ 0x53,0x04,0x12,0x00,0x52,0x04,0x12,0x00,0x91,0x08,0x10,0x04,0x12,0x00,0x14,0x00,
+ 0x14,0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x14,0x00,0x15,0x00,0x15,0x00,0x00,0x00,
+ 0xd4,0x36,0xd3,0x06,0xcf,0x06,0x12,0x00,0xd2,0x2a,0xd1,0x06,0xcf,0x06,0x12,0x00,
+ 0xd0,0x06,0xcf,0x06,0x12,0x00,0xcf,0x86,0x55,0x04,0x12,0x00,0x54,0x04,0x12,0x00,
+ 0x93,0x10,0x92,0x0c,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x86,0xcf,0x06,0x00,0x00,
+ 0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,0xd5,0xa2,0xd4,0x9c,0xd3,0x74,
+ 0xd2,0x26,0xd1,0x20,0xd0,0x1a,0xcf,0x86,0x95,0x14,0x94,0x10,0x93,0x0c,0x92,0x08,
+ 0x11,0x04,0x0c,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0x13,0x00,0xcf,0x06,
+ 0x13,0x00,0xcf,0x06,0x13,0x00,0xd1,0x48,0xd0,0x1e,0xcf,0x86,0x95,0x18,0x54,0x04,
+ 0x13,0x00,0x53,0x04,0x13,0x00,0x52,0x04,0x13,0x00,0x51,0x04,0x13,0x00,0x10,0x04,
+ 0x13,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0xd5,0x18,0x54,0x04,0x00,0x00,0x93,0x10,
+ 0x92,0x0c,0x51,0x04,0x15,0x00,0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x94,0x0c,0x93,0x08,0x12,0x04,0x00,0x00,0x15,0x00,0x00,0x00,0x13,0x00,0xcf,0x06,
+ 0x13,0x00,0xd2,0x22,0xd1,0x06,0xcf,0x06,0x13,0x00,0xd0,0x06,0xcf,0x06,0x13,0x00,
+ 0xcf,0x86,0x55,0x04,0x13,0x00,0x54,0x04,0x13,0x00,0x53,0x04,0x13,0x00,0x12,0x04,
+ 0x13,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,
+ 0x00,0x00,0xd3,0x7f,0xd2,0x79,0xd1,0x34,0xd0,0x06,0xcf,0x06,0x10,0x00,0xcf,0x86,
+ 0x55,0x04,0x10,0x00,0xd4,0x14,0x53,0x04,0x10,0x00,0x92,0x0c,0x51,0x04,0x10,0x00,
+ 0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x10,0x00,0x52,0x04,0x10,0x00,
+ 0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xd0,0x3f,0xcf,0x86,0xd5,0x2c,
+ 0xd4,0x14,0x53,0x04,0x10,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x53,0x04,0x10,0x00,0xd2,0x08,0x11,0x04,0x10,0x00,0x00,0x00,
+ 0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x01,0x10,0x00,0x94,0x0d,0x93,0x09,0x12,0x05,
+ 0x10,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,
+ 0x00,0xcf,0x06,0x00,0x00,0xe1,0x96,0x04,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,
+ 0xcf,0x86,0xe5,0x33,0x04,0xe4,0x83,0x02,0xe3,0xf8,0x01,0xd2,0x26,0xd1,0x06,0xcf,
+ 0x06,0x05,0x00,0xd0,0x06,0xcf,0x06,0x05,0x00,0xcf,0x86,0x55,0x04,0x05,0x00,0x54,
+ 0x04,0x05,0x00,0x93,0x0c,0x52,0x04,0x05,0x00,0x11,0x04,0x05,0x00,0x00,0x00,0x00,
+ 0x00,0xd1,0xef,0xd0,0x2a,0xcf,0x86,0x55,0x04,0x05,0x00,0x94,0x20,0xd3,0x10,0x52,
+ 0x04,0x05,0x00,0x51,0x04,0x05,0x00,0x10,0x04,0x05,0x00,0x00,0x00,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x0a,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0xcf,0x86,0xd5,
+ 0x2a,0x54,0x04,0x05,0x00,0x53,0x04,0x05,0x00,0x52,0x04,0x05,0x00,0x51,0x04,0x05,
+ 0x00,0x10,0x0d,0x05,0xff,0xf0,0x9d,0x85,0x97,0xf0,0x9d,0x85,0xa5,0x00,0x05,0xff,
+ 0xf0,0x9d,0x85,0x98,0xf0,0x9d,0x85,0xa5,0x00,0xd4,0x75,0xd3,0x61,0xd2,0x44,0xd1,
+ 0x22,0x10,0x11,0x05,0xff,0xf0,0x9d,0x85,0x98,0xf0,0x9d,0x85,0xa5,0xf0,0x9d,0x85,
+ 0xae,0x00,0x05,0xff,0xf0,0x9d,0x85,0x98,0xf0,0x9d,0x85,0xa5,0xf0,0x9d,0x85,0xaf,
+ 0x00,0x10,0x11,0x05,0xff,0xf0,0x9d,0x85,0x98,0xf0,0x9d,0x85,0xa5,0xf0,0x9d,0x85,
+ 0xb0,0x00,0x05,0xff,0xf0,0x9d,0x85,0x98,0xf0,0x9d,0x85,0xa5,0xf0,0x9d,0x85,0xb1,
+ 0x00,0xd1,0x15,0x10,0x11,0x05,0xff,0xf0,0x9d,0x85,0x98,0xf0,0x9d,0x85,0xa5,0xf0,
+ 0x9d,0x85,0xb2,0x00,0x05,0xd8,0x10,0x04,0x05,0xd8,0x05,0x01,0xd2,0x08,0x11,0x04,
+ 0x05,0x01,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x05,0xe2,0x05,0xd8,0xd3,0x12,
+ 0x92,0x0d,0x51,0x04,0x05,0xd8,0x10,0x04,0x05,0xd8,0x05,0xff,0x00,0x05,0xff,0x00,
+ 0x92,0x0e,0x51,0x05,0x05,0xff,0x00,0x10,0x05,0x05,0xff,0x00,0x05,0xdc,0x05,0xdc,
+ 0xd0,0x97,0xcf,0x86,0xd5,0x28,0x94,0x24,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x05,0xdc,
+ 0x10,0x04,0x05,0xdc,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x05,0xe6,0x05,0xe6,
+ 0x92,0x08,0x11,0x04,0x05,0xe6,0x05,0xdc,0x05,0x00,0x05,0x00,0xd4,0x14,0x53,0x04,
+ 0x05,0x00,0xd2,0x08,0x11,0x04,0x05,0x00,0x05,0xe6,0x11,0x04,0x05,0xe6,0x05,0x00,
+ 0x53,0x04,0x05,0x00,0xd2,0x15,0x51,0x04,0x05,0x00,0x10,0x04,0x05,0x00,0x05,0xff,
+ 0xf0,0x9d,0x86,0xb9,0xf0,0x9d,0x85,0xa5,0x00,0xd1,0x1e,0x10,0x0d,0x05,0xff,0xf0,
+ 0x9d,0x86,0xba,0xf0,0x9d,0x85,0xa5,0x00,0x05,0xff,0xf0,0x9d,0x86,0xb9,0xf0,0x9d,
+ 0x85,0xa5,0xf0,0x9d,0x85,0xae,0x00,0x10,0x11,0x05,0xff,0xf0,0x9d,0x86,0xba,0xf0,
+ 0x9d,0x85,0xa5,0xf0,0x9d,0x85,0xae,0x00,0x05,0xff,0xf0,0x9d,0x86,0xb9,0xf0,0x9d,
+ 0x85,0xa5,0xf0,0x9d,0x85,0xaf,0x00,0xcf,0x86,0xd5,0x31,0xd4,0x21,0x93,0x1d,0x92,
+ 0x19,0x91,0x15,0x10,0x11,0x05,0xff,0xf0,0x9d,0x86,0xba,0xf0,0x9d,0x85,0xa5,0xf0,
+ 0x9d,0x85,0xaf,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x53,0x04,0x05,0x00,
+ 0x52,0x04,0x05,0x00,0x11,0x04,0x05,0x00,0x11,0x00,0x94,0x14,0x53,0x04,0x11,0x00,
+ 0x92,0x0c,0x91,0x08,0x10,0x04,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0xd2,0x44,0xd1,0x28,0xd0,0x06,0xcf,0x06,0x08,0x00,0xcf,0x86,0x95,0x1c,0x94,0x18,
+ 0x93,0x14,0xd2,0x08,0x11,0x04,0x08,0x00,0x08,0xe6,0x91,0x08,0x10,0x04,0x08,0xe6,
+ 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,
+ 0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x14,0x00,0x93,0x08,0x12,0x04,0x14,0x00,
+ 0x00,0x00,0x00,0x00,0xd1,0x40,0xd0,0x06,0xcf,0x06,0x07,0x00,0xcf,0x86,0xd5,0x18,
+ 0x54,0x04,0x07,0x00,0x93,0x10,0x52,0x04,0x07,0x00,0x51,0x04,0x07,0x00,0x10,0x04,
+ 0x07,0x00,0x00,0x00,0x00,0x00,0x54,0x04,0x09,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,
+ 0x09,0x00,0x14,0x00,0x14,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xe3,0x5f,0x01,0xd2,0xb4,0xd1,0x24,0xd0,
+ 0x06,0xcf,0x06,0x05,0x00,0xcf,0x86,0x95,0x18,0x54,0x04,0x05,0x00,0x93,0x10,0x52,
+ 0x04,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x05,
+ 0x00,0xd0,0x6a,0xcf,0x86,0xd5,0x18,0x54,0x04,0x05,0x00,0x53,0x04,0x05,0x00,0x52,
+ 0x04,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x05,0x00,0xd4,0x34,0xd3,
+ 0x1c,0xd2,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x05,0x00,0x00,0x00,0xd1,0x08,0x10,
+ 0x04,0x00,0x00,0x05,0x00,0x10,0x04,0x05,0x00,0x00,0x00,0xd2,0x0c,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x05,0x00,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x05,
+ 0x00,0x53,0x04,0x05,0x00,0xd2,0x0c,0x51,0x04,0x05,0x00,0x10,0x04,0x00,0x00,0x05,
+ 0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x05,0x00,0x05,0x00,0xcf,0x86,0x95,0x20,0x94,
+ 0x1c,0x93,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x05,0x00,0x07,0x00,0x05,0x00,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0xd1,
+ 0xa4,0xd0,0x6a,0xcf,0x86,0xd5,0x48,0xd4,0x28,0xd3,0x10,0x52,0x04,0x05,0x00,0x51,
+ 0x04,0x05,0x00,0x10,0x04,0x00,0x00,0x05,0x00,0xd2,0x0c,0x51,0x04,0x05,0x00,0x10,
+ 0x04,0x05,0x00,0x00,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x05,0x00,0x05,0x00,0xd3,
+ 0x10,0x52,0x04,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x05,0x00,0x52,
+ 0x04,0x05,0x00,0x91,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x05,0x00,0x54,0x04,0x05,
+ 0x00,0x53,0x04,0x05,0x00,0xd2,0x0c,0x51,0x04,0x05,0x00,0x10,0x04,0x00,0x00,0x05,
+ 0x00,0x51,0x04,0x05,0x00,0x10,0x04,0x05,0x00,0x00,0x00,0xcf,0x86,0x95,0x34,0xd4,
+ 0x20,0xd3,0x14,0x52,0x04,0x05,0x00,0xd1,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x10,
+ 0x04,0x05,0x00,0x00,0x00,0x92,0x08,0x11,0x04,0x00,0x00,0x05,0x00,0x05,0x00,0x93,
+ 0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x05,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x05,
+ 0x00,0x05,0x00,0xcf,0x06,0x05,0x00,0xd2,0x26,0xd1,0x06,0xcf,0x06,0x05,0x00,0xd0,
+ 0x1a,0xcf,0x86,0x55,0x04,0x05,0x00,0x94,0x10,0x93,0x0c,0x52,0x04,0x05,0x00,0x11,
+ 0x04,0x08,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0xcf,0x06,0x05,0x00,0xd1,0x06,0xcf,
+ 0x06,0x05,0x00,0xd0,0x06,0xcf,0x06,0x05,0x00,0xcf,0x86,0x95,0x18,0x94,0x14,0x53,
+ 0x04,0x05,0x00,0xd2,0x08,0x11,0x04,0x05,0x00,0x09,0x00,0x11,0x04,0x00,0x00,0x05,
+ 0x00,0x05,0x00,0x05,0x00,0xd4,0x52,0xd3,0x06,0xcf,0x06,0x11,0x00,0xd2,0x46,0xd1,
+ 0x06,0xcf,0x06,0x11,0x00,0xd0,0x3a,0xcf,0x86,0xd5,0x20,0xd4,0x0c,0x53,0x04,0x11,
+ 0x00,0x12,0x04,0x11,0x00,0x00,0x00,0x53,0x04,0x00,0x00,0x92,0x0c,0x51,0x04,0x00,
+ 0x00,0x10,0x04,0x00,0x00,0x11,0x00,0x11,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x00,0x00,0xcf,
+ 0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xe0,0xc2,0x03,0xcf,0x86,
+ 0xe5,0x03,0x01,0xd4,0xfc,0xd3,0xc0,0xd2,0x66,0xd1,0x60,0xd0,0x5a,0xcf,0x86,0xd5,
+ 0x2c,0xd4,0x14,0x93,0x10,0x52,0x04,0x12,0xe6,0x51,0x04,0x12,0xe6,0x10,0x04,0x12,
+ 0xe6,0x00,0x00,0x12,0xe6,0x53,0x04,0x12,0xe6,0x92,0x10,0xd1,0x08,0x10,0x04,0x12,
+ 0xe6,0x00,0x00,0x10,0x04,0x00,0x00,0x12,0xe6,0x12,0xe6,0x94,0x28,0xd3,0x18,0xd2,
+ 0x0c,0x51,0x04,0x12,0xe6,0x10,0x04,0x00,0x00,0x12,0xe6,0x91,0x08,0x10,0x04,0x12,
+ 0xe6,0x00,0x00,0x12,0xe6,0x92,0x0c,0x51,0x04,0x12,0xe6,0x10,0x04,0x12,0xe6,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xd1,0x54,0xd0,
+ 0x36,0xcf,0x86,0x55,0x04,0x15,0x00,0xd4,0x14,0x53,0x04,0x15,0x00,0x52,0x04,0x15,
+ 0x00,0x91,0x08,0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0xd3,0x10,0x52,0x04,0x15,
+ 0xe6,0x51,0x04,0x15,0xe6,0x10,0x04,0x15,0xe6,0x15,0x00,0x52,0x04,0x15,0x00,0x11,
+ 0x04,0x15,0x00,0x00,0x00,0xcf,0x86,0x95,0x18,0x94,0x14,0x53,0x04,0x15,0x00,0xd2,
+ 0x08,0x11,0x04,0x15,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x15,0x00,0x00,0x00,0x00,
+ 0x00,0xcf,0x06,0x00,0x00,0xd2,0x36,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,
+ 0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x15,0x00,0xd4,0x0c,0x53,0x04,0x15,0x00,0x12,
+ 0x04,0x15,0x00,0x15,0xe6,0x53,0x04,0x15,0x00,0xd2,0x08,0x11,0x04,0x15,0x00,0x00,
+ 0x00,0x51,0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x15,0x00,0xcf,0x06,0x00,0x00,0xcf,
+ 0x06,0x00,0x00,0xd4,0x82,0xd3,0x7c,0xd2,0x3e,0xd1,0x06,0xcf,0x06,0x10,0x00,0xd0,
+ 0x06,0xcf,0x06,0x10,0x00,0xcf,0x86,0x95,0x2c,0xd4,0x18,0x93,0x14,0x52,0x04,0x10,
+ 0x00,0xd1,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x10,0x00,0x10,
+ 0x00,0x93,0x10,0x52,0x04,0x10,0xdc,0x51,0x04,0x10,0xdc,0x10,0x04,0x10,0xdc,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0xd1,0x38,0xd0,0x06,0xcf,0x06,0x12,0x00,0xcf,0x86,0x95,
+ 0x2c,0xd4,0x18,0xd3,0x08,0x12,0x04,0x12,0x00,0x12,0xe6,0x92,0x0c,0x51,0x04,0x12,
+ 0xe6,0x10,0x04,0x12,0x07,0x15,0x00,0x00,0x00,0x53,0x04,0x12,0x00,0xd2,0x08,0x11,
+ 0x04,0x12,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x12,0x00,0x00,0x00,0xcf,0x06,0x00,
+ 0x00,0xcf,0x06,0x00,0x00,0xd3,0x82,0xd2,0x48,0xd1,0x24,0xd0,0x06,0xcf,0x06,0x00,
+ 0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,0x93,0x10,0x92,0x0c,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0xd0,0x1e,0xcf,
+ 0x86,0x55,0x04,0x14,0x00,0x54,0x04,0x14,0x00,0x93,0x10,0x52,0x04,0x14,0x00,0x91,
+ 0x08,0x10,0x04,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd1,
+ 0x34,0xd0,0x2e,0xcf,0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x54,0x04,0x15,
+ 0x00,0x53,0x04,0x15,0x00,0x52,0x04,0x15,0x00,0x11,0x04,0x15,0x00,0x00,0x00,0xcf,
+ 0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xe2,0xb2,0x01,0xe1,0x41,0x01,0xd0,0x6e,0xcf,
+ 0x86,0xd5,0x18,0x94,0x14,0x93,0x10,0x52,0x04,0x0d,0x00,0x91,0x08,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0x0d,0x00,0x0d,0x00,0x0d,0x00,0xd4,0x30,0xd3,0x20,0xd2,0x10,0xd1,
+ 0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x10,0x04,0x0d,0x00,0x00,0x00,0xd1,0x08,0x10,
+ 0x04,0x0d,0x00,0x00,0x00,0x10,0x04,0x00,0x00,0x0d,0x00,0x92,0x0c,0x91,0x08,0x10,
+ 0x04,0x00,0x00,0x0d,0x00,0x0d,0x00,0x0d,0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x0d,
+ 0x00,0x10,0x04,0x0d,0x00,0x00,0x00,0x0d,0x00,0x92,0x10,0xd1,0x08,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0x10,0x04,0x00,0x00,0x0d,0x00,0x00,0x00,0xcf,0x86,0xd5,0x74,0xd4,
+ 0x34,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x00,0x00,0x10,0x04,0x0d,0x00,0x00,0x00,0x51,
+ 0x04,0x00,0x00,0x10,0x04,0x00,0x00,0x0d,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0x10,0x04,0x00,0x00,0x0d,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x0d,
+ 0x00,0x0d,0x00,0xd3,0x20,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x10,
+ 0x04,0x0d,0x00,0x00,0x00,0xd1,0x08,0x10,0x04,0x0d,0x00,0x00,0x00,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0xd1,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x10,0x04,0x00,0x00,0x0d,
+ 0x00,0xd4,0x30,0xd3,0x20,0xd2,0x10,0xd1,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x10,
+ 0x04,0x0d,0x00,0x00,0x00,0xd1,0x08,0x10,0x04,0x0d,0x00,0x00,0x00,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0x92,0x0c,0x51,0x04,0x0d,0x00,0x10,0x04,0x0d,0x00,0x00,0x00,0x0d,
+ 0x00,0xd3,0x10,0x92,0x0c,0x51,0x04,0x0d,0x00,0x10,0x04,0x0d,0x00,0x00,0x00,0x0d,
+ 0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x0d,0x00,0xd1,0x08,0x10,
+ 0x04,0x0d,0x00,0x00,0x00,0x10,0x04,0x0d,0x00,0x00,0x00,0xd0,0x56,0xcf,0x86,0xd5,
+ 0x20,0xd4,0x14,0x53,0x04,0x0d,0x00,0x92,0x0c,0x51,0x04,0x0d,0x00,0x10,0x04,0x00,
+ 0x00,0x0d,0x00,0x0d,0x00,0x53,0x04,0x0d,0x00,0x12,0x04,0x0d,0x00,0x00,0x00,0xd4,
+ 0x28,0xd3,0x18,0xd2,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x0d,0x00,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x0d,0x00,0x0d,0x00,0x92,0x0c,0x51,0x04,0x0d,0x00,0x10,
+ 0x04,0x00,0x00,0x0d,0x00,0x0d,0x00,0x53,0x04,0x0d,0x00,0x12,0x04,0x0d,0x00,0x00,
+ 0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,0x93,0x0c,0x92,0x08,0x11,
+ 0x04,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x86,0xe5,
+ 0x96,0x05,0xe4,0x28,0x03,0xe3,0xed,0x01,0xd2,0xa0,0xd1,0x1c,0xd0,0x16,0xcf,0x86,
+ 0x55,0x04,0x0a,0x00,0x94,0x0c,0x53,0x04,0x0a,0x00,0x12,0x04,0x0a,0x00,0x00,0x00,
+ 0x0a,0x00,0xcf,0x06,0x0a,0x00,0xd0,0x46,0xcf,0x86,0xd5,0x10,0x54,0x04,0x0a,0x00,
+ 0x93,0x08,0x12,0x04,0x0a,0x00,0x00,0x00,0x00,0x00,0xd4,0x14,0x53,0x04,0x0c,0x00,
+ 0x52,0x04,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,0x00,0x00,0xd3,0x10,
+ 0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0c,0x00,0x0c,0x00,0x0c,0x00,0x52,0x04,
+ 0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,0x10,0x00,0xcf,0x86,0xd5,0x28,
+ 0xd4,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0c,0x00,0x0c,0x00,
+ 0x0c,0x00,0x0c,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x00,0x00,0x0c,0x00,
+ 0x0c,0x00,0x0c,0x00,0x0c,0x00,0x54,0x04,0x10,0x00,0x93,0x0c,0x52,0x04,0x10,0x00,
+ 0x11,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xd1,0xe4,0xd0,0x5a,0xcf,0x86,0xd5,0x20,
+ 0x94,0x1c,0x53,0x04,0x0b,0x00,0xd2,0x0c,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,
+ 0x10,0x00,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0xd4,0x14,
+ 0x53,0x04,0x0b,0x00,0x52,0x04,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,0x0b,0x00,
+ 0x14,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0c,0x00,0x0b,0x00,0x0c,0x00,
+ 0x0c,0x00,0x52,0x04,0x0c,0x00,0xd1,0x08,0x10,0x04,0x0c,0x00,0x0b,0x00,0x10,0x04,
+ 0x0c,0x00,0x0b,0x00,0xcf,0x86,0xd5,0x4c,0xd4,0x2c,0xd3,0x18,0xd2,0x0c,0x51,0x04,
+ 0x0c,0x00,0x10,0x04,0x0b,0x00,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0b,0x00,
+ 0x0c,0x00,0xd2,0x08,0x11,0x04,0x0c,0x00,0x0b,0x00,0x51,0x04,0x0b,0x00,0x10,0x04,
+ 0x0b,0x00,0x0c,0x00,0xd3,0x10,0x52,0x04,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,
+ 0x0c,0x00,0x0b,0x00,0x52,0x04,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,
+ 0x0b,0x00,0xd4,0x18,0x53,0x04,0x0c,0x00,0xd2,0x08,0x11,0x04,0x0c,0x00,0x0d,0x00,
+ 0x91,0x08,0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0x53,0x04,0x0c,0x00,0xd2,0x10,
+ 0xd1,0x08,0x10,0x04,0x0c,0x00,0x0b,0x00,0x10,0x04,0x0c,0x00,0x0b,0x00,0xd1,0x08,
+ 0x10,0x04,0x0b,0x00,0x0c,0x00,0x10,0x04,0x0c,0x00,0x0b,0x00,0xd0,0x4e,0xcf,0x86,
+ 0xd5,0x34,0xd4,0x14,0x53,0x04,0x0c,0x00,0xd2,0x08,0x11,0x04,0x0c,0x00,0x0b,0x00,
+ 0x11,0x04,0x0b,0x00,0x0c,0x00,0xd3,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0b,0x00,
+ 0x0c,0x00,0x0c,0x00,0x0c,0x00,0x92,0x0c,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,
+ 0x12,0x00,0x12,0x00,0x94,0x14,0x53,0x04,0x12,0x00,0x52,0x04,0x12,0x00,0x91,0x08,
+ 0x10,0x04,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,
+ 0x94,0x10,0x93,0x0c,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x0c,0x00,0x0c,0x00,
+ 0x0c,0x00,0xd2,0x7e,0xd1,0x78,0xd0,0x3e,0xcf,0x86,0xd5,0x1c,0x94,0x18,0x93,0x14,
+ 0x92,0x10,0xd1,0x08,0x10,0x04,0x0b,0x00,0x0c,0x00,0x10,0x04,0x0c,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x0b,0x00,0x54,0x04,0x0b,0x00,0xd3,0x0c,0x92,0x08,0x11,0x04,
+ 0x0b,0x00,0x0c,0x00,0x0c,0x00,0x92,0x0c,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,
+ 0x12,0x00,0x00,0x00,0xcf,0x86,0xd5,0x24,0xd4,0x14,0x53,0x04,0x0b,0x00,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93,0x0c,0x92,0x08,
+ 0x11,0x04,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x10,0x93,0x0c,0x52,0x04,
+ 0x13,0x00,0x11,0x04,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,
+ 0xd1,0x58,0xd0,0x3a,0xcf,0x86,0x55,0x04,0x0c,0x00,0xd4,0x20,0xd3,0x10,0x92,0x0c,
+ 0x91,0x08,0x10,0x04,0x0c,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x52,0x04,0x10,0x00,
+ 0x91,0x08,0x10,0x04,0x10,0x00,0x11,0x00,0x11,0x00,0x93,0x10,0x52,0x04,0x0c,0x00,
+ 0x51,0x04,0x0c,0x00,0x10,0x04,0x10,0x00,0x0c,0x00,0x0c,0x00,0xcf,0x86,0x55,0x04,
+ 0x0c,0x00,0x54,0x04,0x0c,0x00,0x53,0x04,0x0c,0x00,0x52,0x04,0x0c,0x00,0x91,0x08,
+ 0x10,0x04,0x0c,0x00,0x10,0x00,0x11,0x00,0xd0,0x16,0xcf,0x86,0x95,0x10,0x54,0x04,
+ 0x0c,0x00,0x93,0x08,0x12,0x04,0x0c,0x00,0x10,0x00,0x10,0x00,0x0c,0x00,0xcf,0x86,
+ 0xd5,0x34,0xd4,0x28,0xd3,0x10,0x52,0x04,0x0c,0x00,0x91,0x08,0x10,0x04,0x0c,0x00,
+ 0x10,0x00,0x0c,0x00,0xd2,0x0c,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,0x10,0x00,
+ 0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x11,0x00,0x93,0x08,0x12,0x04,0x11,0x00,
+ 0x10,0x00,0x10,0x00,0x54,0x04,0x0c,0x00,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,
+ 0x0c,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x11,0x00,0xd3,0xfc,0xd2,0x6c,0xd1,0x3c,
+ 0xd0,0x1e,0xcf,0x86,0x55,0x04,0x0c,0x00,0x54,0x04,0x0c,0x00,0x53,0x04,0x0c,0x00,
+ 0x52,0x04,0x0c,0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,0x10,0x00,0xcf,0x86,
+ 0x95,0x18,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0c,0x00,0x10,0x00,
+ 0x0c,0x00,0x0c,0x00,0x0c,0x00,0x0c,0x00,0x0c,0x00,0xd0,0x06,0xcf,0x06,0x0c,0x00,
+ 0xcf,0x86,0x55,0x04,0x0c,0x00,0x54,0x04,0x0c,0x00,0x53,0x04,0x0c,0x00,0xd2,0x0c,
+ 0x91,0x08,0x10,0x04,0x10,0x00,0x0c,0x00,0x0c,0x00,0xd1,0x08,0x10,0x04,0x0c,0x00,
+ 0x10,0x00,0x10,0x04,0x10,0x00,0x11,0x00,0xd1,0x54,0xd0,0x1a,0xcf,0x86,0x55,0x04,
+ 0x0c,0x00,0x54,0x04,0x0c,0x00,0x53,0x04,0x0c,0x00,0x52,0x04,0x0c,0x00,0x11,0x04,
+ 0x0c,0x00,0x10,0x00,0xcf,0x86,0xd5,0x1c,0x94,0x18,0xd3,0x08,0x12,0x04,0x0d,0x00,
+ 0x10,0x00,0x92,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,0x10,0x00,0x11,0x00,0x11,0x00,
+ 0x0c,0x00,0xd4,0x08,0x13,0x04,0x0c,0x00,0x10,0x00,0x53,0x04,0x10,0x00,0x92,0x0c,
+ 0x51,0x04,0x10,0x00,0x10,0x04,0x12,0x00,0x10,0x00,0x10,0x00,0xd0,0x1e,0xcf,0x86,
+ 0x55,0x04,0x10,0x00,0x94,0x14,0x93,0x10,0x52,0x04,0x10,0x00,0x91,0x08,0x10,0x04,
+ 0x12,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0xcf,0x86,0x55,0x04,0x10,0x00,
+ 0x54,0x04,0x10,0x00,0x53,0x04,0x10,0x00,0x92,0x0c,0x51,0x04,0x10,0x00,0x10,0x04,
+ 0x10,0x00,0x0c,0x00,0x0c,0x00,0xe2,0x19,0x01,0xd1,0xa8,0xd0,0x7e,0xcf,0x86,0xd5,
+ 0x4c,0xd4,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x0d,0x00,0x0c,0x00,0x0c,
+ 0x00,0x0c,0x00,0x0c,0x00,0xd3,0x1c,0xd2,0x0c,0x91,0x08,0x10,0x04,0x0c,0x00,0x0d,
+ 0x00,0x0c,0x00,0xd1,0x08,0x10,0x04,0x0c,0x00,0x0d,0x00,0x10,0x04,0x0c,0x00,0x0d,
+ 0x00,0xd2,0x10,0xd1,0x08,0x10,0x04,0x0c,0x00,0x0d,0x00,0x10,0x04,0x0c,0x00,0x0d,
+ 0x00,0x51,0x04,0x0c,0x00,0x10,0x04,0x0c,0x00,0x0d,0x00,0xd4,0x1c,0xd3,0x0c,0x52,
+ 0x04,0x0c,0x00,0x11,0x04,0x0c,0x00,0x0d,0x00,0x52,0x04,0x0c,0x00,0x91,0x08,0x10,
+ 0x04,0x0d,0x00,0x0c,0x00,0x0d,0x00,0x93,0x10,0x52,0x04,0x0c,0x00,0x91,0x08,0x10,
+ 0x04,0x0d,0x00,0x0c,0x00,0x0c,0x00,0x0c,0x00,0xcf,0x86,0x95,0x24,0x94,0x20,0x93,
+ 0x1c,0xd2,0x10,0xd1,0x08,0x10,0x04,0x0c,0x00,0x10,0x00,0x10,0x04,0x10,0x00,0x11,
+ 0x00,0x91,0x08,0x10,0x04,0x11,0x00,0x0c,0x00,0x0c,0x00,0x0c,0x00,0x10,0x00,0x10,
+ 0x00,0xd0,0x06,0xcf,0x06,0x0c,0x00,0xcf,0x86,0xd5,0x30,0xd4,0x10,0x93,0x0c,0x52,
+ 0x04,0x0c,0x00,0x11,0x04,0x0c,0x00,0x10,0x00,0x10,0x00,0x93,0x1c,0xd2,0x10,0xd1,
+ 0x08,0x10,0x04,0x11,0x00,0x12,0x00,0x10,0x04,0x12,0x00,0x13,0x00,0x91,0x08,0x10,
+ 0x04,0x13,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0xd4,0x14,0x53,0x04,0x10,0x00,0x52,
+ 0x04,0x10,0x00,0x91,0x08,0x10,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0xd3,0x10,0x52,
+ 0x04,0x10,0x00,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x13,0x00,0x92,0x10,0xd1,
+ 0x08,0x10,0x04,0x13,0x00,0x14,0x00,0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0xd1,
+ 0x1c,0xd0,0x06,0xcf,0x06,0x0c,0x00,0xcf,0x86,0x55,0x04,0x0c,0x00,0x54,0x04,0x0c,
+ 0x00,0x93,0x08,0x12,0x04,0x0c,0x00,0x00,0x00,0x00,0x00,0xd0,0x06,0xcf,0x06,0x10,
+ 0x00,0xcf,0x86,0xd5,0x24,0x54,0x04,0x10,0x00,0xd3,0x10,0x52,0x04,0x10,0x00,0x91,
+ 0x08,0x10,0x04,0x10,0x00,0x14,0x00,0x14,0x00,0x92,0x0c,0x91,0x08,0x10,0x04,0x14,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94,0x0c,0x53,0x04,0x15,0x00,0x12,0x04,0x15,
+ 0x00,0x00,0x00,0x00,0x00,0xe4,0x40,0x02,0xe3,0xc9,0x01,0xd2,0x5c,0xd1,0x34,0xd0,
+ 0x16,0xcf,0x86,0x95,0x10,0x94,0x0c,0x53,0x04,0x10,0x00,0x12,0x04,0x10,0x00,0x00,
+ 0x00,0x10,0x00,0x10,0x00,0xcf,0x86,0x95,0x18,0xd4,0x08,0x13,0x04,0x10,0x00,0x00,
+ 0x00,0x53,0x04,0x10,0x00,0x92,0x08,0x11,0x04,0x10,0x00,0x00,0x00,0x00,0x00,0x10,
+ 0x00,0xd0,0x22,0xcf,0x86,0xd5,0x0c,0x94,0x08,0x13,0x04,0x10,0x00,0x00,0x00,0x10,
+ 0x00,0x94,0x10,0x53,0x04,0x10,0x00,0x52,0x04,0x10,0x00,0x11,0x04,0x10,0x00,0x00,
+ 0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xd1,0xc0,0xd0,0x5e,0xcf,0x86,0xd5,0x30,0xd4,
+ 0x14,0x53,0x04,0x13,0x00,0x52,0x04,0x13,0x00,0x91,0x08,0x10,0x04,0x00,0x00,0x15,
+ 0x00,0x15,0x00,0x53,0x04,0x11,0x00,0xd2,0x0c,0x91,0x08,0x10,0x04,0x11,0x00,0x12,
+ 0x00,0x12,0x00,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x13,0x00,0xd4,0x08,0x13,
+ 0x04,0x12,0x00,0x13,0x00,0xd3,0x14,0x92,0x10,0xd1,0x08,0x10,0x04,0x12,0x00,0x13,
+ 0x00,0x10,0x04,0x13,0x00,0x12,0x00,0x12,0x00,0x52,0x04,0x12,0x00,0x51,0x04,0x12,
+ 0x00,0x10,0x04,0x12,0x00,0x15,0x00,0xcf,0x86,0xd5,0x28,0xd4,0x14,0x53,0x04,0x12,
+ 0x00,0x52,0x04,0x12,0x00,0x91,0x08,0x10,0x04,0x13,0x00,0x14,0x00,0x14,0x00,0x53,
+ 0x04,0x12,0x00,0x52,0x04,0x12,0x00,0x51,0x04,0x12,0x00,0x10,0x04,0x12,0x00,0x13,
+ 0x00,0xd4,0x0c,0x53,0x04,0x13,0x00,0x12,0x04,0x13,0x00,0x14,0x00,0xd3,0x1c,0xd2,
+ 0x10,0xd1,0x08,0x10,0x04,0x14,0x00,0x15,0x00,0x10,0x04,0x00,0x00,0x14,0x00,0x51,
+ 0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x00,0x00,0x92,0x0c,0x51,0x04,0x00,0x00,0x10,
+ 0x04,0x14,0x00,0x15,0x00,0x14,0x00,0xd0,0x62,0xcf,0x86,0xd5,0x24,0xd4,0x14,0x93,
+ 0x10,0x52,0x04,0x11,0x00,0x91,0x08,0x10,0x04,0x11,0x00,0x12,0x00,0x12,0x00,0x12,
+ 0x00,0x93,0x0c,0x92,0x08,0x11,0x04,0x12,0x00,0x13,0x00,0x13,0x00,0x14,0x00,0xd4,
+ 0x2c,0xd3,0x18,0xd2,0x0c,0x51,0x04,0x14,0x00,0x10,0x04,0x14,0x00,0x00,0x00,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x15,0x00,0x15,0x00,0xd2,0x0c,0x51,0x04,0x15,0x00,0x10,
+ 0x04,0x15,0x00,0x00,0x00,0x11,0x04,0x00,0x00,0x15,0x00,0x53,0x04,0x14,0x00,0x92,
+ 0x08,0x11,0x04,0x14,0x00,0x15,0x00,0x15,0x00,0xcf,0x86,0xd5,0x30,0x94,0x2c,0xd3,
+ 0x14,0x92,0x10,0xd1,0x08,0x10,0x04,0x11,0x00,0x14,0x00,0x10,0x04,0x14,0x00,0x15,
+ 0x00,0x15,0x00,0xd2,0x0c,0x51,0x04,0x15,0x00,0x10,0x04,0x15,0x00,0x00,0x00,0x91,
+ 0x08,0x10,0x04,0x00,0x00,0x15,0x00,0x15,0x00,0x13,0x00,0x94,0x14,0x93,0x10,0x52,
+ 0x04,0x13,0x00,0x51,0x04,0x13,0x00,0x10,0x04,0x13,0x00,0x14,0x00,0x14,0x00,0x14,
+ 0x00,0xd2,0x70,0xd1,0x40,0xd0,0x06,0xcf,0x06,0x15,0x00,0xcf,0x86,0xd5,0x10,0x54,
+ 0x04,0x15,0x00,0x93,0x08,0x12,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0xd4,0x10,0x53,
+ 0x04,0x14,0x00,0x52,0x04,0x14,0x00,0x11,0x04,0x14,0x00,0x00,0x00,0xd3,0x08,0x12,
+ 0x04,0x15,0x00,0x00,0x00,0x92,0x0c,0x51,0x04,0x15,0x00,0x10,0x04,0x15,0x00,0x00,
+ 0x00,0x00,0x00,0xd0,0x2a,0xcf,0x86,0x95,0x24,0xd4,0x14,0x93,0x10,0x92,0x0c,0x51,
+ 0x04,0x15,0x00,0x10,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x93,0x0c,0x52,
+ 0x04,0x15,0x00,0x11,0x04,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,
+ 0x00,0xcf,0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,
+ 0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,
+ 0x04,0x00,0x00,0x54,0x04,0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,
+ 0x04,0x00,0x00,0x02,0x00,0xe4,0xf9,0x12,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x05,0x00,
+ 0xd2,0xc2,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x05,0x00,0xd0,0x44,0xcf,0x86,0xd5,0x3c,
+ 0xd4,0x06,0xcf,0x06,0x05,0x00,0xd3,0x06,0xcf,0x06,0x05,0x00,0xd2,0x2a,0xd1,0x06,
+ 0xcf,0x06,0x05,0x00,0xd0,0x06,0xcf,0x06,0x05,0x00,0xcf,0x86,0x95,0x18,0x54,0x04,
+ 0x05,0x00,0x93,0x10,0x52,0x04,0x05,0x00,0x51,0x04,0x05,0x00,0x10,0x04,0x05,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x0b,0x00,0xcf,0x06,0x0b,0x00,0xcf,0x86,
+ 0xd5,0x3c,0xd4,0x06,0xcf,0x06,0x0b,0x00,0xd3,0x06,0xcf,0x06,0x0b,0x00,0xd2,0x06,
+ 0xcf,0x06,0x0b,0x00,0xd1,0x24,0xd0,0x1e,0xcf,0x86,0x55,0x04,0x0b,0x00,0x54,0x04,
+ 0x0b,0x00,0x93,0x10,0x52,0x04,0x0b,0x00,0x91,0x08,0x10,0x04,0x0b,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0xcf,0x06,0x0c,0x00,0xcf,0x06,0x0c,0x00,0xd4,0x32,0xd3,0x2c,
+ 0xd2,0x26,0xd1,0x20,0xd0,0x1a,0xcf,0x86,0x95,0x14,0x54,0x04,0x0c,0x00,0x53,0x04,
+ 0x0c,0x00,0x52,0x04,0x0c,0x00,0x11,0x04,0x0c,0x00,0x00,0x00,0x11,0x00,0xcf,0x06,
+ 0x11,0x00,0xcf,0x06,0x11,0x00,0xcf,0x06,0x11,0x00,0xcf,0x06,0x11,0x00,0xcf,0x06,
+ 0x11,0x00,0xd1,0x48,0xd0,0x40,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x11,0x00,0xd4,0x06,
+ 0xcf,0x06,0x11,0x00,0xd3,0x06,0xcf,0x06,0x11,0x00,0xd2,0x26,0xd1,0x06,0xcf,0x06,
+ 0x11,0x00,0xd0,0x1a,0xcf,0x86,0x55,0x04,0x11,0x00,0x94,0x10,0x93,0x0c,0x92,0x08,
+ 0x11,0x04,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0xcf,0x06,0x13,0x00,
+ 0xcf,0x06,0x13,0x00,0xcf,0x86,0xcf,0x06,0x13,0x00,0xd0,0x44,0xcf,0x86,0xd5,0x06,
+ 0xcf,0x06,0x13,0x00,0xd4,0x36,0xd3,0x06,0xcf,0x06,0x13,0x00,0xd2,0x06,0xcf,0x06,
+ 0x13,0x00,0xd1,0x06,0xcf,0x06,0x13,0x00,0xd0,0x06,0xcf,0x06,0x13,0x00,0xcf,0x86,
+ 0x55,0x04,0x13,0x00,0x94,0x14,0x93,0x10,0x92,0x0c,0x91,0x08,0x10,0x04,0x13,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x86,
+ 0xd5,0x06,0xcf,0x06,0x00,0x00,0xe4,0x68,0x11,0xe3,0x51,0x10,0xe2,0x17,0x08,0xe1,
+ 0x06,0x04,0xe0,0x03,0x02,0xcf,0x86,0xe5,0x06,0x01,0xd4,0x82,0xd3,0x41,0xd2,0x21,
+ 0xd1,0x10,0x10,0x08,0x05,0xff,0xe4,0xb8,0xbd,0x00,0x05,0xff,0xe4,0xb8,0xb8,0x00,
+ 0x10,0x08,0x05,0xff,0xe4,0xb9,0x81,0x00,0x05,0xff,0xf0,0xa0,0x84,0xa2,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe4,0xbd,0xa0,0x00,0x05,0xff,0xe4,0xbe,0xae,0x00,0x10,
+ 0x08,0x05,0xff,0xe4,0xbe,0xbb,0x00,0x05,0xff,0xe5,0x80,0x82,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe5,0x81,0xba,0x00,0x05,0xff,0xe5,0x82,0x99,0x00,0x10,
+ 0x08,0x05,0xff,0xe5,0x83,0xa7,0x00,0x05,0xff,0xe5,0x83,0x8f,0x00,0xd1,0x11,0x10,
+ 0x08,0x05,0xff,0xe3,0x92,0x9e,0x00,0x05,0xff,0xf0,0xa0,0x98,0xba,0x00,0x10,0x08,
+ 0x05,0xff,0xe5,0x85,0x8d,0x00,0x05,0xff,0xe5,0x85,0x94,0x00,0xd3,0x42,0xd2,0x21,
+ 0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0x85,0xa4,0x00,0x05,0xff,0xe5,0x85,0xb7,0x00,
+ 0x10,0x09,0x05,0xff,0xf0,0xa0,0x94,0x9c,0x00,0x05,0xff,0xe3,0x92,0xb9,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe5,0x85,0xa7,0x00,0x05,0xff,0xe5,0x86,0x8d,0x00,0x10,
+ 0x09,0x05,0xff,0xf0,0xa0,0x95,0x8b,0x00,0x05,0xff,0xe5,0x86,0x97,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0x86,0xa4,0x00,0x05,0xff,0xe4,0xbb,0x8c,0x00,
+ 0x10,0x08,0x05,0xff,0xe5,0x86,0xac,0x00,0x05,0xff,0xe5,0x86,0xb5,0x00,0xd1,0x11,
+ 0x10,0x09,0x05,0xff,0xf0,0xa9,0x87,0x9f,0x00,0x05,0xff,0xe5,0x87,0xb5,0x00,0x10,
+ 0x08,0x05,0xff,0xe5,0x88,0x83,0x00,0x05,0xff,0xe3,0x93,0x9f,0x00,0xd4,0x80,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0x88,0xbb,0x00,0x05,0xff,0xe5,
+ 0x89,0x86,0x00,0x10,0x08,0x05,0xff,0xe5,0x89,0xb2,0x00,0x05,0xff,0xe5,0x89,0xb7,
+ 0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe3,0x94,0x95,0x00,0x05,0xff,0xe5,0x8b,0x87,
+ 0x00,0x10,0x08,0x05,0xff,0xe5,0x8b,0x89,0x00,0x05,0xff,0xe5,0x8b,0xa4,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0x8b,0xba,0x00,0x05,0xff,0xe5,0x8c,0x85,
+ 0x00,0x10,0x08,0x05,0xff,0xe5,0x8c,0x86,0x00,0x05,0xff,0xe5,0x8c,0x97,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe5,0x8d,0x89,0x00,0x05,0xff,0xe5,0x8d,0x91,0x00,0x10,
+ 0x08,0x05,0xff,0xe5,0x8d,0x9a,0x00,0x05,0xff,0xe5,0x8d,0xb3,0x00,0xd3,0x39,0xd2,
+ 0x18,0x91,0x10,0x10,0x08,0x05,0xff,0xe5,0x8d,0xbd,0x00,0x05,0xff,0xe5,0x8d,0xbf,
+ 0x00,0x05,0xff,0xe5,0x8d,0xbf,0x00,0xd1,0x11,0x10,0x09,0x05,0xff,0xf0,0xa0,0xa8,
+ 0xac,0x00,0x05,0xff,0xe7,0x81,0xb0,0x00,0x10,0x08,0x05,0xff,0xe5,0x8f,0x8a,0x00,
+ 0x05,0xff,0xe5,0x8f,0x9f,0x00,0xd2,0x21,0xd1,0x11,0x10,0x09,0x05,0xff,0xf0,0xa0,
+ 0xad,0xa3,0x00,0x05,0xff,0xe5,0x8f,0xab,0x00,0x10,0x08,0x05,0xff,0xe5,0x8f,0xb1,
+ 0x00,0x05,0xff,0xe5,0x90,0x86,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0x92,0x9e,
+ 0x00,0x05,0xff,0xe5,0x90,0xb8,0x00,0x10,0x08,0x05,0xff,0xe5,0x91,0x88,0x00,0x05,
+ 0xff,0xe5,0x91,0xa8,0x00,0xcf,0x86,0xe5,0x02,0x01,0xd4,0x80,0xd3,0x40,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0x92,0xa2,0x00,0x05,0xff,0xe5,0x93,0xb6,0x00,
+ 0x10,0x08,0x05,0xff,0xe5,0x94,0x90,0x00,0x05,0xff,0xe5,0x95,0x93,0x00,0xd1,0x10,
+ 0x10,0x08,0x05,0xff,0xe5,0x95,0xa3,0x00,0x05,0xff,0xe5,0x96,0x84,0x00,0x10,0x08,
+ 0x05,0xff,0xe5,0x96,0x84,0x00,0x05,0xff,0xe5,0x96,0x99,0x00,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x05,0xff,0xe5,0x96,0xab,0x00,0x05,0xff,0xe5,0x96,0xb3,0x00,0x10,0x08,
+ 0x05,0xff,0xe5,0x97,0x82,0x00,0x05,0xff,0xe5,0x9c,0x96,0x00,0xd1,0x10,0x10,0x08,
+ 0x05,0xff,0xe5,0x98,0x86,0x00,0x05,0xff,0xe5,0x9c,0x97,0x00,0x10,0x08,0x05,0xff,
+ 0xe5,0x99,0x91,0x00,0x05,0xff,0xe5,0x99,0xb4,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x05,0xff,0xe5,0x88,0x87,0x00,0x05,0xff,0xe5,0xa3,0xae,0x00,0x10,0x08,
+ 0x05,0xff,0xe5,0x9f,0x8e,0x00,0x05,0xff,0xe5,0x9f,0xb4,0x00,0xd1,0x10,0x10,0x08,
+ 0x05,0xff,0xe5,0xa0,0x8d,0x00,0x05,0xff,0xe5,0x9e,0x8b,0x00,0x10,0x08,0x05,0xff,
+ 0xe5,0xa0,0xb2,0x00,0x05,0xff,0xe5,0xa0,0xb1,0x00,0xd2,0x21,0xd1,0x11,0x10,0x08,
+ 0x05,0xff,0xe5,0xa2,0xac,0x00,0x05,0xff,0xf0,0xa1,0x93,0xa4,0x00,0x10,0x08,0x05,
+ 0xff,0xe5,0xa3,0xb2,0x00,0x05,0xff,0xe5,0xa3,0xb7,0x00,0xd1,0x10,0x10,0x08,0x05,
+ 0xff,0xe5,0xa4,0x86,0x00,0x05,0xff,0xe5,0xa4,0x9a,0x00,0x10,0x08,0x05,0xff,0xe5,
+ 0xa4,0xa2,0x00,0x05,0xff,0xe5,0xa5,0xa2,0x00,0xd4,0x7b,0xd3,0x42,0xd2,0x22,0xd1,
+ 0x12,0x10,0x09,0x05,0xff,0xf0,0xa1,0x9a,0xa8,0x00,0x05,0xff,0xf0,0xa1,0x9b,0xaa,
+ 0x00,0x10,0x08,0x05,0xff,0xe5,0xa7,0xac,0x00,0x05,0xff,0xe5,0xa8,0x9b,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe5,0xa8,0xa7,0x00,0x05,0xff,0xe5,0xa7,0x98,0x00,0x10,
+ 0x08,0x05,0xff,0xe5,0xa9,0xa6,0x00,0x05,0xff,0xe3,0x9b,0xae,0x00,0xd2,0x18,0x91,
+ 0x10,0x10,0x08,0x05,0xff,0xe3,0x9b,0xbc,0x00,0x05,0xff,0xe5,0xac,0x88,0x00,0x05,
+ 0xff,0xe5,0xac,0xbe,0x00,0xd1,0x11,0x10,0x09,0x05,0xff,0xf0,0xa1,0xa7,0x88,0x00,
+ 0x05,0xff,0xe5,0xaf,0x83,0x00,0x10,0x08,0x05,0xff,0xe5,0xaf,0x98,0x00,0x05,0xff,
+ 0xe5,0xaf,0xa7,0x00,0xd3,0x41,0xd2,0x21,0xd1,0x11,0x10,0x08,0x05,0xff,0xe5,0xaf,
+ 0xb3,0x00,0x05,0xff,0xf0,0xa1,0xac,0x98,0x00,0x10,0x08,0x05,0xff,0xe5,0xaf,0xbf,
+ 0x00,0x05,0xff,0xe5,0xb0,0x86,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0xbd,0x93,
+ 0x00,0x05,0xff,0xe5,0xb0,0xa2,0x00,0x10,0x08,0x05,0xff,0xe3,0x9e,0x81,0x00,0x05,
+ 0xff,0xe5,0xb1,0xa0,0x00,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0xb1,0xae,
+ 0x00,0x05,0xff,0xe5,0xb3,0x80,0x00,0x10,0x08,0x05,0xff,0xe5,0xb2,0x8d,0x00,0x05,
+ 0xff,0xf0,0xa1,0xb7,0xa4,0x00,0xd1,0x11,0x10,0x08,0x05,0xff,0xe5,0xb5,0x83,0x00,
+ 0x05,0xff,0xf0,0xa1,0xb7,0xa6,0x00,0x10,0x08,0x05,0xff,0xe5,0xb5,0xae,0x00,0x05,
+ 0xff,0xe5,0xb5,0xab,0x00,0xe0,0x04,0x02,0xcf,0x86,0xd5,0xfe,0xd4,0x82,0xd3,0x40,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0xb5,0xbc,0x00,0x05,0xff,0xe5,0xb7,
+ 0xa1,0x00,0x10,0x08,0x05,0xff,0xe5,0xb7,0xa2,0x00,0x05,0xff,0xe3,0xa0,0xaf,0x00,
+ 0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0xb7,0xbd,0x00,0x05,0xff,0xe5,0xb8,0xa8,0x00,
+ 0x10,0x08,0x05,0xff,0xe5,0xb8,0xbd,0x00,0x05,0xff,0xe5,0xb9,0xa9,0x00,0xd2,0x21,
+ 0xd1,0x11,0x10,0x08,0x05,0xff,0xe3,0xa1,0xa2,0x00,0x05,0xff,0xf0,0xa2,0x86,0x83,
+ 0x00,0x10,0x08,0x05,0xff,0xe3,0xa1,0xbc,0x00,0x05,0xff,0xe5,0xba,0xb0,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe5,0xba,0xb3,0x00,0x05,0xff,0xe5,0xba,0xb6,0x00,0x10,
+ 0x08,0x05,0xff,0xe5,0xbb,0x8a,0x00,0x05,0xff,0xf0,0xaa,0x8e,0x92,0x00,0xd3,0x3b,
+ 0xd2,0x22,0xd1,0x11,0x10,0x08,0x05,0xff,0xe5,0xbb,0xbe,0x00,0x05,0xff,0xf0,0xa2,
+ 0x8c,0xb1,0x00,0x10,0x09,0x05,0xff,0xf0,0xa2,0x8c,0xb1,0x00,0x05,0xff,0xe8,0x88,
+ 0x81,0x00,0x51,0x08,0x05,0xff,0xe5,0xbc,0xa2,0x00,0x10,0x08,0x05,0xff,0xe3,0xa3,
+ 0x87,0x00,0x05,0xff,0xf0,0xa3,0x8a,0xb8,0x00,0xd2,0x21,0xd1,0x11,0x10,0x09,0x05,
+ 0xff,0xf0,0xa6,0x87,0x9a,0x00,0x05,0xff,0xe5,0xbd,0xa2,0x00,0x10,0x08,0x05,0xff,
+ 0xe5,0xbd,0xab,0x00,0x05,0xff,0xe3,0xa3,0xa3,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,
+ 0xe5,0xbe,0x9a,0x00,0x05,0xff,0xe5,0xbf,0x8d,0x00,0x10,0x08,0x05,0xff,0xe5,0xbf,
+ 0x97,0x00,0x05,0xff,0xe5,0xbf,0xb9,0x00,0xd4,0x81,0xd3,0x41,0xd2,0x20,0xd1,0x10,
+ 0x10,0x08,0x05,0xff,0xe6,0x82,0x81,0x00,0x05,0xff,0xe3,0xa4,0xba,0x00,0x10,0x08,
+ 0x05,0xff,0xe3,0xa4,0x9c,0x00,0x05,0xff,0xe6,0x82,0x94,0x00,0xd1,0x11,0x10,0x09,
+ 0x05,0xff,0xf0,0xa2,0x9b,0x94,0x00,0x05,0xff,0xe6,0x83,0x87,0x00,0x10,0x08,0x05,
+ 0xff,0xe6,0x85,0x88,0x00,0x05,0xff,0xe6,0x85,0x8c,0x00,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x05,0xff,0xe6,0x85,0x8e,0x00,0x05,0xff,0xe6,0x85,0x8c,0x00,0x10,0x08,0x05,
+ 0xff,0xe6,0x85,0xba,0x00,0x05,0xff,0xe6,0x86,0x8e,0x00,0xd1,0x10,0x10,0x08,0x05,
+ 0xff,0xe6,0x86,0xb2,0x00,0x05,0xff,0xe6,0x86,0xa4,0x00,0x10,0x08,0x05,0xff,0xe6,
+ 0x86,0xaf,0x00,0x05,0xff,0xe6,0x87,0x9e,0x00,0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,
+ 0x08,0x05,0xff,0xe6,0x87,0xb2,0x00,0x05,0xff,0xe6,0x87,0xb6,0x00,0x10,0x08,0x05,
+ 0xff,0xe6,0x88,0x90,0x00,0x05,0xff,0xe6,0x88,0x9b,0x00,0xd1,0x10,0x10,0x08,0x05,
+ 0xff,0xe6,0x89,0x9d,0x00,0x05,0xff,0xe6,0x8a,0xb1,0x00,0x10,0x08,0x05,0xff,0xe6,
+ 0x8b,0x94,0x00,0x05,0xff,0xe6,0x8d,0x90,0x00,0xd2,0x21,0xd1,0x11,0x10,0x09,0x05,
+ 0xff,0xf0,0xa2,0xac,0x8c,0x00,0x05,0xff,0xe6,0x8c,0xbd,0x00,0x10,0x08,0x05,0xff,
+ 0xe6,0x8b,0xbc,0x00,0x05,0xff,0xe6,0x8d,0xa8,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,
+ 0xe6,0x8e,0x83,0x00,0x05,0xff,0xe6,0x8f,0xa4,0x00,0x10,0x09,0x05,0xff,0xf0,0xa2,
+ 0xaf,0xb1,0x00,0x05,0xff,0xe6,0x90,0xa2,0x00,0xcf,0x86,0xe5,0x03,0x01,0xd4,0x81,
+ 0xd3,0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x8f,0x85,0x00,0x05,0xff,
+ 0xe6,0x8e,0xa9,0x00,0x10,0x08,0x05,0xff,0xe3,0xa8,0xae,0x00,0x05,0xff,0xe6,0x91,
+ 0xa9,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x91,0xbe,0x00,0x05,0xff,0xe6,0x92,
+ 0x9d,0x00,0x10,0x08,0x05,0xff,0xe6,0x91,0xb7,0x00,0x05,0xff,0xe3,0xa9,0xac,0x00,
+ 0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x95,0x8f,0x00,0x05,0xff,0xe6,0x95,
+ 0xac,0x00,0x10,0x09,0x05,0xff,0xf0,0xa3,0x80,0x8a,0x00,0x05,0xff,0xe6,0x97,0xa3,
+ 0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x9b,0xb8,0x00,0x05,0xff,0xe6,0x99,0x89,
+ 0x00,0x10,0x08,0x05,0xff,0xe3,0xac,0x99,0x00,0x05,0xff,0xe6,0x9a,0x91,0x00,0xd3,
+ 0x40,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe3,0xac,0x88,0x00,0x05,0xff,0xe3,
+ 0xab,0xa4,0x00,0x10,0x08,0x05,0xff,0xe5,0x86,0x92,0x00,0x05,0xff,0xe5,0x86,0x95,
+ 0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x9c,0x80,0x00,0x05,0xff,0xe6,0x9a,0x9c,
+ 0x00,0x10,0x08,0x05,0xff,0xe8,0x82,0xad,0x00,0x05,0xff,0xe4,0x8f,0x99,0x00,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x9c,0x97,0x00,0x05,0xff,0xe6,0x9c,0x9b,
+ 0x00,0x10,0x08,0x05,0xff,0xe6,0x9c,0xa1,0x00,0x05,0xff,0xe6,0x9d,0x9e,0x00,0xd1,
+ 0x11,0x10,0x08,0x05,0xff,0xe6,0x9d,0x93,0x00,0x05,0xff,0xf0,0xa3,0x8f,0x83,0x00,
+ 0x10,0x08,0x05,0xff,0xe3,0xad,0x89,0x00,0x05,0xff,0xe6,0x9f,0xba,0x00,0xd4,0x82,
+ 0xd3,0x41,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0x9e,0x85,0x00,0x05,0xff,
+ 0xe6,0xa1,0x92,0x00,0x10,0x08,0x05,0xff,0xe6,0xa2,0x85,0x00,0x05,0xff,0xf0,0xa3,
+ 0x91,0xad,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xa2,0x8e,0x00,0x05,0xff,0xe6,
+ 0xa0,0x9f,0x00,0x10,0x08,0x05,0xff,0xe6,0xa4,0x94,0x00,0x05,0xff,0xe3,0xae,0x9d,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xa5,0x82,0x00,0x05,0xff,0xe6,
+ 0xa6,0xa3,0x00,0x10,0x08,0x05,0xff,0xe6,0xa7,0xaa,0x00,0x05,0xff,0xe6,0xaa,0xa8,
+ 0x00,0xd1,0x11,0x10,0x09,0x05,0xff,0xf0,0xa3,0x9a,0xa3,0x00,0x05,0xff,0xe6,0xab,
+ 0x9b,0x00,0x10,0x08,0x05,0xff,0xe3,0xb0,0x98,0x00,0x05,0xff,0xe6,0xac,0xa1,0x00,
+ 0xd3,0x42,0xd2,0x21,0xd1,0x11,0x10,0x09,0x05,0xff,0xf0,0xa3,0xa2,0xa7,0x00,0x05,
+ 0xff,0xe6,0xad,0x94,0x00,0x10,0x08,0x05,0xff,0xe3,0xb1,0x8e,0x00,0x05,0xff,0xe6,
+ 0xad,0xb2,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xae,0x9f,0x00,0x05,0xff,0xe6,
+ 0xae,0xba,0x00,0x10,0x08,0x05,0xff,0xe6,0xae,0xbb,0x00,0x05,0xff,0xf0,0xa3,0xaa,
+ 0x8d,0x00,0xd2,0x23,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa1,0xb4,0x8b,0x00,0x05,
+ 0xff,0xf0,0xa3,0xab,0xba,0x00,0x10,0x08,0x05,0xff,0xe6,0xb1,0x8e,0x00,0x05,0xff,
+ 0xf0,0xa3,0xb2,0xbc,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xb2,0xbf,0x00,0x05,
+ 0xff,0xe6,0xb3,0x8d,0x00,0x10,0x08,0x05,0xff,0xe6,0xb1,0xa7,0x00,0x05,0xff,0xe6,
+ 0xb4,0x96,0x00,0xe1,0x1d,0x04,0xe0,0x0c,0x02,0xcf,0x86,0xe5,0x08,0x01,0xd4,0x82,
+ 0xd3,0x41,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xb4,0xbe,0x00,0x05,0xff,
+ 0xe6,0xb5,0xb7,0x00,0x10,0x08,0x05,0xff,0xe6,0xb5,0x81,0x00,0x05,0xff,0xe6,0xb5,
+ 0xa9,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xb5,0xb8,0x00,0x05,0xff,0xe6,0xb6,
+ 0x85,0x00,0x10,0x09,0x05,0xff,0xf0,0xa3,0xb4,0x9e,0x00,0x05,0xff,0xe6,0xb4,0xb4,
+ 0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe6,0xb8,0xaf,0x00,0x05,0xff,0xe6,
+ 0xb9,0xae,0x00,0x10,0x08,0x05,0xff,0xe3,0xb4,0xb3,0x00,0x05,0xff,0xe6,0xbb,0x8b,
+ 0x00,0xd1,0x11,0x10,0x08,0x05,0xff,0xe6,0xbb,0x87,0x00,0x05,0xff,0xf0,0xa3,0xbb,
+ 0x91,0x00,0x10,0x08,0x05,0xff,0xe6,0xb7,0xb9,0x00,0x05,0xff,0xe6,0xbd,0xae,0x00,
+ 0xd3,0x42,0xd2,0x22,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa3,0xbd,0x9e,0x00,0x05,
+ 0xff,0xf0,0xa3,0xbe,0x8e,0x00,0x10,0x08,0x05,0xff,0xe6,0xbf,0x86,0x00,0x05,0xff,
+ 0xe7,0x80,0xb9,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0x80,0x9e,0x00,0x05,0xff,
+ 0xe7,0x80,0x9b,0x00,0x10,0x08,0x05,0xff,0xe3,0xb6,0x96,0x00,0x05,0xff,0xe7,0x81,
+ 0x8a,0x00,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0x81,0xbd,0x00,0x05,0xff,
+ 0xe7,0x81,0xb7,0x00,0x10,0x08,0x05,0xff,0xe7,0x82,0xad,0x00,0x05,0xff,0xf0,0xa0,
+ 0x94,0xa5,0x00,0xd1,0x11,0x10,0x08,0x05,0xff,0xe7,0x85,0x85,0x00,0x05,0xff,0xf0,
+ 0xa4,0x89,0xa3,0x00,0x10,0x08,0x05,0xff,0xe7,0x86,0x9c,0x00,0x05,0xff,0xf0,0xa4,
+ 0x8e,0xab,0x00,0xd4,0x7b,0xd3,0x43,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,
+ 0x88,0xa8,0x00,0x05,0xff,0xe7,0x88,0xb5,0x00,0x10,0x08,0x05,0xff,0xe7,0x89,0x90,
+ 0x00,0x05,0xff,0xf0,0xa4,0x98,0x88,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0x8a,
+ 0x80,0x00,0x05,0xff,0xe7,0x8a,0x95,0x00,0x10,0x09,0x05,0xff,0xf0,0xa4,0x9c,0xb5,
+ 0x00,0x05,0xff,0xf0,0xa4,0xa0,0x94,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,
+ 0xe7,0x8d,0xba,0x00,0x05,0xff,0xe7,0x8e,0x8b,0x00,0x10,0x08,0x05,0xff,0xe3,0xba,
+ 0xac,0x00,0x05,0xff,0xe7,0x8e,0xa5,0x00,0x51,0x08,0x05,0xff,0xe3,0xba,0xb8,0x00,
+ 0x10,0x08,0x05,0xff,0xe7,0x91,0x87,0x00,0x05,0xff,0xe7,0x91,0x9c,0x00,0xd3,0x42,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0x91,0xb1,0x00,0x05,0xff,0xe7,0x92,
+ 0x85,0x00,0x10,0x08,0x05,0xff,0xe7,0x93,0x8a,0x00,0x05,0xff,0xe3,0xbc,0x9b,0x00,
+ 0xd1,0x11,0x10,0x08,0x05,0xff,0xe7,0x94,0xa4,0x00,0x05,0xff,0xf0,0xa4,0xb0,0xb6,
+ 0x00,0x10,0x08,0x05,0xff,0xe7,0x94,0xbe,0x00,0x05,0xff,0xf0,0xa4,0xb2,0x92,0x00,
+ 0xd2,0x22,0xd1,0x11,0x10,0x08,0x05,0xff,0xe7,0x95,0xb0,0x00,0x05,0xff,0xf0,0xa2,
+ 0x86,0x9f,0x00,0x10,0x08,0x05,0xff,0xe7,0x98,0x90,0x00,0x05,0xff,0xf0,0xa4,0xbe,
+ 0xa1,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa4,0xbe,0xb8,0x00,0x05,0xff,0xf0,
+ 0xa5,0x81,0x84,0x00,0x10,0x08,0x05,0xff,0xe3,0xbf,0xbc,0x00,0x05,0xff,0xe4,0x80,
+ 0x88,0x00,0xcf,0x86,0xe5,0x04,0x01,0xd4,0x7d,0xd3,0x3c,0xd2,0x23,0xd1,0x11,0x10,
+ 0x08,0x05,0xff,0xe7,0x9b,0xb4,0x00,0x05,0xff,0xf0,0xa5,0x83,0xb3,0x00,0x10,0x09,
+ 0x05,0xff,0xf0,0xa5,0x83,0xb2,0x00,0x05,0xff,0xf0,0xa5,0x84,0x99,0x00,0x91,0x11,
+ 0x10,0x09,0x05,0xff,0xf0,0xa5,0x84,0xb3,0x00,0x05,0xff,0xe7,0x9c,0x9e,0x00,0x05,
+ 0xff,0xe7,0x9c,0x9f,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0x9d,0x8a,
+ 0x00,0x05,0xff,0xe4,0x80,0xb9,0x00,0x10,0x08,0x05,0xff,0xe7,0x9e,0x8b,0x00,0x05,
+ 0xff,0xe4,0x81,0x86,0x00,0xd1,0x11,0x10,0x08,0x05,0xff,0xe4,0x82,0x96,0x00,0x05,
+ 0xff,0xf0,0xa5,0x90,0x9d,0x00,0x10,0x08,0x05,0xff,0xe7,0xa1,0x8e,0x00,0x05,0xff,
+ 0xe7,0xa2,0x8c,0x00,0xd3,0x43,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0xa3,
+ 0x8c,0x00,0x05,0xff,0xe4,0x83,0xa3,0x00,0x10,0x09,0x05,0xff,0xf0,0xa5,0x98,0xa6,
+ 0x00,0x05,0xff,0xe7,0xa5,0x96,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa5,0x9a,
+ 0x9a,0x00,0x05,0xff,0xf0,0xa5,0x9b,0x85,0x00,0x10,0x08,0x05,0xff,0xe7,0xa6,0x8f,
+ 0x00,0x05,0xff,0xe7,0xa7,0xab,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe4,
+ 0x84,0xaf,0x00,0x05,0xff,0xe7,0xa9,0x80,0x00,0x10,0x08,0x05,0xff,0xe7,0xa9,0x8a,
+ 0x00,0x05,0xff,0xe7,0xa9,0x8f,0x00,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa5,0xa5,
+ 0xbc,0x00,0x05,0xff,0xf0,0xa5,0xaa,0xa7,0x00,0x10,0x09,0x05,0xff,0xf0,0xa5,0xaa,
+ 0xa7,0x00,0x05,0xff,0xe7,0xab,0xae,0x00,0xd4,0x83,0xd3,0x42,0xd2,0x21,0xd1,0x11,
+ 0x10,0x08,0x05,0xff,0xe4,0x88,0x82,0x00,0x05,0xff,0xf0,0xa5,0xae,0xab,0x00,0x10,
+ 0x08,0x05,0xff,0xe7,0xaf,0x86,0x00,0x05,0xff,0xe7,0xaf,0x89,0x00,0xd1,0x11,0x10,
+ 0x08,0x05,0xff,0xe4,0x88,0xa7,0x00,0x05,0xff,0xf0,0xa5,0xb2,0x80,0x00,0x10,0x08,
+ 0x05,0xff,0xe7,0xb3,0x92,0x00,0x05,0xff,0xe4,0x8a,0xa0,0x00,0xd2,0x21,0xd1,0x10,
+ 0x10,0x08,0x05,0xff,0xe7,0xb3,0xa8,0x00,0x05,0xff,0xe7,0xb3,0xa3,0x00,0x10,0x08,
+ 0x05,0xff,0xe7,0xb4,0x80,0x00,0x05,0xff,0xf0,0xa5,0xbe,0x86,0x00,0xd1,0x10,0x10,
+ 0x08,0x05,0xff,0xe7,0xb5,0xa3,0x00,0x05,0xff,0xe4,0x8c,0x81,0x00,0x10,0x08,0x05,
+ 0xff,0xe7,0xb7,0x87,0x00,0x05,0xff,0xe7,0xb8,0x82,0x00,0xd3,0x44,0xd2,0x22,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe7,0xb9,0x85,0x00,0x05,0xff,0xe4,0x8c,0xb4,0x00,0x10,
+ 0x09,0x05,0xff,0xf0,0xa6,0x88,0xa8,0x00,0x05,0xff,0xf0,0xa6,0x89,0x87,0x00,0xd1,
+ 0x11,0x10,0x08,0x05,0xff,0xe4,0x8d,0x99,0x00,0x05,0xff,0xf0,0xa6,0x8b,0x99,0x00,
+ 0x10,0x08,0x05,0xff,0xe7,0xbd,0xba,0x00,0x05,0xff,0xf0,0xa6,0x8c,0xbe,0x00,0xd2,
+ 0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe7,0xbe,0x95,0x00,0x05,0xff,0xe7,0xbf,0xba,
+ 0x00,0x10,0x08,0x05,0xff,0xe8,0x80,0x85,0x00,0x05,0xff,0xf0,0xa6,0x93,0x9a,0x00,
+ 0xd1,0x11,0x10,0x09,0x05,0xff,0xf0,0xa6,0x94,0xa3,0x00,0x05,0xff,0xe8,0x81,0xa0,
+ 0x00,0x10,0x09,0x05,0xff,0xf0,0xa6,0x96,0xa8,0x00,0x05,0xff,0xe8,0x81,0xb0,0x00,
+ 0xe0,0x11,0x02,0xcf,0x86,0xe5,0x07,0x01,0xd4,0x85,0xd3,0x42,0xd2,0x21,0xd1,0x11,
+ 0x10,0x09,0x05,0xff,0xf0,0xa3,0x8d,0x9f,0x00,0x05,0xff,0xe4,0x8f,0x95,0x00,0x10,
+ 0x08,0x05,0xff,0xe8,0x82,0xb2,0x00,0x05,0xff,0xe8,0x84,0x83,0x00,0xd1,0x10,0x10,
+ 0x08,0x05,0xff,0xe4,0x90,0x8b,0x00,0x05,0xff,0xe8,0x84,0xbe,0x00,0x10,0x08,0x05,
+ 0xff,0xe5,0xaa,0xb5,0x00,0x05,0xff,0xf0,0xa6,0x9e,0xa7,0x00,0xd2,0x23,0xd1,0x12,
+ 0x10,0x09,0x05,0xff,0xf0,0xa6,0x9e,0xb5,0x00,0x05,0xff,0xf0,0xa3,0x8e,0x93,0x00,
+ 0x10,0x09,0x05,0xff,0xf0,0xa3,0x8e,0x9c,0x00,0x05,0xff,0xe8,0x88,0x81,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe8,0x88,0x84,0x00,0x05,0xff,0xe8,0xbe,0x9e,0x00,0x10,
+ 0x08,0x05,0xff,0xe4,0x91,0xab,0x00,0x05,0xff,0xe8,0x8a,0x91,0x00,0xd3,0x41,0xd2,
+ 0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x8a,0x8b,0x00,0x05,0xff,0xe8,0x8a,0x9d,
+ 0x00,0x10,0x08,0x05,0xff,0xe5,0x8a,0xb3,0x00,0x05,0xff,0xe8,0x8a,0xb1,0x00,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe8,0x8a,0xb3,0x00,0x05,0xff,0xe8,0x8a,0xbd,0x00,0x10,
+ 0x08,0x05,0xff,0xe8,0x8b,0xa6,0x00,0x05,0xff,0xf0,0xa6,0xac,0xbc,0x00,0xd2,0x20,
+ 0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x8b,0xa5,0x00,0x05,0xff,0xe8,0x8c,0x9d,0x00,
+ 0x10,0x08,0x05,0xff,0xe8,0x8d,0xa3,0x00,0x05,0xff,0xe8,0x8e,0xad,0x00,0xd1,0x10,
+ 0x10,0x08,0x05,0xff,0xe8,0x8c,0xa3,0x00,0x05,0xff,0xe8,0x8e,0xbd,0x00,0x10,0x08,
+ 0x05,0xff,0xe8,0x8f,0xa7,0x00,0x05,0xff,0xe8,0x91,0x97,0x00,0xd4,0x85,0xd3,0x43,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x8d,0x93,0x00,0x05,0xff,0xe8,0x8f,
+ 0x8a,0x00,0x10,0x08,0x05,0xff,0xe8,0x8f,0x8c,0x00,0x05,0xff,0xe8,0x8f,0x9c,0x00,
+ 0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa6,0xb0,0xb6,0x00,0x05,0xff,0xf0,0xa6,0xb5,
+ 0xab,0x00,0x10,0x09,0x05,0xff,0xf0,0xa6,0xb3,0x95,0x00,0x05,0xff,0xe4,0x94,0xab,
+ 0x00,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x93,0xb1,0x00,0x05,0xff,0xe8,
+ 0x93,0xb3,0x00,0x10,0x08,0x05,0xff,0xe8,0x94,0x96,0x00,0x05,0xff,0xf0,0xa7,0x8f,
+ 0x8a,0x00,0xd1,0x11,0x10,0x08,0x05,0xff,0xe8,0x95,0xa4,0x00,0x05,0xff,0xf0,0xa6,
+ 0xbc,0xac,0x00,0x10,0x08,0x05,0xff,0xe4,0x95,0x9d,0x00,0x05,0xff,0xe4,0x95,0xa1,
+ 0x00,0xd3,0x42,0xd2,0x22,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa6,0xbe,0xb1,0x00,
+ 0x05,0xff,0xf0,0xa7,0x83,0x92,0x00,0x10,0x08,0x05,0xff,0xe4,0x95,0xab,0x00,0x05,
+ 0xff,0xe8,0x99,0x90,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x99,0x9c,0x00,0x05,
+ 0xff,0xe8,0x99,0xa7,0x00,0x10,0x08,0x05,0xff,0xe8,0x99,0xa9,0x00,0x05,0xff,0xe8,
+ 0x9a,0xa9,0x00,0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x9a,0x88,0x00,0x05,
+ 0xff,0xe8,0x9c,0x8e,0x00,0x10,0x08,0x05,0xff,0xe8,0x9b,0xa2,0x00,0x05,0xff,0xe8,
+ 0x9d,0xb9,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe8,0x9c,0xa8,0x00,0x05,0xff,0xe8,
+ 0x9d,0xab,0x00,0x10,0x08,0x05,0xff,0xe8,0x9e,0x86,0x00,0x05,0xff,0xe4,0x97,0x97,
+ 0x00,0xcf,0x86,0xe5,0x08,0x01,0xd4,0x83,0xd3,0x41,0xd2,0x20,0xd1,0x10,0x10,0x08,
+ 0x05,0xff,0xe8,0x9f,0xa1,0x00,0x05,0xff,0xe8,0xa0,0x81,0x00,0x10,0x08,0x05,0xff,
+ 0xe4,0x97,0xb9,0x00,0x05,0xff,0xe8,0xa1,0xa0,0x00,0xd1,0x11,0x10,0x08,0x05,0xff,
+ 0xe8,0xa1,0xa3,0x00,0x05,0xff,0xf0,0xa7,0x99,0xa7,0x00,0x10,0x08,0x05,0xff,0xe8,
+ 0xa3,0x97,0x00,0x05,0xff,0xe8,0xa3,0x9e,0x00,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,
+ 0xff,0xe4,0x98,0xb5,0x00,0x05,0xff,0xe8,0xa3,0xba,0x00,0x10,0x08,0x05,0xff,0xe3,
+ 0x92,0xbb,0x00,0x05,0xff,0xf0,0xa7,0xa2,0xae,0x00,0xd1,0x11,0x10,0x09,0x05,0xff,
+ 0xf0,0xa7,0xa5,0xa6,0x00,0x05,0xff,0xe4,0x9a,0xbe,0x00,0x10,0x08,0x05,0xff,0xe4,
+ 0x9b,0x87,0x00,0x05,0xff,0xe8,0xaa,0xa0,0x00,0xd3,0x41,0xd2,0x21,0xd1,0x10,0x10,
+ 0x08,0x05,0xff,0xe8,0xab,0xad,0x00,0x05,0xff,0xe8,0xae,0x8a,0x00,0x10,0x08,0x05,
+ 0xff,0xe8,0xb1,0x95,0x00,0x05,0xff,0xf0,0xa7,0xb2,0xa8,0x00,0xd1,0x10,0x10,0x08,
+ 0x05,0xff,0xe8,0xb2,0xab,0x00,0x05,0xff,0xe8,0xb3,0x81,0x00,0x10,0x08,0x05,0xff,
+ 0xe8,0xb4,0x9b,0x00,0x05,0xff,0xe8,0xb5,0xb7,0x00,0xd2,0x22,0xd1,0x12,0x10,0x09,
+ 0x05,0xff,0xf0,0xa7,0xbc,0xaf,0x00,0x05,0xff,0xf0,0xa0,0xa0,0x84,0x00,0x10,0x08,
+ 0x05,0xff,0xe8,0xb7,0x8b,0x00,0x05,0xff,0xe8,0xb6,0xbc,0x00,0xd1,0x11,0x10,0x08,
+ 0x05,0xff,0xe8,0xb7,0xb0,0x00,0x05,0xff,0xf0,0xa0,0xa3,0x9e,0x00,0x10,0x08,0x05,
+ 0xff,0xe8,0xbb,0x94,0x00,0x05,0xff,0xe8,0xbc,0xb8,0x00,0xd4,0x84,0xd3,0x43,0xd2,
+ 0x22,0xd1,0x12,0x10,0x09,0x05,0xff,0xf0,0xa8,0x97,0x92,0x00,0x05,0xff,0xf0,0xa8,
+ 0x97,0xad,0x00,0x10,0x08,0x05,0xff,0xe9,0x82,0x94,0x00,0x05,0xff,0xe9,0x83,0xb1,
+ 0x00,0xd1,0x11,0x10,0x08,0x05,0xff,0xe9,0x84,0x91,0x00,0x05,0xff,0xf0,0xa8,0x9c,
+ 0xae,0x00,0x10,0x08,0x05,0xff,0xe9,0x84,0x9b,0x00,0x05,0xff,0xe9,0x88,0xb8,0x00,
+ 0xd2,0x20,0xd1,0x10,0x10,0x08,0x05,0xff,0xe9,0x8b,0x97,0x00,0x05,0xff,0xe9,0x8b,
+ 0x98,0x00,0x10,0x08,0x05,0xff,0xe9,0x89,0xbc,0x00,0x05,0xff,0xe9,0x8f,0xb9,0x00,
+ 0xd1,0x11,0x10,0x08,0x05,0xff,0xe9,0x90,0x95,0x00,0x05,0xff,0xf0,0xa8,0xaf,0xba,
+ 0x00,0x10,0x08,0x05,0xff,0xe9,0x96,0x8b,0x00,0x05,0xff,0xe4,0xa6,0x95,0x00,0xd3,
+ 0x43,0xd2,0x21,0xd1,0x11,0x10,0x08,0x05,0xff,0xe9,0x96,0xb7,0x00,0x05,0xff,0xf0,
+ 0xa8,0xb5,0xb7,0x00,0x10,0x08,0x05,0xff,0xe4,0xa7,0xa6,0x00,0x05,0xff,0xe9,0x9b,
+ 0x83,0x00,0xd1,0x10,0x10,0x08,0x05,0xff,0xe5,0xb6,0xb2,0x00,0x05,0xff,0xe9,0x9c,
+ 0xa3,0x00,0x10,0x09,0x05,0xff,0xf0,0xa9,0x85,0x85,0x00,0x05,0xff,0xf0,0xa9,0x88,
+ 0x9a,0x00,0xd2,0x21,0xd1,0x10,0x10,0x08,0x05,0xff,0xe4,0xa9,0xae,0x00,0x05,0xff,
+ 0xe4,0xa9,0xb6,0x00,0x10,0x08,0x05,0xff,0xe9,0x9f,0xa0,0x00,0x05,0xff,0xf0,0xa9,
+ 0x90,0x8a,0x00,0x91,0x11,0x10,0x08,0x05,0xff,0xe4,0xaa,0xb2,0x00,0x05,0xff,0xf0,
+ 0xa9,0x92,0x96,0x00,0x05,0xff,0xe9,0xa0,0x8b,0x00,0xe2,0x10,0x01,0xe1,0x09,0x01,
+ 0xe0,0x02,0x01,0xcf,0x86,0x95,0xfb,0xd4,0x82,0xd3,0x41,0xd2,0x21,0xd1,0x11,0x10,
+ 0x08,0x05,0xff,0xe9,0xa0,0xa9,0x00,0x05,0xff,0xf0,0xa9,0x96,0xb6,0x00,0x10,0x08,
+ 0x05,0xff,0xe9,0xa3,0xa2,0x00,0x05,0xff,0xe4,0xac,0xb3,0x00,0xd1,0x10,0x10,0x08,
+ 0x05,0xff,0xe9,0xa4,0xa9,0x00,0x05,0xff,0xe9,0xa6,0xa7,0x00,0x10,0x08,0x05,0xff,
+ 0xe9,0xa7,0x82,0x00,0x05,0xff,0xe9,0xa7,0xbe,0x00,0xd2,0x21,0xd1,0x11,0x10,0x08,
+ 0x05,0xff,0xe4,0xaf,0x8e,0x00,0x05,0xff,0xf0,0xa9,0xac,0xb0,0x00,0x10,0x08,0x05,
+ 0xff,0xe9,0xac,0x92,0x00,0x05,0xff,0xe9,0xb1,0x80,0x00,0xd1,0x10,0x10,0x08,0x05,
+ 0xff,0xe9,0xb3,0xbd,0x00,0x05,0xff,0xe4,0xb3,0x8e,0x00,0x10,0x08,0x05,0xff,0xe4,
+ 0xb3,0xad,0x00,0x05,0xff,0xe9,0xb5,0xa7,0x00,0xd3,0x44,0xd2,0x23,0xd1,0x11,0x10,
+ 0x09,0x05,0xff,0xf0,0xaa,0x83,0x8e,0x00,0x05,0xff,0xe4,0xb3,0xb8,0x00,0x10,0x09,
+ 0x05,0xff,0xf0,0xaa,0x84,0x85,0x00,0x05,0xff,0xf0,0xaa,0x88,0x8e,0x00,0xd1,0x11,
+ 0x10,0x09,0x05,0xff,0xf0,0xaa,0x8a,0x91,0x00,0x05,0xff,0xe9,0xba,0xbb,0x00,0x10,
+ 0x08,0x05,0xff,0xe4,0xb5,0x96,0x00,0x05,0xff,0xe9,0xbb,0xb9,0x00,0xd2,0x20,0xd1,
+ 0x10,0x10,0x08,0x05,0xff,0xe9,0xbb,0xbe,0x00,0x05,0xff,0xe9,0xbc,0x85,0x00,0x10,
+ 0x08,0x05,0xff,0xe9,0xbc,0x8f,0x00,0x05,0xff,0xe9,0xbc,0x96,0x00,0x91,0x11,0x10,
+ 0x08,0x05,0xff,0xe9,0xbc,0xbb,0x00,0x05,0xff,0xf0,0xaa,0x98,0x80,0x00,0x00,0x00,
+ 0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xcf,0x06,0x00,0x00,0xd3,0x06,
+ 0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,
+ 0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,
+ 0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xd3,0x08,
+ 0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd1,0x08,
+ 0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,
+ 0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,
+ 0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,
+ 0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,0x53,0x04,
+ 0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xcf,0x86,0xd5,0xc0,
+ 0xd4,0x60,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,
+ 0x00,0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,
+ 0x00,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,
+ 0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,
+ 0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,
+ 0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,
+ 0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,
+ 0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,
+ 0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,0x06,
+ 0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,
+ 0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,
+ 0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xd4,0x60,
+ 0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,
+ 0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,
+ 0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,0x06,
+ 0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,
+ 0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,
+ 0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xd3,0x08,
+ 0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd1,0x08,
+ 0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,
+ 0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,
+ 0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,
+ 0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,0x53,0x04,
+ 0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xe0,0x83,0x01,0xcf,
+ 0x86,0xd5,0xc0,0xd4,0x60,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,
+ 0x86,0xcf,0x06,0x00,0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,
+ 0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,
+ 0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,
+ 0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,
+ 0x00,0x54,0x04,0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,
+ 0x00,0x02,0x00,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,
+ 0x06,0x00,0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,
+ 0x06,0x00,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,
+ 0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,
+ 0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,
+ 0x04,0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,
+ 0x00,0xd4,0x60,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,
+ 0x06,0x00,0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,
+ 0x06,0x00,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,
+ 0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,
+ 0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,
+ 0x04,0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,
+ 0x00,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,0x00,
+ 0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,
+ 0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,
+ 0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,
+ 0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,
+ 0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xcf,
+ 0x86,0xd5,0xc0,0xd4,0x60,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,
+ 0x86,0xcf,0x06,0x00,0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,
+ 0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,
+ 0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,
+ 0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,
+ 0x00,0x54,0x04,0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,
+ 0x00,0x02,0x00,0xd3,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,
+ 0x06,0x00,0x00,0xd1,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,
+ 0x06,0x00,0x00,0xcf,0x86,0xd5,0x06,0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,
+ 0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,
+ 0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,
+ 0x04,0x00,0x00,0x53,0x04,0x00,0x00,0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,
+ 0x00,0xd4,0xd9,0xd3,0x81,0xd2,0x79,0xd1,0x71,0xd0,0x69,0xcf,0x86,0xd5,0x60,0xd4,
+ 0x59,0xd3,0x52,0xd2,0x33,0xd1,0x2c,0xd0,0x25,0xcf,0x86,0x95,0x1e,0x94,0x19,0x93,
+ 0x14,0x92,0x0f,0x91,0x0a,0x10,0x05,0x00,0xff,0x00,0x05,0xff,0x00,0x00,0xff,0x00,
+ 0x00,0xff,0x00,0x00,0xff,0x00,0x00,0xff,0x00,0x05,0xff,0x00,0xcf,0x06,0x05,0xff,
+ 0x00,0xcf,0x06,0x00,0xff,0x00,0xd1,0x07,0xcf,0x06,0x07,0xff,0x00,0xd0,0x07,0xcf,
+ 0x06,0x07,0xff,0x00,0xcf,0x86,0x55,0x05,0x07,0xff,0x00,0x14,0x05,0x07,0xff,0x00,
+ 0x00,0xff,0x00,0xcf,0x06,0x00,0xff,0x00,0xcf,0x06,0x00,0xff,0x00,0xcf,0x06,0x00,
+ 0xff,0x00,0xcf,0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,
+ 0xcf,0x06,0x00,0x00,0xd2,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xd1,0x08,0xcf,0x86,
+ 0xcf,0x06,0x00,0x00,0xd0,0x08,0xcf,0x86,0xcf,0x06,0x00,0x00,0xcf,0x86,0xd5,0x06,
+ 0xcf,0x06,0x00,0x00,0xd4,0x06,0xcf,0x06,0x00,0x00,0xd3,0x06,0xcf,0x06,0x00,0x00,
+ 0xd2,0x06,0xcf,0x06,0x00,0x00,0xd1,0x06,0xcf,0x06,0x00,0x00,0xd0,0x06,0xcf,0x06,
+ 0x00,0x00,0xcf,0x86,0x55,0x04,0x00,0x00,0x54,0x04,0x00,0x00,0x53,0x04,0x00,0x00,
+ 0x52,0x04,0x00,0x00,0x11,0x04,0x00,0x00,0x02,0x00,0xcf,0x86,0xcf,0x06,0x02,0x00,
+ 0x81,0x80,0xcf,0x86,0x85,0x84,0xcf,0x86,0xcf,0x06,0x02,0x00,0x00,0x00,0x00,0x00
+};
diff --git a/lib/ext2fs/utf8n.h b/lib/ext2fs/utf8n.h
new file mode 100644
index 0000000..c214f58
--- /dev/null
+++ b/lib/ext2fs/utf8n.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014 SGI.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* This code is copied from the linux kernel. We have a userspace
+ * version here to such that hashes will match that implementation.
+ */
+
+#ifndef UTF8NORM_H
+#define UTF8NORM_H
+
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Encoding a unicode version number as a single unsigned int. */
+#define UNICODE_MAJ_SHIFT (16)
+#define UNICODE_MIN_SHIFT (8)
+
+#define UNICODE_AGE(MAJ, MIN, REV) \
+ (((unsigned int)(MAJ) << UNICODE_MAJ_SHIFT) | \
+ ((unsigned int)(MIN) << UNICODE_MIN_SHIFT) | \
+ ((unsigned int)(REV)))
+
+/* Highest unicode version supported by the data tables. */
+extern int utf8version_is_supported(uint8_t maj, uint8_t min, uint8_t rev);
+extern int utf8version_latest(void);
+
+/*
+ * Look for the correct const struct utf8data for a unicode version.
+ * Returns NULL if the version requested is too new.
+ *
+ * Two normalization forms are supported: nfdi and nfdicf.
+ *
+ * nfdi:
+ * - Apply unicode normalization form NFD.
+ * - Remove any Default_Ignorable_Code_Point.
+ *
+ * nfdicf:
+ * - Apply unicode normalization form NFD.
+ * - Remove any Default_Ignorable_Code_Point.
+ * - Apply a full casefold (C + F).
+ */
+extern const struct utf8data *utf8nfdi(unsigned int maxage);
+extern const struct utf8data *utf8nfdicf(unsigned int maxage);
+
+/*
+ * Determine the maximum age of any unicode character in the string.
+ * Returns 0 if only unassigned code points are present.
+ * Returns -1 if the input is not valid UTF-8.
+ */
+extern int utf8agemax(const struct utf8data *data, const char *s);
+extern int utf8nagemax(const struct utf8data *data, const char *s, size_t len);
+
+/*
+ * Determine the minimum age of any unicode character in the string.
+ * Returns 0 if any unassigned code points are present.
+ * Returns -1 if the input is not valid UTF-8.
+ */
+extern int utf8agemin(const struct utf8data *data, const char *s);
+extern int utf8nagemin(const struct utf8data *data, const char *s, size_t len);
+
+/*
+ * Determine the length of the normalized from of the string,
+ * excluding any terminating NULL byte.
+ * Returns 0 if only ignorable code points are present.
+ * Returns -1 if the input is not valid UTF-8.
+ */
+extern ssize_t utf8len(const struct utf8data *data, const char *s);
+extern ssize_t utf8nlen(const struct utf8data *data, const char *s, size_t len);
+
+/* Needed in struct utf8cursor below. */
+#define UTF8HANGULLEAF (12)
+
+/*
+ * Cursor structure used by the normalizer.
+ */
+struct utf8cursor {
+ const struct utf8data *data;
+ const char *s;
+ const char *p;
+ const char *ss;
+ const char *sp;
+ unsigned int len;
+ unsigned int slen;
+ short int ccc;
+ short int nccc;
+ unsigned char hangul[UTF8HANGULLEAF];
+};
+
+/*
+ * Initialize a utf8cursor to normalize a string.
+ * Returns 0 on success.
+ * Returns -1 on failure.
+ */
+extern int utf8cursor(struct utf8cursor *u8c, const struct utf8data *data,
+ const char *s);
+extern int utf8ncursor(struct utf8cursor *u8c, const struct utf8data *data,
+ const char *s, size_t len);
+
+/*
+ * Get the next byte in the normalization.
+ * Returns a value > 0 && < 256 on success.
+ * Returns 0 when the end of the normalization is reached.
+ * Returns -1 if the string being normalized is not valid UTF-8.
+ */
+extern int utf8byte(struct utf8cursor *u8c);
+
+#endif /* UTF8NORM_H */
diff --git a/lib/ext2fs/valid_blk.c b/lib/ext2fs/valid_blk.c
new file mode 100644
index 0000000..db5d90a
--- /dev/null
+++ b/lib/ext2fs/valid_blk.c
@@ -0,0 +1,68 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#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_blocks2(ext2_filsys fs, 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(fs, 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 */
+ }
+ }
+
+ /*
+ * If this inode has inline data, it shouldn't have valid block
+ * entries.
+ */
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return 0;
+ return 1;
+}
+
+int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
+{
+ return ext2fs_inode_has_valid_blocks2(NULL, inode);
+}
diff --git a/lib/ext2fs/version.c b/lib/ext2fs/version.c
new file mode 100644
index 0000000..f8c8acf
--- /dev/null
+++ b/lib/ext2fs/version.c
@@ -0,0 +1,57 @@
+/*
+ * 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%
+ */
+
+#include "config.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#include "../../version.h"
+
+static const char *lib_version = E2FSPROGS_VERSION;
+static const char *lib_date = E2FSPROGS_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(*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/ext2fs/windows_io.c b/lib/ext2fs/windows_io.c
new file mode 100644
index 0000000..83aea68
--- /dev/null
+++ b/lib/ext2fs/windows_io.c
@@ -0,0 +1,1031 @@
+/*
+ * windows_io.c --- This is the Windows implementation of the I/O manager.
+ *
+ * Implements a one-block write-through cache.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002 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 <windows.h>
+#include <winioctl.h>
+#include <io.h>
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
+#define _XOPEN_SOURCE 600
+#define _DARWIN_C_SOURCE
+#define _FILE_OFFSET_BITS 64
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#define PR_GET_DUMPABLE 3
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+
+#undef ALIGN_DEBUG
+
+//#define DEBUG
+#ifdef DEBUG
+#define TRACE(...) {\
+ char __log[256];\
+ snprintf(__log, sizeof(__log), __VA_ARGS__);\
+ __log[sizeof(__log)-1] = 0;\
+ OutputDebugString(__log);\
+ }
+#else
+#define TRACE(...) do { } while (0);
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+ if ((struct)->magic != (code)) return (code)
+#define EXT2_CHECK_MAGIC_RETURN(struct, code, ret) \
+ if ((struct)->magic != (code)) return (ret)
+
+#define EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL 0x10ed
+
+struct windows_cache {
+ char *buf;
+ unsigned long long block;
+ int access_time;
+ unsigned dirty:1;
+ unsigned in_use:1;
+};
+
+#define CACHE_SIZE 8
+#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */
+#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */
+
+struct windows_private_data {
+ int magic;
+ char name[MAX_PATH];
+ HANDLE handle;
+ char dos_device[MAX_PATH];
+ char cf_device[MAX_PATH];
+ int dev;
+ int flags;
+ int align;
+ int access_time;
+ ext2_loff_t offset;
+ struct windows_cache cache[CACHE_SIZE];
+ void *bounce;
+ struct struct_io_stats io_stats;
+};
+
+#define IS_ALIGNED(n, align) ((((uintptr_t) n) & \
+ ((uintptr_t) ((align)-1))) == 0)
+
+static int fake_dos_name_for_device(struct windows_private_data *data)
+{
+ if (strncmp(data->name, "\\\\", 2) == 0) {
+ data->dos_device[0] = 0;
+ strcpy(data->cf_device, data->name);
+ return 0;
+ }
+
+ _snprintf(data->dos_device, MAX_PATH, "fakedevice%lu", GetCurrentProcessId());
+
+ if (!DefineDosDevice(DDD_RAW_TARGET_PATH, data->dos_device, data->name))
+ return 1;
+
+ _snprintf(data->cf_device, MAX_PATH, "\\\\.\\%s", data->dos_device);
+ TRACE("e2fsprogs::fake_dos_name_for_device::DefineDosDevice(\"%s\")", data->dos_device);
+
+ return 0;
+}
+
+static void remove_fake_dos_name(struct windows_private_data *data)
+{
+ if (*data->dos_device) {
+ TRACE("e2fsprogs::remove_fake_dos_name::DefineDosDevice(\"%s\")", data->dos_device);
+ DefineDosDevice(DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE | DDD_REMOVE_DEFINITION, data->dos_device, data->name);
+ }
+}
+
+static errcode_t windows_get_stats(io_channel channel, io_stats *stats)
+{
+ errcode_t retval = 0;
+
+ struct windows_private_data *data;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+ if (stats)
+ *stats = &data->io_stats;
+
+ return retval;
+}
+
+/*
+ * Here are the raw I/O functions
+ */
+static errcode_t raw_read_blk(io_channel channel,
+ struct windows_private_data *data,
+ unsigned long long block,
+ int count, void *bufv)
+{
+ errcode_t retval;
+ ssize_t size;
+ ext2_loff_t location;
+ DWORD actual = 0;
+ unsigned char *buf = bufv;
+ ssize_t really_read = 0;
+
+ size = (count < 0) ? -count : count * channel->block_size;
+ data->io_stats.bytes_read += size;
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+ if (data->flags & IO_FLAG_FORCE_BOUNCE) {
+ if (SetFilePointer(data->handle, location, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ goto bounce_read;
+ }
+
+ if (SetFilePointer(data->handle, location, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ if ((channel->align == 0) || (IS_ALIGNED(buf, channel->align) && IS_ALIGNED(size, channel->align))) {
+ if (!ReadFile(data->handle, buf, size, &actual, NULL)) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ if (actual != size) {
+ short_read:
+ if (actual < 0) {
+ retval = GetLastError();
+ actual = 0;
+ } else
+ retval = EXT2_ET_SHORT_READ;
+ goto error_out;
+ }
+ return 0;
+ }
+
+ /*
+ * The buffer or size which we're trying to read isn't aligned
+ * to the O_DIRECT rules, so we need to do this the hard way...
+ */
+bounce_read:
+ while (size > 0) {
+ if (!ReadFile(data->handle, data->bounce, channel->block_size, &actual, NULL)) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ if (actual != channel->block_size) {
+ actual = really_read;
+ buf -= really_read;
+ size += really_read;
+ goto short_read;
+ }
+ actual = size;
+ if (size > channel->block_size)
+ actual = channel->block_size;
+ memcpy(buf, data->bounce, actual);
+ really_read += actual;
+ size -= actual;
+ buf += actual;
+ }
+ return 0;
+
+error_out:
+ if (actual >= 0 && actual < size)
+ memset((char *) buf+actual, 0, size-actual);
+ if (channel->read_error)
+ retval = (channel->read_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+
+static errcode_t raw_write_blk(io_channel channel,
+ struct windows_private_data *data,
+ unsigned long long block,
+ int count, const void *bufv)
+{
+ ssize_t size;
+ ext2_loff_t location;
+ DWORD actual = 0;
+ errcode_t retval;
+ const unsigned char *buf = bufv;
+
+ if (count == 1)
+ size = channel->block_size;
+ else {
+ if (count < 0)
+ size = -count;
+ else
+ size = count * channel->block_size;
+ }
+ data->io_stats.bytes_written += size;
+
+ location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+ if (data->flags & IO_FLAG_FORCE_BOUNCE) {
+ if (SetFilePointer(data->handle, location, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ goto bounce_write;
+ }
+
+ if (SetFilePointer(data->handle, location, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+ retval = GetLastError();
+ goto error_out;
+ }
+
+ SetLastError(0);
+
+ if ((channel->align == 0) || (IS_ALIGNED(buf, channel->align) && IS_ALIGNED(size, channel->align))) {
+ if (!WriteFile(data->handle, buf, size, &actual, NULL)) {
+ retval = GetLastError();
+ goto error_out;
+ }
+
+ if (actual != size) {
+ short_write:
+ retval = EXT2_ET_SHORT_WRITE;
+ goto error_out;
+ }
+ return 0;
+ }
+
+ /*
+ * The buffer or size which we're trying to write isn't aligned
+ * to the O_DIRECT rules, so we need to do this the hard way...
+ */
+bounce_write:
+ while (size > 0) {
+ if (size < channel->block_size) {
+ if (!ReadFile(data->handle, data->bounce, channel->block_size, &actual, NULL)) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ if (actual != channel->block_size) {
+ if (actual < 0) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ memset((char *) data->bounce + actual, 0,
+ channel->block_size - actual);
+ }
+ }
+ actual = size;
+ if (size > channel->block_size)
+ actual = channel->block_size;
+ memcpy(data->bounce, buf, actual);
+ if (SetFilePointer(data->handle, location, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
+ retval = GetLastError();
+ goto error_out;
+ }
+ if (!WriteFile(data->handle, data->bounce, channel->block_size, &actual, NULL)) {
+ retval = GetLastError();
+ goto error_out;
+ }
+
+ if (actual != channel->block_size)
+ goto short_write;
+ size -= actual;
+ buf += actual;
+ location += actual;
+ }
+ return 0;
+
+error_out:
+ if (channel->write_error)
+ retval = (channel->write_error)(channel, block, count, buf,
+ size, actual, retval);
+ return retval;
+}
+
+
+/*
+ * Here we implement the cache functions
+ */
+
+/* Allocate the cache buffers */
+static errcode_t alloc_cache(io_channel channel,
+ struct windows_private_data *data)
+{
+ errcode_t retval;
+ struct windows_cache *cache;
+ int i;
+
+ data->access_time = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ cache->block = 0;
+ cache->access_time = 0;
+ cache->dirty = 0;
+ cache->in_use = 0;
+ if (cache->buf)
+ ext2fs_free_mem(&cache->buf);
+ retval = io_channel_alloc_buf(channel, 0, &cache->buf);
+ if (retval)
+ return retval;
+ }
+ if (channel->align || data->flags & IO_FLAG_FORCE_BOUNCE) {
+ if (data->bounce)
+ ext2fs_free_mem(&data->bounce);
+ retval = io_channel_alloc_buf(channel, 0, &data->bounce);
+ }
+ return retval;
+}
+
+/* Free the cache buffers */
+static void free_cache(struct windows_private_data *data)
+{
+ struct windows_cache *cache;
+ int i;
+
+ data->access_time = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ cache->block = 0;
+ cache->access_time = 0;
+ cache->dirty = 0;
+ cache->in_use = 0;
+ if (cache->buf)
+ ext2fs_free_mem(&cache->buf);
+ }
+ if (data->bounce)
+ ext2fs_free_mem(&data->bounce);
+}
+
+#ifndef NO_IO_CACHE
+/*
+ * Try to find a block in the cache. If the block is not found, and
+ * eldest is a non-zero pointer, then fill in eldest with the cache
+ * entry to that should be reused.
+ */
+static struct windows_cache *find_cached_block(struct windows_private_data *data,
+ unsigned long long block,
+ struct windows_cache **eldest)
+{
+ struct windows_cache *cache, *unused_cache, *oldest_cache;
+ int i;
+
+ unused_cache = oldest_cache = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use) {
+ if (!unused_cache)
+ unused_cache = cache;
+ continue;
+ }
+ if (cache->block == block) {
+ cache->access_time = ++data->access_time;
+ return cache;
+ }
+ if (!oldest_cache ||
+ (cache->access_time < oldest_cache->access_time))
+ oldest_cache = cache;
+ }
+ if (eldest)
+ *eldest = (unused_cache) ? unused_cache : oldest_cache;
+ return 0;
+}
+
+/*
+ * Reuse a particular cache entry for another block.
+ */
+static void reuse_cache(io_channel channel, struct windows_private_data *data,
+ struct windows_cache *cache, unsigned long long block)
+{
+ if (cache->dirty && cache->in_use)
+ raw_write_blk(channel, data, cache->block, 1, cache->buf);
+
+ cache->in_use = 1;
+ cache->dirty = 0;
+ cache->block = block;
+ cache->access_time = ++data->access_time;
+}
+
+/*
+ * Flush all of the blocks in the cache
+ */
+static errcode_t flush_cached_blocks(io_channel channel,
+ struct windows_private_data *data,
+ int invalidate)
+{
+ struct windows_cache *cache;
+ errcode_t retval, retval2;
+ int i;
+
+ retval2 = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use)
+ continue;
+
+ if (invalidate)
+ cache->in_use = 0;
+
+ if (!cache->dirty)
+ continue;
+
+ retval = raw_write_blk(channel, data,
+ cache->block, 1, cache->buf);
+ if (retval)
+ retval2 = retval;
+ else
+ cache->dirty = 0;
+ }
+ return retval2;
+}
+#endif /* NO_IO_CACHE */
+
+static errcode_t windows_open_channel(struct windows_private_data *data,
+ int flags, io_channel *channel,
+ io_manager io_mgr)
+{
+ io_channel io = NULL;
+ errcode_t retval;
+ ext2fs_struct_stat st;
+
+ 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 = io_mgr;
+ retval = ext2fs_get_mem(strlen(data->name)+1, &io->name);
+ if (retval)
+ goto cleanup;
+
+ strcpy(io->name, data->name);
+ io->private_data = data;
+ io->block_size = 1024;
+ io->read_error = 0;
+ io->write_error = 0;
+ io->refcount = 1;
+
+#if defined(O_DIRECT)
+ if (flags & IO_FLAG_DIRECT_IO)
+ io->align = ext2fs_get_dio_alignment(data->dev);
+#endif
+
+ /*
+ * If the device is really a block device, then set the
+ * appropriate flag, otherwise we can set DISCARD_ZEROES flag
+ * because we are going to use punch hole instead of discard
+ * and if it succeed, subsequent read from sparse area returns
+ * zero.
+ */
+ if (ext2fs_fstat(data->dev, &st) == 0) {
+ if (ext2fsP_is_disk_device(st.st_mode))
+ io->flags |= CHANNEL_FLAGS_BLOCK_DEVICE;
+ else
+ io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+ }
+
+ if ((retval = alloc_cache(io, data)))
+ goto cleanup;
+
+#ifdef BLKROGET
+ if (flags & IO_FLAG_RW) {
+ int error;
+ int readonly = 0;
+
+ /* Is the block device actually writable? */
+ error = ioctl(data->dev, BLKROGET, &readonly);
+ if (!error && readonly) {
+ retval = EPERM;
+ goto cleanup;
+ }
+ }
+#endif
+
+ *channel = io;
+ return 0;
+
+cleanup:
+ if (data) {
+ if (data->dev >= 0)
+ close(data->dev);
+ free_cache(data);
+ ext2fs_free_mem(&data);
+ }
+ if (io) {
+ if (io->name) {
+ ext2fs_free_mem(&io->name);
+ }
+ ext2fs_free_mem(&io);
+ }
+ return retval;
+}
+
+static DWORD windows_open_device(struct windows_private_data *data, int open_flags)
+{
+ DWORD ret = 0;
+
+ if (*data->name != '\\')
+ strcpy(data->cf_device, data->name);
+ else if (fake_dos_name_for_device(data))
+ return -1;
+
+ DWORD desired_access = GENERIC_READ | ((open_flags & O_RDWR) ? GENERIC_WRITE : 0);
+ DWORD share_mode = (open_flags & O_EXCL) ? 0 : FILE_SHARE_READ | ((open_flags & O_RDWR) ? FILE_SHARE_WRITE : 0);
+ DWORD flags_and_attributes =
+#if defined(O_DIRECT)
+ (open_flags & O_DIRECT) ? (FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH) : FILE_ATTRIBUTE_NORMAL;
+#else
+ FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
+#endif
+
+ data->handle = CreateFile(data->cf_device, desired_access, share_mode, NULL, OPEN_EXISTING,
+ flags_and_attributes, NULL);
+
+ if (data->handle == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ goto invalid_handle;
+ }
+
+ TRACE("e2fsprogs::windows_open_device::CreateFile(\"%s\") = %p", data->cf_device, data->handle);
+
+ data->dev = _open_osfhandle((intptr_t)data->handle, 0);
+ if (data->dev < 0) {
+ ret = GetLastError() ? GetLastError() : 9999;
+ goto osfhandle_error;
+ }
+
+ TRACE("e2fsprogs::windows_open_device::_open_osfhandle(%p) = %d", data->handle, data->dev);
+
+ return 0;
+
+osfhandle_error:
+ TRACE("e2fsprogs::windows_open_device::CloseHandle(%p)", data->handle);
+ CloseHandle(data->handle);
+invalid_handle:
+ remove_fake_dos_name(data);
+ TRACE("e2fsprogs::windows_open_device() = %lu, errno = %d", ret, errno);
+ return ret;
+}
+
+static struct windows_private_data *init_private_data(const char *name, int flags)
+{
+ struct windows_private_data *data = NULL;
+
+ if (ext2fs_get_mem(sizeof(struct windows_private_data), &data))
+ return NULL;
+
+ memset(data, 0, sizeof(struct windows_private_data));
+ strncpy(data->name, name, sizeof(data->name) - 1);
+ data->magic = EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL;
+ data->io_stats.num_fields = 2;
+ data->flags = flags;
+ data->handle = INVALID_HANDLE_VALUE;
+
+ return data;
+}
+
+static errcode_t windows_open(const char *name, int flags, io_channel *channel)
+{
+ int open_flags;
+ struct windows_private_data *data;
+
+ if (name == 0)
+ return EXT2_ET_BAD_DEVICE_NAME;
+
+ data = init_private_data(name, flags);
+ if (!data)
+ return EXT2_ET_NO_MEMORY;
+
+ open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
+ if (flags & IO_FLAG_EXCLUSIVE)
+ open_flags |= O_EXCL;
+#if defined(O_DIRECT)
+ if (flags & IO_FLAG_DIRECT_IO)
+ open_flags |= O_DIRECT;
+#endif
+
+ if (windows_open_device(data, open_flags)) {
+ ext2fs_free_mem(&data);
+ return EXT2_ET_BAD_DEVICE_NAME;
+ }
+
+ return windows_open_channel(data, flags, channel, windows_io_manager);
+}
+
+static errcode_t windows_close(io_channel channel)
+{
+ struct windows_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+ if (--channel->refcount > 0)
+ return 0;
+
+#ifndef NO_IO_CACHE
+ retval = flush_cached_blocks(channel, data, 0);
+#endif
+
+ remove_fake_dos_name(data);
+
+ if (_close(data->dev) != 0)
+ retval = errno;
+
+ TRACE("e2fsprogs::windows_close::_close(%d)", data->dev);
+
+ free_cache(data);
+
+ ext2fs_free_mem(&channel->private_data);
+ if (channel->name)
+ ext2fs_free_mem(&channel->name);
+ ext2fs_free_mem(&channel);
+ return retval;
+}
+
+static errcode_t windows_set_blksize(io_channel channel, int blksize)
+{
+ struct windows_private_data *data;
+ errcode_t retval;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+ if (channel->block_size != blksize) {
+#ifndef NO_IO_CACHE
+ if ((retval = flush_cached_blocks(channel, data, 0)))
+ return retval;
+#endif
+
+ channel->block_size = blksize;
+ free_cache(data);
+ if ((retval = alloc_cache(channel, data)))
+ return retval;
+ }
+ return 0;
+}
+
+static errcode_t windows_read_blk64(io_channel channel, unsigned long long block, int count, void *buf)
+{
+ struct windows_private_data *data;
+ struct windows_cache *cache, *reuse[READ_DIRECT_SIZE];
+ errcode_t retval;
+ char *cp;
+ int i, j;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+ return raw_read_blk(channel, data, block, count, buf);
+#else
+ /*
+ * If we're doing an odd-sized read or a very large read,
+ * flush out the cache and then do a direct read.
+ */
+ if (count < 0 || count > WRITE_DIRECT_SIZE) {
+ if ((retval = flush_cached_blocks(channel, data, 0)))
+ return retval;
+ return raw_read_blk(channel, data, block, count, buf);
+ }
+
+ cp = buf;
+ while (count > 0) {
+ /* If it's in the cache, use it! */
+ if ((cache = find_cached_block(data, block, &reuse[0]))) {
+#ifdef DEBUG
+ printf("Using cached block %lu\n", block);
+#endif
+ memcpy(cp, cache->buf, channel->block_size);
+ count--;
+ block++;
+ cp += channel->block_size;
+ continue;
+ }
+ if (count == 1) {
+ /*
+ * Special case where we read directly into the
+ * cache buffer; important in the O_DIRECT case
+ */
+ cache = reuse[0];
+ reuse_cache(channel, data, cache, block);
+ if ((retval = raw_read_blk(channel, data, block, 1,
+ cache->buf))) {
+ cache->in_use = 0;
+ return retval;
+ }
+ memcpy(cp, cache->buf, channel->block_size);
+ return 0;
+ }
+
+ /*
+ * Find the number of uncached blocks so we can do a
+ * single read request
+ */
+ for (i=1; i < count; i++)
+ if (find_cached_block(data, block+i, &reuse[i]))
+ break;
+#ifdef DEBUG
+ printf("Reading %d blocks starting at %lu\n", i, block);
+#endif
+ if ((retval = raw_read_blk(channel, data, block, i, cp)))
+ return retval;
+
+ /* Save the results in the cache */
+ for (j=0; j < i; j++) {
+ count--;
+ cache = reuse[j];
+ reuse_cache(channel, data, cache, block++);
+ memcpy(cache->buf, cp, channel->block_size);
+ cp += channel->block_size;
+ }
+ }
+ return 0;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t windows_read_blk(io_channel channel, unsigned long block,
+ int count, void *buf)
+{
+ return windows_read_blk64(channel, block, count, buf);
+}
+
+static errcode_t windows_write_blk64(io_channel channel,
+ unsigned long long block,
+ int count, const void *buf)
+{
+ struct windows_private_data *data;
+ struct windows_cache *cache, *reuse;
+ errcode_t retval = 0;
+ const char *cp;
+ int writethrough;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+ return raw_write_blk(channel, data, block, count, buf);
+#else
+ /*
+ * If we're doing an odd-sized write or a very large write,
+ * flush out the cache completely and then do a direct write.
+ */
+ if (count < 0 || count > WRITE_DIRECT_SIZE) {
+ if ((retval = flush_cached_blocks(channel, data, 1)))
+ return retval;
+ return raw_write_blk(channel, data, block, count, buf);
+ }
+
+ /*
+ * For a moderate-sized multi-block write, first force a write
+ * if we're in write-through cache mode, and then fill the
+ * cache with the blocks.
+ */
+ writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
+ if (writethrough)
+ retval = raw_write_blk(channel, data, block, count, buf);
+
+ cp = buf;
+ while (count > 0) {
+ cache = find_cached_block(data, block, &reuse);
+ if (!cache) {
+ cache = reuse;
+ reuse_cache(channel, data, cache, block);
+ }
+ if (cache->buf != cp)
+ memcpy(cache->buf, cp, channel->block_size);
+ cache->dirty = !writethrough;
+ count--;
+ block++;
+ cp += channel->block_size;
+ }
+ return retval;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t windows_cache_readahead(io_channel channel,
+ unsigned long long block,
+ unsigned long long count)
+{
+ return EXT2_ET_OP_NOT_SUPPORTED;
+}
+
+static errcode_t windows_write_blk(io_channel channel, unsigned long block,
+ int count, const void *buf)
+{
+ return windows_write_blk64(channel, block, count, buf);
+}
+
+static errcode_t windows_write_byte(io_channel channel, unsigned long offset,
+ int size, const void *buf)
+{
+ return EXT2_ET_UNIMPLEMENTED;
+}
+
+HANDLE windows_get_handle(io_channel channel)
+{
+ struct windows_private_data *data;
+
+ EXT2_CHECK_MAGIC_RETURN(channel, EXT2_ET_MAGIC_IO_CHANNEL, INVALID_HANDLE_VALUE);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC_RETURN(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL, INVALID_HANDLE_VALUE);
+
+ return data->handle;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t windows_flush(io_channel channel)
+{
+ struct windows_private_data *data;
+ errcode_t retval = 0;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+ retval = flush_cached_blocks(channel, data, 0);
+#endif
+
+ return retval;
+}
+
+static errcode_t windows_set_option(io_channel channel, const char *option,
+ const char *arg)
+{
+ struct windows_private_data *data;
+ unsigned long long tmp;
+ char *end;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_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;
+ data->offset = tmp;
+ if (data->offset < 0)
+ return EXT2_ET_INVALID_ARGUMENT;
+ return 0;
+ }
+ return EXT2_ET_INVALID_ARGUMENT;
+}
+
+static errcode_t windows_discard(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ TRACE("e2fsprogs::windows_discard::EXT2_ET_UNIMPLEMENTED");
+ return EXT2_ET_UNIMPLEMENTED;
+}
+
+/* parameters might not be used if OS doesn't support zeroout */
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+static errcode_t windows_zeroout(io_channel channel, unsigned long long block,
+ unsigned long long count)
+{
+ struct windows_private_data *data;
+ int ret;
+
+ EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+ data = (struct windows_private_data *) channel->private_data;
+ EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_WINDOWS_IO_CHANNEL);
+
+ if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
+ /* Not implemented until the BLKZEROOUT mess is fixed */
+ goto unimplemented;
+ } else {
+ /* Regular file, try to use truncate/punch/zero. */
+ struct stat statbuf;
+
+ if (count == 0)
+ return 0;
+ /*
+ * If we're trying to zero a range past the end of the file,
+ * extend the file size, then truncate everything.
+ */
+ ret = fstat(data->dev, &statbuf);
+ if (ret)
+ goto err;
+ if ((unsigned long long) statbuf.st_size <
+ (block + count) * channel->block_size + data->offset) {
+ ret = ftruncate(data->dev,
+ (block + count) * channel->block_size + data->offset);
+ if (ret)
+ goto err;
+ }
+ goto unimplemented;
+ }
+err:
+ if (ret < 0) {
+ if (errno == EOPNOTSUPP)
+ goto unimplemented;
+ return errno;
+ }
+ return 0;
+unimplemented:
+ return EXT2_ET_UNIMPLEMENTED;
+}
+
+int ext2fs_open_file(const char *pathname, int flags, mode_t mode)
+{
+ flags |= O_BINARY;
+
+ if (mode)
+#if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return open64(pathname, flags, mode);
+ else
+ return open64(pathname, flags);
+#else
+ return open(pathname, flags, mode);
+ else
+ return open(pathname, flags);
+#endif
+}
+
+int ext2fs_stat(const char *path, ext2fs_struct_stat *buf)
+{
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return stat64(path, buf);
+#else
+ return stat(path, buf);
+#endif
+}
+
+int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
+{
+#if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
+ return fstat64(fd, buf);
+#else
+ return fstat(fd, buf);
+#endif
+}
+
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic pop
+#endif
+
+static struct struct_io_manager struct_windows_manager = {
+ .magic = EXT2_ET_MAGIC_IO_MANAGER,
+ .name = "Windows I/O Manager",
+ .open = windows_open,
+ .close = windows_close,
+ .set_blksize = windows_set_blksize,
+ .read_blk = windows_read_blk,
+ .write_blk = windows_write_blk,
+ .flush = windows_flush,
+ .write_byte = windows_write_byte,
+ .set_option = windows_set_option,
+ .get_stats = windows_get_stats,
+ .read_blk64 = windows_read_blk64,
+ .write_blk64 = windows_write_blk64,
+ .discard = windows_discard,
+ .cache_readahead = windows_cache_readahead,
+ .zeroout = windows_zeroout,
+};
+
+io_manager windows_io_manager = &struct_windows_manager;
diff --git a/lib/ext2fs/write_bb_file.c b/lib/ext2fs/write_bb_file.c
new file mode 100644
index 0000000..5834340
--- /dev/null
+++ b/lib/ext2fs/write_bb_file.c
@@ -0,0 +1,35 @@
+/*
+ * 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 "config.h"
+#include <stdio.h>
+
+#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/fpopen.c b/lib/fpopen.c
new file mode 100644
index 0000000..60af208
--- /dev/null
+++ b/lib/fpopen.c
@@ -0,0 +1,116 @@
+/*
+ * fpopen.c --- unlike the libc popen, it directly executes the
+ * command instead of call out to the shell.
+ *
+ * Copyright Theodore Ts'o, 1996-1999.
+ *
+ * Permission to use this file is granted for any purposes, as long as
+ * this copyright statement is kept intact and the author is not held
+ * liable for any damages resulting from the use of this program.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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.
+ */
+
+#include "config.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MAX_ARGV 256
+
+extern FILE *fpopen(const char *cmd, const char *mode);
+
+FILE *fpopen(const char *cmd, const char *mode)
+{
+ char *argv[MAX_ARGV];
+ int i = 0;
+ char *buf, *prog = 0;
+ char *p;
+ int do_stdin, do_stderr = 0;
+ int fds[2];
+ pid_t pid;
+
+ if (!mode) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ switch (*mode) {
+ case 'r':
+ do_stdin = 0;
+ break;
+ case 'w':
+ do_stdin = 1;
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (*(mode+1)) {
+ case '&':
+ do_stderr = 1;
+ }
+
+ /*
+ * Create the argv vector....
+ */
+ buf = malloc(strlen(cmd)+1);
+ if (!buf)
+ return NULL;
+ strcpy(buf, cmd);
+ p = buf;
+ while (p && *p) {
+ if (isspace(*p)) {
+ p++;
+ continue;
+ }
+ if (i == 0)
+ prog = p;
+ argv[i++] = p;
+ p = strchr(p, ' ');
+ if (p)
+ *p++ = 0;
+ }
+
+ argv[i] = 0;
+
+ /*
+ * Get the pipe
+ */
+ if (pipe(fds) < 0)
+ return NULL;
+
+ /* Fork and execute the correct program. */
+ if ((pid = fork()) < 0) {
+ perror("fork");
+ return NULL;
+ } else if (pid == 0) {
+ if (do_stdin) {
+ close(fds[1]);
+ dup2(fds[0], 0);
+ } else {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ if (do_stderr)
+ dup2(fds[1], 2);
+ }
+ (void) execvp(prog, argv);
+ perror(prog);
+ exit(1);
+ }
+ return fdopen(do_stdin ? fds[1] : fds[0], mode);
+}
+
diff --git a/lib/ss/Android.bp b/lib/ss/Android.bp
new file mode 100644
index 0000000..ebc1e1a
--- /dev/null
+++ b/lib/ss/Android.bp
@@ -0,0 +1,39 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-0BSD
+ // SPDX-license-identifier-MIT
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library {
+ name: "libext2_ss",
+ host_supported: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "ss_err.c",
+ "std_rqs.c",
+ "invocation.c",
+ "help.c",
+ "execute_cmd.c",
+ "listen.c",
+ "parse.c",
+ "error.c",
+ "prompt.c",
+ "request_tbl.c",
+ "list_rqs.c",
+ "pager.c",
+ "requests.c",
+ "data.c",
+ "get_readline.c",
+ ],
+ shared_libs: ["libext2_com_err"],
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
diff --git a/lib/ss/Makefile.in b/lib/ss/Makefile.in
new file mode 100644
index 0000000..bb50418
--- /dev/null
+++ b/lib/ss/Makefile.in
@@ -0,0 +1,234 @@
+#
+# Makefile for lib/ss
+#
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/ss
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+DLOPEN_LIB = @DLOPEN_LIB@
+
+@MCONFIG@
+
+LIBRARY= libss
+LIBDIR= ss
+
+ELF_VERSION = 2.0
+ELF_SO_VERSION = 2
+ELF_IMAGE = libss
+ELF_MYDIR = ss
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS = -lcom_err $(DLOPEN_LIB)
+
+BSDLIB_VERSION = 1.0
+BSDLIB_IMAGE = libss
+BSDLIB_MYDIR = ss
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+TAGS=etags
+MK_CMDS= _SS_DIR_OVERRIDE=$(srcdir) ./mk_cmds
+COMPILE_ET= _ET_DIR_OVERRIDE=$(srcdir)/../et ../et/compile_et
+
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -c $<
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) -DSHARED_ELF_LIB -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+# for the library
+
+# with ss_err.o first, ss_err.h should get rebuilt first too. should not
+# be relying on this, though.
+OBJS= ss_err.o \
+ std_rqs.o \
+ invocation.o help.o \
+ execute_cmd.o listen.o parse.o error.o prompt.o \
+ request_tbl.o list_rqs.o pager.o requests.o \
+ data.o get_readline.o
+
+SRCS= $(srcdir)/invocation.c $(srcdir)/help.c \
+ $(srcdir)/execute_cmd.c $(srcdir)/listen.c $(srcdir)/parse.c \
+ $(srcdir)/error.c $(srcdir)/prompt.c $(srcdir)/request_tbl.c \
+ $(srcdir)/list_rqs.c $(srcdir)/pager.c $(srcdir)/requests.c \
+ $(srcdir)/data.c $(srcdir)/get_readline.c
+
+all:: mk_cmds
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+
+CODE= $(SRCS) $(MKCMDSFILES)
+
+MKCMDSOBJS= mk_cmds.o utils.o options.o ct.tab.o cmd_tbl.lex.o
+
+MKCMDSFILES= mk_cmds.c utils.c options.c ct.y cmd_tbl.lex.l
+
+MKCMDSCSRCS= mk_cmds.c utils.c options.c ct.tab.c cmd_tbl.lex.c
+
+
+HFILES= ss.h ss_internal.h
+SHARE_FILES= ct_c.awk ct_c.sed
+
+INSTALL_HFILES= ss.h
+
+# for 'tags' and dependencies
+
+CFILES= $(SRCS) $(MKCMDSCSRCS) test_ss.c
+
+# for building archives
+
+FILES= $(SRCS) $(MKCMDSFILES) $(HFILES) \
+ ss_err.et std_rqs.ct Makefile \
+ test_ss.c ss
+
+all:: libss.a ss.pc # libss_p.a lint
+
+std_rqs.c: std_rqs.ct mk_cmds
+ $(E) " MK_CMDS $@"
+ $(Q) DIR=$(srcdir) $(MK_CMDS) $(srcdir)/std_rqs.ct
+
+std_rqs.o: ss_err.h
+
+test_cmd.c: test_cmd.ct mk_cmds
+ $(E) " MK_CMDS $@"
+ $(Q) DIR=$(srcdir) $(MK_CMDS) $(srcdir)/test_cmd.ct
+
+ss_err.c ss_err.h: ss_err.et
+ $(E) " COMPILE_ET ss_err.et"
+ $(Q) $(COMPILE_ET) $(srcdir)/ss_err.et
+
+ct.tab.c ct.tab.h: ct.y
+ $(RM) -f ct.tab.* y.*
+ $(YACC) -d $(srcdir)/ct.y
+ $(MV) -f y.tab.c ct.tab.c
+ $(MV) -f y.tab.h ct.tab.h
+
+#libss.o: $(OBJS)
+# $(LD) -r -s -o $@ $(OBJS)
+# $(CHMOD) -x $@
+
+mk_cmds: $(DEP_SUBSTITUTE) $(srcdir)/mk_cmds.sh.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE) $(srcdir)/mk_cmds.sh.in mk_cmds
+ $(Q) $(CHMOD) +x mk_cmds
+
+ss.pc: $(srcdir)/ss.pc.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); CONFIG_FILES=lib/ss/ss.pc ./config.status
+
+installdirs::
+ $(E) " MKDIR_P $(libdir) $(includedir)/ss $(datadir)/ss $(bindir) $(pkgconfigdir) $(man1dir)"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(includedir)/ss $(DESTDIR)$(datadir)/ss \
+ $(DESTDIR)$(bindir) $(DESTDIR)$(pkgconfigdir) \
+ $(DESTDIR)$(man1dir)
+
+install:: libss.a $(INSTALL_HFILES) installdirs ss_err.h mk_cmds ss.pc
+ $(E) " INSTALL_DATA $(DESTDIR)$(libdir)/libss.a"
+ $(Q) $(INSTALL_DATA) libss.a $(DESTDIR)$(libdir)/libss.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libss.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libss.a
+ $(Q) $(RM) -f $(DESTDIR)$(includedir)/ss/*
+ $(Q) for i in $(INSTALL_HFILES); do \
+ echo " INSTALL_DATA $(DESTDIR)$(includedir)/ss/$$i"; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/ss/$$i; \
+ done
+ $(E) " INSTALL_DATA $(includedir)/ss/ss_err.h"
+ $(Q) $(INSTALL_DATA) ss_err.h $(DESTDIR)$(includedir)/ss/ss_err.h
+ $(Q) for i in $(SHARE_FILES); do \
+ echo " INSTALL_DATA $(DESTDIR)$(datadir)/ss/$$i"; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(datadir)/ss/$$i; \
+ done
+ $(E) " INSTALL $(bindir)/mk_cmds"
+ $(Q) $(INSTALL) mk_cmds $(DESTDIR)$(bindir)/mk_cmds
+ $(E) " INSTALL_DATA $(man1dir)/mk_cmds.1"
+ $(Q) $(INSTALL_DATA) $(srcdir)/mk_cmds.1 $(DESTDIR)$(man1dir)/mk_cmds.1
+ $(E) " INSTALL_DATA $(pkgconfigdir)/ss.pc"
+ $(Q) $(INSTALL_DATA) ss.pc $(DESTDIR)$(pkgconfigdir)/ss.pc
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/libss.a $(DESTDIR)$(bindir)/mk_cmds \
+ $(DESTDIR)$(pkgconfigdir)/ss.pc \
+ $(DESTDIR)$(man1dir)/mk_cmds.1
+ $(RM) -rf $(DESTDIR)$(includedir)/ss $(DESTDIR)$(datadir)/ss
+
+test_ss: test_ss.o test_cmd.o $(DEPLIBSS) $(DEPLIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o $@ test_ss.o test_cmd.o $(ALL_CFLAGS) $(ALL_LDFLAGS) \
+ $(LIBSS) $(LIBCOM_ERR) $(SYSLIBS)
+
+fullcheck check:: all test_ss
+ $(E) " RUN TEST test_ss"
+ -@($(TESTENV) ./test_ss -f $(srcdir)/test_script > test_out 2>&1; exit 0)
+ $(Q) if diff test_out $(srcdir)/test_script_expected > test.diff; then \
+ true ; else echo "Regression test for ss library failed!"; exit 1 ; fi
+
+clean::
+ $(RM) -f ../libss.a libss.a mk_cmds ss_err.h ss_err.c std_rqs.c \
+ tst_cmds.c test_ss test_out test.diff *.o *~ \#* *.bak core \
+ test_cmd.c ss.pc
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend Makefile ss.pc \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+#
+# Hack to parallel makes recognize dependencies correctly.
+#
+$(top_builddir)/lib/ss/ss_err.h: ss_err.h
+
+$(OBJS): subdirs
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+invocation.o: $(srcdir)/invocation.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+help.o: $(srcdir)/help.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+execute_cmd.o: $(srcdir)/execute_cmd.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+listen.o: $(srcdir)/listen.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+parse.o: $(srcdir)/parse.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+error.o: $(srcdir)/error.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ss_internal.h $(srcdir)/ss.h $(top_builddir)/lib/ss/ss_err.h
+prompt.o: $(srcdir)/prompt.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+request_tbl.o: $(srcdir)/request_tbl.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+list_rqs.o: $(srcdir)/list_rqs.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+pager.o: $(srcdir)/pager.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+requests.o: $(srcdir)/requests.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+data.o: $(srcdir)/data.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
+get_readline.o: $(srcdir)/get_readline.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ss_internal.h $(srcdir)/ss.h \
+ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h
diff --git a/lib/ss/ct_c.awk b/lib/ss/ct_c.awk
new file mode 100644
index 0000000..a4424c8
--- /dev/null
+++ b/lib/ss/ct_c.awk
@@ -0,0 +1,75 @@
+/^command_table / {
+ cmdtbl = $2;
+ printf "/* %s.c - automatically generated from %s.ct */\n", \
+ rootname, rootname > outfile
+ print "#include <ss/ss.h>" > outfile
+ print "" >outfile
+}
+
+/^BOR$/ {
+ cmdnum++
+ options = 0
+ cmdtab = ""
+ printf "static char const * const ssu%05d[] = {\n", cmdnum > outfile
+}
+
+/^sub/ {
+ subr = substr($0, 6, length($0)-5)
+}
+
+/^hlp/ {
+ help = substr($0, 6, length($0)-5)
+}
+
+/^cmd/ {
+ cmd = substr($0, 6, length($0)-5)
+ printf "%s\"%s\",\n", cmdtab, cmd > outfile
+ cmdtab = " "
+}
+
+/^opt/ {
+ opt = substr($0, 6, length($0)-5)
+ if (opt == "dont_list") {
+ options += 1
+ }
+ if (opt == "dont_summarize") {
+ options += 2
+ }
+}
+
+/^EOR/ {
+ print " (char const *)0" > outfile
+ print "};" > outfile
+ printf "extern void %s __SS_PROTO;\n", subr > outfile
+ # Work around a bug in gawk 3.0.5
+ awk_bug = cmdnum
+ subr_tab[awk_bug] = subr
+ options_tab[awk_bug] = options
+ help_tab[awk_bug] = help
+}
+
+/^[0-9]/ {
+ linenum = $1;
+}
+
+/^ERROR/ {
+ error = substr($0, 8, length($0)-7)
+ printf "Error in line %d: %s\n", linenum, error
+ print "#__ERROR_IN_FILE__" > outfile
+}
+
+END {
+ printf "static ss_request_entry ssu%05d[] = {\n", cmdnum+1 > outfile
+ for (i=1; i <= cmdnum; i++) {
+ printf " { ssu%05d,\n", i > outfile
+ printf " %s,\n", subr_tab[i] > outfile
+ printf " \"%s\",\n", help_tab[i] > outfile
+ printf " %d },\n", options_tab[i] > outfile
+ }
+ print " { 0, 0, 0, 0 }" > outfile
+ print "};" > outfile
+ print "" > outfile
+ printf "ss_request_table %s = { 2, ssu%05d };\n", \
+ cmdtbl, cmdnum+1 > outfile
+}
+
diff --git a/lib/ss/ct_c.sed b/lib/ss/ct_c.sed
new file mode 100644
index 0000000..9e5eebb
--- /dev/null
+++ b/lib/ss/ct_c.sed
@@ -0,0 +1,160 @@
+#
+# This script parses a command_table file into something which is a bit
+# easier for an awk script to understand.
+#
+# Input syntax: a .ct file
+#
+# Output syntax:
+# (for the command_table line)
+# command_table <command_table>
+#
+#(for each request definition)
+# BOR
+# sub: <subroutine name>
+# hlp: <help text>
+# cmd: <command>
+# opt: <option>
+# EOR
+# (there may be more than one 'cmd' or 'opt' line
+#
+# A number sent to the output represents a parse error --- it will be
+# followed by the next line which will have the form:
+# ERROR: <error text>
+#
+# The design of this output syntax is such that it should be easy for
+# an awk script to parse.
+
+#
+# The first section of this script is just to canonicalize the file.
+# It removes comments, and puts each command_table request onto a single
+# line
+#
+:FIRST
+y/ / /
+s/^ *//
+s/#.*$//
+/; *$/!{
+N
+y/ / /
+s/\n */ /
+bFIRST
+}
+s/, */, /g
+#
+# Now we take care of some syntactic sugar.....
+#
+/^unimplemented/ {
+ s/^unimplemented [A-Za-z_0-9]*/request ss_unimplemented/
+ s/;/, (dont_list, dont_summarize);/
+}
+/^unknown/ {
+ s/^unknown /request ss_unknown, "", /
+}
+#
+# Dispatch based on the keyword.... illegal keywords are prefixed by ERROR:
+# and are handled by the awk script.
+#
+/^command_table /bCMD
+/^request /bREQUEST
+/^end;/bEND
+s/ .*//
+s/^/ERROR: unknown keyword: /
+=
+b
+#
+# Handle the command_table keyword
+#
+:CMD
+s/;$//
+p
+d
+b
+#
+# Handle the request keyword --- this is the heart of the sed script.
+#
+:REQUEST
+s/^request *//
+h
+i\
+BOR
+# First, parse out the subroutine name
+s/^/sub: /
+s/,.*//
+p
+# Next, parse out the help message, being careful to handle a quoted string
+g
+s/^[^,]*, *//
+h
+/^"/ {
+ s/^"//
+ s/".*//
+ x
+ s/^"[^"]*", *//
+ x
+ b EMITHLP
+}
+s/[^a-zA-Z0-9].*//
+x
+s/[a-zA-Z0-9]*, *//
+x
+:EMITHLP
+s/^/hlp: /
+p
+# Next take care of the command names
+:CMDLIST
+g
+/^(/b OPTIONS
+/^;/b EOR
+/^"/ {
+ s/^"//
+ s/".*//
+ x
+ s/^"[^"]*"//
+ s/, *//
+ x
+ b EMITREQ
+}
+s/[^A-Za-z_0-9].*//
+x
+s/[A-Za-z_0-9]*//
+s/, *//
+x
+:EMITREQ
+s/^/cmd: /
+p
+b CMDLIST
+#
+# Here we parse the list of options.
+#
+: OPTIONS
+g
+s/^(//
+h
+: OPTLIST
+/^)/ b EOR
+/^[^A-Za-z_0-9]/ {
+ =
+ c\
+ERROR: parse error in options list
+}
+s/[^A-Za-z_0-9].*//
+x
+s/[A-Za-z_0-9]*//
+s/, *//
+x
+s/^/opt: /
+p
+g
+b OPTLIST
+: EOR
+c\
+EOR\
+
+d
+b
+#
+# Handle the end keyword --- it's basically ignored.
+#
+:END
+d
+b
diff --git a/lib/ss/data.c b/lib/ss/data.c
new file mode 100644
index 0000000..e8245bd
--- /dev/null
+++ b/lib/ss/data.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 1987, 1988, 1989 Massachusetts Institute of Technology
+ * (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 "config.h"
+#include <stdio.h>
+#include "ss_internal.h"
+
+ss_data **_ss_table = (ss_data **)NULL;
+char *_ss_pager_name = (char *)NULL;
diff --git a/lib/ss/error.c b/lib/ss/error.c
new file mode 100644
index 0000000..656b71b
--- /dev/null
+++ b/lib/ss/error.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1987, 1988, 1989 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 "config.h"
+#include <stdio.h>
+
+#include "et/com_err.h"
+#include "ss_internal.h"
+
+#include <stdarg.h>
+
+char *ss_name(int sci_idx)
+{
+ register char *ret_val;
+ register ss_data *infop;
+
+ infop = ss_info(sci_idx);
+ if (infop->current_request == (char const *)NULL) {
+ ret_val = malloc((unsigned)
+ (strlen(infop->subsystem_name)+1)
+ * sizeof(char));
+ if (ret_val == (char *)NULL)
+ return((char *)NULL);
+ strcpy(ret_val, infop->subsystem_name);
+ return(ret_val);
+ }
+ else {
+ register char *cp;
+ register char const *cp1;
+ ret_val = malloc((unsigned)sizeof(char) *
+ (strlen(infop->subsystem_name)+
+ strlen(infop->current_request)+
+ 4));
+ if (ret_val == (char *)NULL)
+ return ((char *)NULL);
+ cp = ret_val;
+ cp1 = infop->subsystem_name;
+ while (*cp1)
+ *cp++ = *cp1++;
+ *cp++ = ' ';
+ *cp++ = '(';
+ cp1 = infop->current_request;
+ while (*cp1)
+ *cp++ = *cp1++;
+ *cp++ = ')';
+ *cp = '\0';
+ return(ret_val);
+ }
+}
+
+void ss_error (int sci_idx, long code, const char * fmt, ...)
+{
+ register char *whoami;
+ va_list pvar;
+
+ va_start (pvar, fmt);
+ whoami = ss_name (sci_idx);
+ com_err_va (whoami, code, fmt, pvar);
+ free (whoami);
+ va_end(pvar);
+}
+
+void ss_perror(int sci_idx, long code, char const *msg) /* for compatibility */
+{
+ ss_error (sci_idx, code, "%s", msg);
+}
diff --git a/lib/ss/execute_cmd.c b/lib/ss/execute_cmd.c
new file mode 100644
index 0000000..d092134
--- /dev/null
+++ b/lib/ss/execute_cmd.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 1987, 1988, 1989 by Massachusetts Institute of Technology
+ *
+ * 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 "config.h"
+#ifdef HAS_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "ss_internal.h"
+#include <stdio.h>
+
+static int check_request_table PROTOTYPE((ss_request_table *rqtbl, int argc,
+ char *argv[], int sci_idx));
+static int really_execute_command PROTOTYPE((int sci_idx, int argc,
+ char **argv[]));
+
+/*
+ * get_request(tbl, idx)
+ *
+ * Function:
+ * Gets the idx'th request from the request table pointed to
+ * by tbl.
+ * Arguments:
+ * tbl (ss_request_table *)
+ * pointer to request table
+ * idx (int)
+ * index into table
+ * Returns:
+ * (ss_request_entry *)
+ * pointer to request table entry
+ * Notes:
+ * Has been replaced by a macro.
+ */
+
+#ifdef __SABER__
+/* sigh. saber won't deal with pointer-to-const-struct */
+static struct _ss_request_entry * get_request (tbl, idx)
+ ss_request_table * tbl;
+ int idx;
+{
+ struct _ss_request_table *tbl1 = (struct _ss_request_table *) tbl;
+ struct _ss_request_entry *e = (struct _ss_request_entry *) tbl1->requests;
+ return e + idx;
+}
+#else
+#define get_request(tbl,idx) ((tbl) -> requests + (idx))
+#endif
+
+/*
+ * check_request_table(rqtbl, argc, argv, sci_idx)
+ *
+ * Function:
+ * If the command string in argv[0] is in the request table, execute
+ * the commands and return error code 0. Otherwise, return error
+ * code ss_et_command_not_found.
+ * Arguments:
+ * rqtbl (ss_request_table *)
+ * pointer to request table
+ * argc (int)
+ * number of elements in argv[]
+ * argv (char *[])
+ * argument string array
+ * sci_idx (int)
+ * ss-internal index for subsystem control info structure
+ * Returns:
+ * (int)
+ * zero if command found, ss_et_command_not_found otherwise
+ * Notes:
+ */
+
+static int check_request_table(register ss_request_table *rqtbl, int argc,
+ char *argv[], int sci_idx)
+{
+#ifdef __SABER__
+ struct _ss_request_entry *request;
+#else
+ register ss_request_entry *request;
+#endif
+ register ss_data *info;
+ register char const * const * name;
+ char *string = argv[0];
+ int i;
+
+ info = ss_info(sci_idx);
+ info->argc = argc;
+ info->argv = argv;
+ for (i = 0; (request = get_request(rqtbl, i))->command_names; i++) {
+ for (name = request->command_names; *name; name++)
+ if (!strcmp(*name, string)) {
+ info->current_request = request->command_names[0];
+ (request->function)(argc, (const char *const *) argv,
+ sci_idx,info->info_ptr);
+ info->current_request = (char *)NULL;
+ return(0);
+ }
+ }
+ return(SS_ET_COMMAND_NOT_FOUND);
+}
+
+/*
+ * really_execute_command(sci_idx, argc, argv)
+ *
+ * Function:
+ * Fills in the argc, argv values in the subsystem entry and
+ * call the appropriate routine.
+ * Arguments:
+ * sci_idx (int)
+ * ss-internal index for subsystem control info structure
+ * argc (int)
+ * number of arguments in argument list
+ * argv (char **[])
+ * pointer to parsed argument list (may be reallocated
+ * on abbrev expansion)
+ *
+ * Returns:
+ * (int)
+ * Zero if successful, ss_et_command_not_found otherwise.
+ * Notes:
+ */
+
+static int really_execute_command(int sci_idx, int argc, char **argv[])
+{
+ register ss_request_table **rqtbl;
+ register ss_data *info;
+
+ info = ss_info(sci_idx);
+
+ for (rqtbl = info->rqt_tables; *rqtbl; rqtbl++) {
+ if (check_request_table (*rqtbl, argc, *argv, sci_idx) == 0)
+ return(0);
+ }
+ return(SS_ET_COMMAND_NOT_FOUND);
+}
+
+/*
+ * ss_execute_command(sci_idx, argv)
+ *
+ * Function:
+ * Executes a parsed command list within the subsystem.
+ * Arguments:
+ * sci_idx (int)
+ * ss-internal index for subsystem control info structure
+ * argv (char *[])
+ * parsed argument list
+ * Returns:
+ * (int)
+ * Zero if successful, ss_et_command_not_found otherwise.
+ * Notes:
+ */
+
+int ss_execute_command(int sci_idx, register char *argv[])
+{
+ register int i, argc;
+ char **argp;
+
+ argc = 0;
+ for (argp = argv; *argp; argp++)
+ argc++;
+ argp = (char **)malloc((argc+1)*sizeof(char *));
+ if (!argp)
+ return(ENOMEM);
+ for (i = 0; i <= argc; i++)
+ argp[i] = argv[i];
+ i = really_execute_command(sci_idx, argc, &argp);
+ free(argp);
+ return(i);
+}
+
+/*
+ * ss_execute_line(sci_idx, line_ptr)
+ *
+ * Function:
+ * Parses and executes a command line within a subsystem.
+ * Arguments:
+ * sci_idx (int)
+ * ss-internal index for subsystem control info structure
+ * line_ptr (char *)
+ * Pointer to command line to be parsed.
+ * Returns:
+ * (int)
+ * Error code.
+ * Notes:
+ */
+
+int ss_execute_line(int sci_idx, char *line_ptr)
+{
+ char **argv;
+ int argc, ret;
+
+ /* flush leading whitespace */
+ while (line_ptr[0] == ' ' || line_ptr[0] == '\t')
+ line_ptr++;
+
+ /* check if it should be sent to operating system for execution */
+ if (*line_ptr == '!') {
+ if (ss_info(sci_idx)->flags.escape_disabled)
+ return SS_ET_ESCAPE_DISABLED;
+ else {
+ line_ptr++;
+ return (system(line_ptr) < 0) ? errno : 0;
+ }
+ }
+
+ /* parse it */
+ argv = ss_parse(sci_idx, line_ptr, &argc);
+ if (argc == 0) {
+ free(argv);
+ return 0;
+ }
+
+ /* look it up in the request tables, execute if found */
+ ret = really_execute_command (sci_idx, argc, &argv);
+
+ free(argv);
+
+ return(ret);
+}
diff --git a/lib/ss/get_readline.c b/lib/ss/get_readline.c
new file mode 100644
index 0000000..aa16157
--- /dev/null
+++ b/lib/ss/get_readline.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2003 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 "config.h"
+#ifdef HAS_STDLIB_H
+#include <stdlib.h>
+#endif
+#include "ss_internal.h"
+#define size sizeof(ss_data *)
+#ifdef HAVE_DLOPEN
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_DLOPEN
+static void ss_release_readline(ss_data *info)
+{
+ if (!info->readline_handle)
+ return;
+
+ info->readline = 0;
+ info->add_history = 0;
+ info->redisplay = 0;
+ info->rl_completion_matches = 0;
+ dlclose(info->readline_handle);
+ info->readline_handle = 0;
+}
+#endif
+
+/* Libraries we will try to use for readline/editline functionality */
+#define DEFAULT_LIBPATH "libreadline.so.8:libreadline.so.7:libreadline.so.6:libreadline.so.5:libreadline.so.4:libreadline.so:libedit.so.2:libedit.so:libeditline.so.0:libeditline.so"
+
+#ifdef HAVE_DLOPEN
+void ss_get_readline(int sci_idx)
+{
+ void *handle = NULL;
+ ss_data *info = ss_info(sci_idx);
+ const char **t, *libpath = 0;
+ char *tmp, *cp, *next;
+ char **(**completion_func)(const char *, int, int);
+
+ if (info->readline_handle)
+ return;
+
+ libpath = ss_safe_getenv("SS_READLINE_PATH");
+ if (!libpath)
+ libpath = DEFAULT_LIBPATH;
+ if (*libpath == 0 || !strcmp(libpath, "none"))
+ return;
+
+ tmp = malloc(strlen(libpath)+1);
+ if (!tmp)
+ return;
+ strcpy(tmp, libpath);
+ for (cp = tmp; cp; cp = next) {
+ next = strchr(cp, ':');
+ if (next)
+ *next++ = 0;
+ if (*cp == 0)
+ continue;
+ if ((handle = dlopen(cp, RTLD_NOW))) {
+ /* printf("Using %s for readline library\n", cp); */
+ break;
+ }
+ }
+ free(tmp);
+ if (!handle)
+ return;
+
+ info->readline_handle = handle;
+ info->readline = (char *(*)(const char *))
+ dlsym(handle, "readline");
+ info->add_history = (void (*)(const char *))
+ dlsym(handle, "add_history");
+ info->redisplay = (void (*)(void))
+ dlsym(handle, "rl_forced_update_display");
+ info->rl_completion_matches = (char **(*)(const char *,
+ char *(*)(const char *, int)))
+ dlsym(handle, "rl_completion_matches");
+ if ((t = dlsym(handle, "rl_readline_name")) != NULL)
+ *t = info->subsystem_name;
+ if ((completion_func =
+ dlsym(handle, "rl_attempted_completion_function")) != NULL)
+ *completion_func = ss_rl_completion;
+ info->readline_shutdown = ss_release_readline;
+}
+#else
+void ss_get_readline(int sci_idx __SS_ATTR((unused)))
+{
+}
+#endif
diff --git a/lib/ss/help.c b/lib/ss/help.c
new file mode 100644
index 0000000..54c78f2
--- /dev/null
+++ b/lib/ss/help.c
@@ -0,0 +1,185 @@
+/*
+ * 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 "config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#ifdef NEED_SYS_FCNTL_H
+/* just for O_* */
+#include <sys/fcntl.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include "ss_internal.h"
+
+void ss_help(int argc, char const * const *argv, int sci_idx, pointer info_ptr)
+{
+ char *buffer;
+ char const *request_name;
+ int code;
+ int fd, child;
+ register int idx;
+ register ss_data *info;
+
+ request_name = ss_current_request(sci_idx, &code);
+ if (code != 0) {
+ ss_perror(sci_idx, code, "");
+ return; /* no ss_abort_line, if invalid invocation */
+ }
+ if (argc == 1) {
+ ss_list_requests(argc, argv, sci_idx, info_ptr);
+ return;
+ }
+ else if (argc != 2) {
+ /* should do something better than this */
+ buffer = malloc(80+2*strlen(request_name));
+ if (!buffer) {
+ ss_perror(sci_idx, 0,
+ "couldn't allocate memory to print usage message");
+ return;
+ }
+ sprintf(buffer, "usage:\n\t%s [topic|command]\nor\t%s\n",
+ request_name, request_name);
+ ss_perror(sci_idx, 0, buffer);
+ free(buffer);
+ return;
+ }
+ info = ss_info(sci_idx);
+ if (info->info_dirs == (char **)NULL) {
+ ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL);
+ return;
+ }
+ if (info->info_dirs[0] == (char *)NULL) {
+ ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL);
+ return;
+ }
+ for (fd = -1, idx = 0; info->info_dirs[idx] != (char *)NULL; idx++) {
+ buffer = malloc(strlen (info->info_dirs[idx]) + 1 +
+ strlen (argv[1]) + 6);
+ if (!buffer) {
+ ss_perror(sci_idx, 0,
+ "couldn't allocate memory for help filename");
+ return;
+ }
+ (void) strcpy(buffer, info->info_dirs[idx]);
+ (void) strcat(buffer, "/");
+ (void) strcat(buffer, argv[1]);
+ (void) strcat(buffer, ".info");
+ fd = open(buffer, O_RDONLY);
+ free(buffer);
+ if (fd >= 0)
+ break;
+ }
+ if (fd < 0) {
+#define MSG "No info found for "
+ char *buf = malloc(strlen (MSG) + strlen (argv[1]) + 1);
+ if (!buf) {
+ ss_perror(sci_idx, 0,
+ "couldn't allocate memory to print error message");
+ return;
+ }
+ strcpy(buf, MSG);
+ strcat(buf, argv[1]);
+ ss_perror(sci_idx, 0, buf);
+ free(buf);
+ return;
+ }
+ switch (child = fork()) {
+ case -1:
+ ss_perror(sci_idx, errno, "Can't fork for pager");
+ (void) close(fd);
+ return;
+ case 0:
+ (void) dup2(fd, 0); /* put file on stdin */
+ ss_page_stdin();
+ default:
+ (void) close(fd); /* what can we do if it fails? */
+ while (wait(NULL) != child) {
+ /* do nothing if wrong pid */
+ };
+ }
+}
+
+#ifndef HAVE_DIRENT_H
+#include <sys/dir.h>
+#else
+#include <dirent.h>
+#endif
+
+void ss_add_info_dir(int sci_idx, char *info_dir, int *code_ptr)
+{
+ register ss_data *info;
+ DIR *d;
+ int n_dirs;
+ register char **dirs;
+
+ info = ss_info(sci_idx);
+ if (info_dir == NULL || *info_dir == '\0') {
+ *code_ptr = SS_ET_NO_INFO_DIR;
+ return;
+ }
+ if ((d = opendir(info_dir)) == (DIR *)NULL) {
+ *code_ptr = errno;
+ return;
+ }
+ closedir(d);
+ dirs = info->info_dirs;
+ for (n_dirs = 0; dirs[n_dirs] != (char *)NULL; n_dirs++)
+ ; /* get number of non-NULL dir entries */
+ dirs = (char **)realloc((char *)dirs,
+ (unsigned)(n_dirs + 2)*sizeof(char *));
+ if (dirs == (char **)NULL) {
+ *code_ptr = ENOMEM;
+ return;
+ }
+ info->info_dirs = dirs;
+ dirs[n_dirs + 1] = (char *)NULL;
+ dirs[n_dirs] = malloc((unsigned)strlen(info_dir)+1);
+ if (dirs[n_dirs] == (char *)NULL) {
+ *code_ptr = ENOMEM;
+ return;
+ }
+ strcpy(dirs[n_dirs], info_dir);
+ *code_ptr = 0;
+}
+
+void ss_delete_info_dir(int sci_idx, char *info_dir, int *code_ptr)
+{
+ register char **i_d;
+ register char **info_dirs;
+
+ info_dirs = ss_info(sci_idx)->info_dirs;
+ for (i_d = info_dirs; *i_d; i_d++) {
+ if (!strcmp(*i_d, info_dir)) {
+ while (*i_d) {
+ *i_d = *(i_d+1);
+ i_d++;
+ }
+ *code_ptr = 0;
+ return;
+ }
+ }
+ *code_ptr = SS_ET_NO_INFO_DIR;
+}
diff --git a/lib/ss/invocation.c b/lib/ss/invocation.c
new file mode 100644
index 0000000..7d7458a
--- /dev/null
+++ b/lib/ss/invocation.c
@@ -0,0 +1,132 @@
+/*
+ * 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 "config.h"
+#ifdef HAS_STDLIB_H
+#include <stdlib.h>
+#endif
+#include "ss_internal.h"
+#define size sizeof(ss_data *)
+#ifdef HAVE_DLOPEN
+#include <dlfcn.h>
+#endif
+#include <errno.h>
+
+int ss_create_invocation(const char *subsystem_name, const char *version_string,
+ void *info_ptr, ss_request_table *request_table_ptr,
+ int *code_ptr)
+{
+ int sci_idx;
+ ss_data *new_table = NULL;
+ ss_data **table = NULL;
+ ss_data **realloc_table = NULL;
+
+ *code_ptr = 0;
+ table = _ss_table;
+ new_table = (ss_data *) malloc(sizeof(ss_data));
+ if (!new_table)
+ goto out;
+ memset(new_table, 0, sizeof(ss_data));
+
+ if (table == (ss_data **) NULL) {
+ table = (ss_data **) malloc(2 * size);
+ if (!table)
+ goto out;
+ table[0] = table[1] = (ss_data *)NULL;
+ }
+ initialize_ss_error_table ();
+
+ for (sci_idx = 1; table[sci_idx] != (ss_data *)NULL; sci_idx++)
+ ;
+ realloc_table = (ss_data **) realloc((char *)table,
+ ((unsigned)sci_idx+2)*size);
+ if (realloc_table == NULL)
+ goto out;
+
+ table = realloc_table;
+ table[sci_idx+1] = (ss_data *) NULL;
+ table[sci_idx] = new_table;
+
+ new_table->subsystem_name = subsystem_name;
+ new_table->subsystem_version = version_string;
+ new_table->argv = (char **)NULL;
+ new_table->current_request = (char *)NULL;
+ new_table->info_dirs = (char **)malloc(sizeof(char *));
+ if (!new_table->info_dirs)
+ goto out;
+
+ *new_table->info_dirs = (char *)NULL;
+ new_table->info_ptr = info_ptr;
+ new_table->prompt = malloc((unsigned)strlen(subsystem_name)+4);
+ if (!new_table->prompt)
+ goto out;
+
+ strcpy(new_table->prompt, subsystem_name);
+ strcat(new_table->prompt, ": ");
+#ifdef silly
+ new_table->abbrev_info = ss_abbrev_initialize("/etc/passwd", code_ptr);
+#else
+ new_table->abbrev_info = NULL;
+#endif
+ new_table->flags.escape_disabled = 0;
+ new_table->flags.abbrevs_disabled = 0;
+ new_table->rqt_tables =
+ (ss_request_table **) calloc(2, sizeof(ss_request_table *));
+ if (!new_table->rqt_tables)
+ goto out;
+
+ *(new_table->rqt_tables) = request_table_ptr;
+ *(new_table->rqt_tables+1) = (ss_request_table *) NULL;
+
+ new_table->readline_handle = 0;
+ new_table->readline_shutdown = 0;
+ new_table->readline = 0;
+ new_table->add_history = 0;
+ new_table->redisplay = 0;
+ new_table->rl_completion_matches = 0;
+ _ss_table = table;
+#if defined(HAVE_DLOPEN) && defined(SHARED_ELF_LIB)
+ ss_get_readline(sci_idx);
+#endif
+ return(sci_idx);
+
+out:
+ if (new_table) {
+ free(new_table->prompt);
+ free(new_table->info_dirs);
+ }
+ free(new_table);
+ free(table);
+ *code_ptr = ENOMEM;
+ return 0;
+
+}
+
+void
+ss_delete_invocation(int sci_idx)
+{
+ register ss_data *t;
+ int ignored_code;
+
+ t = ss_info(sci_idx);
+ free(t->prompt);
+ free(t->rqt_tables);
+ while(t->info_dirs[0] != (char *)NULL)
+ ss_delete_info_dir(sci_idx, t->info_dirs[0], &ignored_code);
+ free(t->info_dirs);
+#if defined(HAVE_DLOPEN) && defined(SHARED_ELF_LIB)
+ if (t->readline_shutdown)
+ (*t->readline_shutdown)(t);
+#endif
+ free(t);
+}
diff --git a/lib/ss/list_rqs.c b/lib/ss/list_rqs.c
new file mode 100644
index 0000000..89e37bb
--- /dev/null
+++ b/lib/ss/list_rqs.c
@@ -0,0 +1,92 @@
+/*
+ * 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 "config.h"
+#include "ss_internal.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/wait.h>
+
+typedef void sigret_t;
+
+void ss_list_requests(int argc __SS_ATTR((unused)),
+ const char * const *argv __SS_ATTR((unused)),
+ int sci_idx, void *infop __SS_ATTR((unused)))
+{
+ ss_request_entry *entry;
+ char const * const *name;
+ int i, spacing;
+ ss_request_table **table;
+
+ FILE *output;
+ int fd;
+ sigset_t omask, igmask;
+ sigret_t (*func)(int);
+#ifndef NO_FORK
+ int waitb;
+#endif
+
+ sigemptyset(&igmask);
+ sigaddset(&igmask, SIGINT);
+ sigprocmask(SIG_BLOCK, &igmask, &omask);
+ func = signal(SIGINT, SIG_IGN);
+ fd = ss_pager_create();
+ if (fd < 0) {
+ perror("ss_pager_create");
+ (void) signal(SIGINT, func);
+ return;
+ }
+ output = fdopen(fd, "w");
+ if (!output) {
+ perror("fdopen");
+ close(fd);
+ (void) signal(SIGINT, func);
+ return;
+ }
+ sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
+
+ fprintf (output, "Available %s requests:\n\n",
+ ss_info (sci_idx) -> subsystem_name);
+
+ for (table = ss_info(sci_idx)->rqt_tables; *table; table++) {
+ entry = (*table)->requests;
+ for (; entry->command_names; entry++) {
+ spacing = -2;
+ if (entry->flags & SS_OPT_DONT_LIST)
+ continue;
+ for (name = entry->command_names; *name; name++) {
+ int len = strlen(*name);
+ fputs(*name, output);
+ spacing += len + 2;
+ if (name[1]) {
+ fputs(", ", output);
+ }
+ }
+ if (spacing > 23) {
+ fputc('\n', output);
+ spacing = 0;
+ }
+ for (i = 0; i < 25 - spacing; i++)
+ fputc(' ', output);
+ fputs(entry->info_string, output);
+ fputc('\n', output);
+ }
+ }
+ fclose(output);
+#ifndef NO_FORK
+ wait(&waitb);
+#endif
+ (void) signal(SIGINT, func);
+}
diff --git a/lib/ss/listen.c b/lib/ss/listen.c
new file mode 100644
index 0000000..9578c3e
--- /dev/null
+++ b/lib/ss/listen.c
@@ -0,0 +1,200 @@
+/*
+ * Listener loop for subsystem library libss.a.
+ *
+ * $Header$
+ * $Locker$
+ *
+ * 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 "config.h"
+#include "ss_internal.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/param.h>
+
+typedef void sigret_t;
+
+static ss_data *current_info;
+static jmp_buf listen_jmpb;
+static sigret_t (*sig_cont)(int);
+
+static sigret_t print_prompt(int sig __SS_ATTR((unused)))
+{
+ if (current_info->redisplay)
+ (*current_info->redisplay)();
+ else {
+ (void) fputs(current_info->prompt, stdout);
+ (void) fflush(stdout);
+ }
+}
+
+static sigret_t listen_int_handler(int sig __SS_ATTR((unused)))
+{
+ putc('\n', stdout);
+ signal(SIGINT, listen_int_handler);
+ longjmp(listen_jmpb, 1);
+}
+
+int ss_listen (int sci_idx)
+{
+ char *cp;
+ ss_data *info;
+ sigret_t (*sig_int)(int), (*old_sig_cont)(int);
+ char input[BUFSIZ];
+ sigset_t omask, igmask;
+ int code;
+ jmp_buf old_jmpb;
+ ss_data *old_info = current_info;
+ char *line;
+
+ current_info = info = ss_info(sci_idx);
+ sig_cont = (sigret_t (*)(int)) 0;
+ info->abort = 0;
+ sigemptyset(&igmask);
+ sigaddset(&igmask, SIGINT);
+ sigprocmask(SIG_BLOCK, &igmask, &omask);
+ memcpy(old_jmpb, listen_jmpb, sizeof(jmp_buf));
+ sig_int = signal(SIGINT, listen_int_handler);
+ setjmp(listen_jmpb);
+ sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0);
+
+ while(!info->abort) {
+ old_sig_cont = sig_cont;
+ sig_cont = signal(SIGCONT, print_prompt);
+ if (sig_cont == print_prompt)
+ sig_cont = old_sig_cont;
+ if (info->readline) {
+ line = (*info->readline)(current_info->prompt);
+ } else {
+ print_prompt(0);
+ if (fgets(input, BUFSIZ, stdin) == input)
+ line = input;
+ else
+ line = NULL;
+
+ input[BUFSIZ-1] = 0;
+ }
+ if (line == NULL) {
+ code = SS_ET_EOF;
+ (void) signal(SIGCONT, sig_cont);
+ goto egress;
+ }
+
+ cp = strchr(line, '\n');
+ if (cp) {
+ *cp = '\0';
+ if (cp == line)
+ continue;
+ }
+ (void) signal(SIGCONT, sig_cont);
+ if (info->add_history)
+ (*info->add_history)(line);
+
+ code = ss_execute_line (sci_idx, line);
+ if (code == SS_ET_COMMAND_NOT_FOUND) {
+ register char *c = line;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ cp = strchr (c, ' ');
+ if (cp)
+ *cp = '\0';
+ cp = strchr (c, '\t');
+ if (cp)
+ *cp = '\0';
+ ss_error (sci_idx, 0,
+ "Unknown request \"%s\". Type \"?\" for a request list.",
+ c);
+ }
+ if (info->readline)
+ free(line);
+ }
+ code = 0;
+egress:
+ (void) signal(SIGINT, sig_int);
+ memcpy(listen_jmpb, old_jmpb, sizeof(jmp_buf));
+ current_info = old_info;
+ return code;
+}
+
+void ss_abort_subsystem(int sci_idx, int code)
+{
+ ss_info(sci_idx)->abort = 1;
+ ss_info(sci_idx)->exit_status = code;
+
+}
+
+void ss_quit(int argc __SS_ATTR((unused)),
+ const char * const *argv __SS_ATTR((unused)),
+ int sci_idx, pointer infop __SS_ATTR((unused)))
+{
+ ss_abort_subsystem(sci_idx, 0);
+}
+
+#ifdef HAVE_DLOPEN
+#define get_request(tbl,idx) ((tbl) -> requests + (idx))
+
+static char *cmd_generator(const char *text, int state)
+{
+ static int len;
+ static ss_request_table **rqtbl;
+ static int curr_rqt;
+ static char const * const * name;
+ ss_request_entry *request;
+ char *ret;
+
+ if (state == 0) {
+ len = strlen(text);
+ rqtbl = current_info->rqt_tables;
+ if (!rqtbl || !*rqtbl)
+ return 0;
+ curr_rqt = 0;
+ name = 0;
+ }
+
+ while (1) {
+ if (!name || !*name) {
+ request = get_request(*rqtbl, curr_rqt++);
+ name = request->command_names;
+ if (!name) {
+ rqtbl++;
+ if (*rqtbl) {
+ curr_rqt = 0;
+ continue;
+ } else
+ break;
+ }
+ }
+ if (strncmp(*name, text, len) == 0) {
+ ret = malloc(strlen(*name)+1);
+ if (ret)
+ strcpy(ret, *name);
+ name++;
+ return ret;
+ }
+ name++;
+ }
+
+ return 0;
+}
+
+char **ss_rl_completion(const char *text, int start,
+ int end __SS_ATTR((unused)))
+{
+ if ((start == 0) && current_info->rl_completion_matches)
+ return (*current_info->rl_completion_matches)
+ (text, cmd_generator);
+ return 0;
+}
+#endif
+
diff --git a/lib/ss/mit-sipb-copyright.h b/lib/ss/mit-sipb-copyright.h
new file mode 100644
index 0000000..ffcfc38
--- /dev/null
+++ b/lib/ss/mit-sipb-copyright.h
@@ -0,0 +1,19 @@
+/*
+
+Copyright 1987 by the Student Information Processing Board
+ of the Massachusetts Institute of Technology
+
+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,
+and 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.
+
+*/
+
diff --git a/lib/ss/mk_cmds.1 b/lib/ss/mk_cmds.1
new file mode 100644
index 0000000..216e483
--- /dev/null
+++ b/lib/ss/mk_cmds.1
@@ -0,0 +1,59 @@
+.\" Copyright (c) 2003 Theodore Ts'o
+.\"
+.TH MK_CMDS 1 "2003" E2FSPROGS
+.SH NAME
+mk_cmds \- error table compiler
+.SH SYNOPSIS
+.B mk_cmds
+file
+.SH DESCRIPTION
+.B Mk_cmds
+converts a table listing command names and associated help messages
+into a C source file suitable for use with the
+.IR ss (3)
+library.
+
+The source file name must end with a suffix of ``.ct''; the file
+consists of a declaration supplying the name of the command table:
+
+.B command_table
+.I name
+
+followed by entries of the form:
+
+[
+.B request
+|
+.B unimplemented
+]
+.I name,
+"
+.I string
+"[, abbrev]...;
+
+and a final
+
+.B end
+
+to indicate the end of the table.
+
+A C source file is generated which should be compiled and linked
+with the object files use the ss library.
+
+A ``#'' in the source file is treated as a comment character, and all
+remaining text to the end of the source line will be ignored.
+
+.SH BUGS
+
+Since the original
+.B mk_cmds
+uses a very simple parser based on
+.IR yacc (1),
+and this current version of
+.B mk_cmds
+uses an awk/sed combination of scripts,
+its error recovery leaves much to be desired.
+
+.SH "SEE ALSO"
+ss (3)
+
diff --git a/lib/ss/mk_cmds.sh.in b/lib/ss/mk_cmds.sh.in
new file mode 100644
index 0000000..53282f4
--- /dev/null
+++ b/lib/ss/mk_cmds.sh.in
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+#
+
+DIR=@datadir@/ss
+AWK=@AWK@
+SED=sed
+
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+if test "x$1" = x ; then
+ echo "Usage: mk_cmds file"
+ exit 1
+fi
+
+if test -n "$_SS_DIR_OVERRIDE" ; then
+ DIR="$_SS_DIR_OVERRIDE";
+fi
+
+if test ! -f $DIR/ct_c.sed || test ! -f $DIR/ct_c.awk ; then
+ echo "mk_cmds: Couldn't find mk_cmds's template files."
+ exit 1
+fi
+
+FILE="$1"
+ROOT=`echo $1 | sed -e s/.ct$//`
+BASE=`basename "$ROOT"`
+TMP="ct$$.c"
+
+if test ! -f "$FILE" ; then
+ echo "mk_cmds: $FILE: File not found"
+ exit 1;
+fi
+
+${SED} -f "${DIR}/ct_c.sed" "${FILE}" \
+ | ${AWK} -f "${DIR}/ct_c.awk" "rootname=${ROOT}" "outfile=${TMP}" -
+
+if grep "^#__ERROR_IN_FILE" "${TMP}" > /dev/null; then
+ rm "${TMP}"
+ exit 1
+else
+ rm -f "${BASE}.c"
+ mv -f "${TMP}" "${BASE}.c"
+ chmod a-w "${BASE}.c"
+ exit 0
+fi
diff --git a/lib/ss/pager.c b/lib/ss/pager.c
new file mode 100644
index 0000000..ba32b20
--- /dev/null
+++ b/lib/ss/pager.c
@@ -0,0 +1,152 @@
+/*
+ * Pager: Routines to create a "more" running out of a particular file
+ * descriptor.
+ *
+ * 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 "config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ss_internal.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <signal.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+
+static char MORE[] = "more";
+extern char *getenv PROTOTYPE((const char *));
+
+char *ss_safe_getenv(const char *arg)
+{
+ if ((getuid() != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#if HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+
+#if defined(HAVE_SECURE_GETENV)
+ return secure_getenv(arg);
+#elif defined(HAVE___SECURE_GETENV)
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
+
+/*
+ * this needs a *lot* of work....
+ *
+ * run in same process
+ * handle SIGINT sensibly
+ * allow finer control -- put-page-break-here
+ */
+
+#ifndef NO_FORK
+int ss_pager_create(void)
+{
+ int filedes[2];
+
+ if (pipe(filedes) != 0)
+ return(-1);
+
+ switch(fork()) {
+ case -1:
+ return(-1);
+ case 0:
+ /*
+ * Child; dup read half to 0, close all but 0, 1, and 2
+ */
+ if (dup2(filedes[0], 0) == -1)
+ exit(1);
+ ss_page_stdin();
+ default:
+ /*
+ * Parent: close "read" side of pipe, return
+ * "write" side.
+ */
+ (void) close(filedes[0]);
+ return(filedes[1]);
+ }
+}
+#else /* don't fork */
+int ss_pager_create()
+{
+ int fd;
+ fd = open("/dev/tty", O_WRONLY, 0);
+ return fd;
+}
+#endif
+
+static int write_all(int fd, char *buf, size_t count)
+{
+ ssize_t ret;
+ int c = 0;
+
+ while (count > 0) {
+ ret = write(fd, buf, count);
+ if (ret < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ return -1;
+ }
+ count -= ret;
+ buf += ret;
+ c += ret;
+ }
+ return c;
+}
+
+void ss_page_stdin(void)
+{
+ int i;
+ sigset_t mask;
+
+ for (i = 3; i < 32; i++)
+ (void) close(i);
+ (void) signal(SIGINT, SIG_DFL);
+ sigprocmask(SIG_BLOCK, 0, &mask);
+ sigdelset(&mask, SIGINT);
+ sigprocmask(SIG_SETMASK, &mask, 0);
+ if (_ss_pager_name == (char *)NULL) {
+ if ((_ss_pager_name = ss_safe_getenv("PAGER")) == (char *)NULL)
+ _ss_pager_name = MORE;
+ }
+ (void) execlp(_ss_pager_name, _ss_pager_name, (char *) NULL);
+ {
+ /* minimal recovery if pager program isn't found */
+ char buf[80];
+ register int n;
+ while ((n = read(0, buf, 80)) > 0)
+ write_all(1, buf, n);
+ }
+ exit(errno);
+}
diff --git a/lib/ss/parse.c b/lib/ss/parse.c
new file mode 100644
index 0000000..7c6c679
--- /dev/null
+++ b/lib/ss/parse.c
@@ -0,0 +1,158 @@
+/*
+ * 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 "config.h"
+#ifdef HAS_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ss_internal.h"
+
+enum parse_mode { WHITESPACE, TOKEN, QUOTED_STRING };
+
+/*
+ * parse(line_ptr, argc_ptr)
+ *
+ * Function:
+ * Parses line, dividing at whitespace, into tokens, returns
+ * the "argc" and "argv" values.
+ * Arguments:
+ * line_ptr (char *)
+ * Pointer to text string to be parsed.
+ * argc_ptr (int *)
+ * Where to put the "argc" (number of tokens) value.
+ * Returns:
+ * argv (char **)
+ * Series of pointers to parsed tokens.
+ */
+
+#define NEW_ARGV(old,n) (char **)realloc((char *)old,\
+ (unsigned)(n+2)*sizeof(char*))
+
+char **ss_parse(int sci_idx, register char *line_ptr, int *argc_ptr)
+{
+ register char **argv, **new_argv, *cp;
+ register int argc;
+ register enum parse_mode parse_mode;
+
+ argv = (char **) malloc (sizeof(char *));
+ if (argv == (char **)NULL) {
+ ss_error(sci_idx, errno, "Can't allocate storage");
+ *argc_ptr = 0;
+ return(argv);
+ }
+ *argv = (char *)NULL;
+
+ argc = 0;
+
+ parse_mode = WHITESPACE; /* flushing whitespace */
+ cp = line_ptr; /* cp is for output */
+ while (1) {
+#ifdef DEBUG
+ {
+ printf ("character `%c', mode %d\n", *line_ptr, parse_mode);
+ }
+#endif
+ while (parse_mode == WHITESPACE) {
+ if (*line_ptr == '\0')
+ goto end_of_line;
+ if (*line_ptr == ' ' || *line_ptr == '\t') {
+ line_ptr++;
+ continue;
+ }
+ if (*line_ptr == '"') {
+ /* go to quoted-string mode */
+ parse_mode = QUOTED_STRING;
+ cp = line_ptr++;
+ new_argv = NEW_ARGV (argv, argc);
+ if (new_argv == NULL) {
+ free(argv);
+ *argc_ptr = 0;
+ return NULL;
+ }
+ argv = new_argv;
+ argv[argc++] = cp;
+ argv[argc] = NULL;
+ }
+ else {
+ /* random-token mode */
+ parse_mode = TOKEN;
+ cp = line_ptr;
+ new_argv = NEW_ARGV (argv, argc);
+ if (new_argv == NULL) {
+ free(argv);
+ *argc_ptr = 0;
+ return NULL;
+ }
+ argv = new_argv;
+ argv[argc++] = line_ptr;
+ argv[argc] = NULL;
+ }
+ }
+ while (parse_mode == TOKEN) {
+ if (*line_ptr == '\0') {
+ *cp++ = '\0';
+ goto end_of_line;
+ }
+ else if (*line_ptr == ' ' || *line_ptr == '\t') {
+ *cp++ = '\0';
+ line_ptr++;
+ parse_mode = WHITESPACE;
+ }
+ else if (*line_ptr == '"') {
+ line_ptr++;
+ parse_mode = QUOTED_STRING;
+ }
+ else {
+ *cp++ = *line_ptr++;
+ }
+ }
+ while (parse_mode == QUOTED_STRING) {
+ if (*line_ptr == '\0') {
+ ss_error (sci_idx, 0,
+ "Unbalanced quotes in command line");
+ free (argv);
+ *argc_ptr = 0;
+ return NULL;
+ }
+ else if (*line_ptr == '"') {
+ if (*++line_ptr == '"') {
+ *cp++ = '"';
+ line_ptr++;
+ }
+ else {
+ parse_mode = TOKEN;
+ }
+ }
+ else {
+ *cp++ = *line_ptr++;
+ }
+ }
+ }
+end_of_line:
+ *argc_ptr = argc;
+#ifdef DEBUG
+ {
+ int i;
+ printf ("argc = %d\n", argc);
+ for (i = 0; i <= argc; i++)
+ printf ("\targv[%2d] = `%s'\n", i,
+ argv[i] ? argv[i] : "<NULL>");
+ }
+#endif
+ return(argv);
+}
diff --git a/lib/ss/prompt.c b/lib/ss/prompt.c
new file mode 100644
index 0000000..c56c2a0
--- /dev/null
+++ b/lib/ss/prompt.c
@@ -0,0 +1,31 @@
+/*
+ * prompt.c: Routines for retrieving and setting a prompt.
+ *
+ * $Header$
+ * $Locker$
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include "ss_internal.h"
+
+void ss_set_prompt(int sci_idx, char *new_prompt)
+{
+ ss_info(sci_idx)->prompt = new_prompt;
+}
+
+char *ss_get_prompt(int sci_idx)
+{
+ return(ss_info(sci_idx)->prompt);
+}
diff --git a/lib/ss/request_tbl.c b/lib/ss/request_tbl.c
new file mode 100644
index 0000000..135cb28
--- /dev/null
+++ b/lib/ss/request_tbl.c
@@ -0,0 +1,68 @@
+/*
+ * 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 "config.h"
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ss_internal.h"
+
+#define ssrt ss_request_table /* for some readable code... */
+
+void ss_add_request_table(int sci_idx, ssrt *rqtbl_ptr, int position, int *code_ptr)
+{
+ register ss_data *info;
+ register int i, size;
+ ssrt **t;
+
+ info = ss_info(sci_idx);
+ for (size=0; info->rqt_tables[size] != (ssrt *)NULL; size++)
+ ;
+ /* size == C subscript of NULL == #elements */
+ size += 2; /* new element, and NULL */
+ t = (ssrt **)realloc(info->rqt_tables, (unsigned)size*sizeof(ssrt *));
+ if (t == (ssrt **)NULL) {
+ *code_ptr = errno;
+ return;
+ }
+ info->rqt_tables = t;
+ if (position > size - 2)
+ position = size - 2;
+
+ if (size > 1)
+ for (i = size - 2; i >= position; i--)
+ info->rqt_tables[i+1] = info->rqt_tables[i];
+
+ info->rqt_tables[position] = rqtbl_ptr;
+ info->rqt_tables[size-1] = (ssrt *)NULL;
+ *code_ptr = 0;
+}
+
+void ss_delete_request_table(int sci_idx, ssrt *rqtbl_ptr, int *code_ptr)
+{
+ register ss_data *info;
+ register ssrt **rt1, **rt2;
+
+ *code_ptr = SS_ET_TABLE_NOT_FOUND;
+ info = ss_info(sci_idx);
+ rt1 = info->rqt_tables;
+ for (rt2 = rt1; *rt1; rt1++) {
+ if (*rt1 != rqtbl_ptr) {
+ *rt2++ = *rt1;
+ *code_ptr = 0;
+ }
+ }
+ *rt2 = (ssrt *)NULL;
+ return;
+}
diff --git a/lib/ss/requests.c b/lib/ss/requests.c
new file mode 100644
index 0000000..33ce5d2
--- /dev/null
+++ b/lib/ss/requests.c
@@ -0,0 +1,66 @@
+/*
+ * Various minor routines...
+ *
+ * Copyright 1987, 1988, 1989 by MIT
+ *
+ * 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 "config.h"
+#include <stdio.h>
+#include "ss_internal.h"
+
+#define DECLARE(name) void name(int argc,const char * const *argv, \
+ int sci_idx, void *infop)
+
+/*
+ * ss_self_identify -- assigned by default to the "." request
+ */
+void ss_self_identify(int argc __SS_ATTR((unused)),
+ const char * const *argv __SS_ATTR((unused)),
+ int sci_idx, void *infop __SS_ATTR((unused)))
+{
+ register ss_data *info = ss_info(sci_idx);
+ printf("%s version %s\n", info->subsystem_name,
+ info->subsystem_version);
+}
+
+/*
+ * ss_subsystem_name -- print name of subsystem
+ */
+void ss_subsystem_name(int argc __SS_ATTR((unused)),
+ const char * const *argv __SS_ATTR((unused)),
+ int sci_idx,
+ void *infop __SS_ATTR((unused)))
+{
+ printf("%s\n", ss_info(sci_idx)->subsystem_name);
+}
+
+/*
+ * ss_subsystem_version -- print version of subsystem
+ */
+void ss_subsystem_version(int argc __SS_ATTR((unused)),
+ const char * const *argv __SS_ATTR((unused)),
+ int sci_idx,
+ void *infop __SS_ATTR((unused)))
+{
+ printf("%s\n", ss_info(sci_idx)->subsystem_version);
+}
+
+/*
+ * ss_unimplemented -- routine not implemented (should be
+ * set up as (dont_list,dont_summarize))
+ */
+void ss_unimplemented(int argc __SS_ATTR((unused)),
+ const char * const *argv __SS_ATTR((unused)),
+ int sci_idx, void *infop __SS_ATTR((unused)))
+{
+ ss_perror(sci_idx, SS_ET_UNIMPLEMENTED, "");
+}
diff --git a/lib/ss/ss.h b/lib/ss/ss.h
new file mode 100644
index 0000000..7333ffc
--- /dev/null
+++ b/lib/ss/ss.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ *
+ * This quote is just too good to not pass on:
+ *
+ * "BTW, I would have rejected the name Story Server because its
+ * initials are SS, the name of the secret police in Nazi
+ * Germany, probably the most despised pair of letters in western
+ * culture." --- http://scriptingnewsarchive.userland.com/1999/12/13
+ *
+ * Let no one say political correctness isn't dead....
+ */
+
+#ifndef _ss_h
+#define _ss_h __FILE__
+
+#include <ss/ss_err.h>
+
+#define __SS_CONST const
+#define __SS_PROTO (int, const char * const *, int, void *)
+
+#ifdef __GNUC__
+#define __SS_ATTR(x) __attribute__(x)
+#else
+#define __SS_ATTR(x)
+#endif
+
+
+typedef __SS_CONST struct _ss_request_entry {
+ __SS_CONST char * __SS_CONST *command_names; /* whatever */
+ void (* __SS_CONST function) __SS_PROTO; /* foo */
+ __SS_CONST char * __SS_CONST info_string; /* NULL */
+ int flags; /* 0 */
+} ss_request_entry;
+
+typedef __SS_CONST struct _ss_request_table {
+ int version;
+ ss_request_entry *requests;
+} ss_request_table;
+
+#define SS_RQT_TBL_V2 2
+
+typedef struct _ss_rp_options { /* DEFAULT VALUES */
+ int version; /* SS_RP_V1 */
+ void (*unknown) __SS_PROTO; /* call for unknown command */
+ int allow_suspend;
+ int catch_int;
+} ss_rp_options;
+
+#define SS_RP_V1 1
+
+#define SS_OPT_DONT_LIST 0x0001
+#define SS_OPT_DONT_SUMMARIZE 0x0002
+
+void ss_help __SS_PROTO;
+#if 0
+char *ss_current_request(); /* This is actually a macro */
+#endif
+
+char *ss_name(int sci_idx);
+void ss_error (int, long, char const *, ...)
+ __SS_ATTR((format(printf, 3, 4)));
+void ss_perror (int, long, char const *);
+
+int ss_create_invocation(const char *, const char *, void *,
+ ss_request_table *, int *);
+void ss_delete_invocation(int);
+int ss_listen(int);
+int ss_execute_line(int, char *);
+void ss_add_request_table(int, ss_request_table *, int, int *);
+void ss_delete_request_table(int, ss_request_table *, int *);
+void ss_abort_subsystem(int sci_idx, int code);
+void ss_quit(int argc, const char * const *argv, int sci_idx, void *infop);
+void ss_self_identify(int argc, const char * const *argv, int sci_idx, void *infop);
+void ss_subsystem_name(int argc, const char * const *argv,
+ int sci_idx, void *infop);
+void ss_subsystem_version(int argc, const char * const *argv,
+ int sci_idx, void *infop);
+void ss_unimplemented(int argc, const char * const *argv,
+ int sci_idx, void *infop);
+void ss_set_prompt(int sci_idx, char *new_prompt);
+char *ss_get_prompt(int sci_idx);
+void ss_get_readline(int sci_idx);
+char *ss_safe_getenv(const char *arg);
+
+extern ss_request_table ss_std_requests;
+#endif /* _ss_h */
diff --git a/lib/ss/ss.pc.in b/lib/ss/ss.pc.in
new file mode 100644
index 0000000..5c9eccb
--- /dev/null
+++ b/lib/ss/ss.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: ss
+Description: Subsystem command parsing library
+Version: @E2FSPROGS_VERSION@
+Requires.private: com_err
+Cflags: -I${includedir}/ss -I${includedir}
+Libs: -L${libdir} -lss
+Libs.private: @DLOPEN_LIB@
diff --git a/lib/ss/ss_err.et b/lib/ss/ss_err.et
new file mode 100644
index 0000000..80e9dfa
--- /dev/null
+++ b/lib/ss/ss_err.et
@@ -0,0 +1,39 @@
+ error_table ss
+
+ec SS_ET_SUBSYSTEM_ABORTED,
+ "Subsystem aborted"
+
+ec SS_ET_VERSION_MISMATCH,
+ "Version mismatch"
+
+ec SS_ET_NULL_INV,
+ "No current invocation"
+
+ec SS_ET_NO_INFO_DIR,
+ "No info directory"
+
+ec SS_ET_COMMAND_NOT_FOUND,
+ "Command not found"
+
+ec SS_ET_LINE_ABORTED,
+ "Command line aborted"
+
+ec SS_ET_EOF,
+ "End-of-file reached"
+
+ec SS_ET_PERMISSION_DENIED,
+ "Permission denied"
+
+ec SS_ET_TABLE_NOT_FOUND,
+ "Request table not found"
+
+ec SS_ET_NO_HELP_FILE,
+ "No info available"
+
+ec SS_ET_ESCAPE_DISABLED,
+ "Shell escapes are disabled"
+
+ec SS_ET_UNIMPLEMENTED,
+ "Sorry, this request is not yet implemented"
+
+ end
diff --git a/lib/ss/ss_internal.h b/lib/ss/ss_internal.h
new file mode 100644
index 0000000..7b1d23b
--- /dev/null
+++ b/lib/ss/ss_internal.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef _ss_ss_internal_h
+#define _ss_ss_internal_h __FILE__
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define PROTOTYPE(p) p
+typedef void * pointer;
+
+#include "ss.h"
+
+typedef char BOOL;
+
+typedef struct _ss_abbrev_entry {
+ char *name; /* abbrev name */
+ char **abbrev; /* new tokens to insert */
+ unsigned int beginning_of_line : 1;
+} ss_abbrev_entry;
+
+typedef struct _ss_abbrev_list {
+ int n_abbrevs;
+ ss_abbrev_entry *first_abbrev;
+} ss_abbrev_list;
+
+typedef struct {
+/* char *path; */
+ ss_abbrev_list abbrevs[127];
+} ss_abbrev_info;
+
+typedef struct _ss_data { /* init values */
+ /* this subsystem */
+ const char *subsystem_name;
+ const char *subsystem_version;
+ /* current request info */
+ int argc;
+ char **argv; /* arg list */
+ char const *current_request; /* primary name */
+ /* info directory for 'help' */
+ char **info_dirs;
+ /* to be extracted by subroutines */
+ pointer info_ptr; /* (void *) NULL */
+ /* for ss_listen processing */
+ char *prompt;
+ ss_request_table **rqt_tables;
+ ss_abbrev_info *abbrev_info;
+ struct {
+ unsigned int escape_disabled : 1,
+ abbrevs_disabled : 1;
+ } flags;
+ /*
+ * Dynamic usage of readline library if present
+ */
+ void *readline_handle;
+ void (*readline_shutdown)(struct _ss_data *info);
+ char *(*readline)(const char *);
+ void (*add_history)(const char *);
+ void (*redisplay)(void);
+ char **(*rl_completion_matches)(const char *,
+ char *(*completer)(const char *, int));
+ /* to get out */
+ int abort; /* exit subsystem */
+ int exit_status;
+} ss_data;
+
+#define CURRENT_SS_VERSION 1
+
+#define ss_info(sci_idx) (_ss_table[sci_idx])
+#define ss_current_request(sci_idx,code_ptr) \
+ (*code_ptr=0,ss_info(sci_idx)->current_request)
+void ss_add_info_dir (int sci_idx, char *info_dir, int *code_ptr);
+void ss_delete_info_dir (int sci_idx, char *info_dir, int *code_ptr);
+int ss_execute_line(int sci_idx, char *line_ptr);
+char **ss_parse(int sci_idx, char *line_ptr, int *argc_ptr);
+ss_abbrev_info *ss_abbrev_initialize(char *, int *);
+void ss_page_stdin(void) __SS_ATTR((noreturn));
+void ss_list_requests(int, char const * const *, int, pointer);
+int ss_execute_command(int sci_idx, char *argv[]);
+int ss_pager_create(void);
+char *ss_safe_getenv(const char *arg);
+char **ss_rl_completion(const char *text, int start, int end);
+
+extern ss_data **_ss_table;
+extern char *ss_et_msgs[];
+extern char *_ss_pager_name;
+
+#ifdef USE_SIGPROCMASK
+/* fake sigmask, sigblock, sigsetmask */
+#include <signal.h>
+#define sigmask(x) (1L<<(x)-1)
+#define sigsetmask(x) sigprocmask(SIG_SETMASK,&x,NULL)
+static int _fake_sigstore;
+#define sigblock(x) (_fake_sigstore=x,sigprocmask(SIG_BLOCK,&_fake_sigstore,0))
+#endif
+#endif /* _ss_internal_h */
diff --git a/lib/ss/std_rqs.ct b/lib/ss/std_rqs.ct
new file mode 100644
index 0000000..500288a
--- /dev/null
+++ b/lib/ss/std_rqs.ct
@@ -0,0 +1,46 @@
+ command_table ss_std_requests;
+
+ request ss_self_identify, "Identify the subsystem.",
+ ".",
+ (dont_list, dont_summarize);
+
+ request ss_help, "Display info on command or topic.",
+ help;
+
+ unimplemented
+ ss_list_help,
+ "List topics for which help is available.",
+ list_help, lh;
+
+ request ss_list_requests, "List available commands.",
+ list_requests, lr, "?";
+
+ request ss_quit, "Leave the subsystem.",
+ quit, q;
+
+ unimplemented
+ ss_abbrev,
+ "Enable/disable abbreviation processing of request lines.",
+ abbrev, ab;
+
+ unimplemented
+ ss_execute,
+ "Execute a UNIX command line.",
+ execute, e;
+
+ unimplemented
+ ss_summarize_requests,
+ "Produce a list of the most commonly used requests.",
+ "?";
+
+ request ss_subsystem_name,
+ "Return the name of this subsystem.",
+ subsystem_name,
+ (dont_list);
+
+ request ss_subsystem_version,
+ "Return the version of this subsystem.",
+ subsystem_version,
+ (dont_list);
+
+ end;
diff --git a/lib/ss/test_cmd.ct b/lib/ss/test_cmd.ct
new file mode 100644
index 0000000..fb49f78
--- /dev/null
+++ b/lib/ss/test_cmd.ct
@@ -0,0 +1,6 @@
+command_table test_cmds;
+
+request test_cmd, "Test command",
+ test_cmd, test;
+
+end;
diff --git a/lib/ss/test_script b/lib/ss/test_script
new file mode 100644
index 0000000..8af7fdc
--- /dev/null
+++ b/lib/ss/test_script
@@ -0,0 +1,8 @@
+test
+test foo bar quux
+test bar quux
+quux bar
+quux
+test quux
+quit
+test foo bar
diff --git a/lib/ss/test_script_expected b/lib/ss/test_script_expected
new file mode 100644
index 0000000..543f828
--- /dev/null
+++ b/lib/ss/test_script_expected
@@ -0,0 +1,22 @@
+test_ss 1.0. Type '?' for a list of commands.
+
+test_icount: test
+Hello, world!
+Args:
+test_icount: test foo bar quux
+Hello, world!
+Args: 'foo', 'bar', 'quux'
+test_icount: test bar quux
+Hello, world!
+Args: 'bar', 'quux'
+test_icount: quux bar
+test_ss: Command not found quux
+test_icount: quux
+test_ss: Command not found quux
+test_icount: test quux
+Hello, world!
+Args: 'quux'
+test_icount: quit
+test_icount: test foo bar
+Hello, world!
+Args: 'foo', 'bar'
diff --git a/lib/ss/test_ss.c b/lib/ss/test_ss.c
new file mode 100644
index 0000000..53ca99f
--- /dev/null
+++ b/lib/ss/test_ss.c
@@ -0,0 +1,151 @@
+/*
+ * test_ss.c
+ *
+ * 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 "config.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <string.h>
+#include "ss.h"
+
+extern ss_request_table test_cmds;
+
+#define TRUE 1
+#define FALSE 0
+
+static char subsystem_name[] = "test_ss";
+static char version[] = "1.0";
+
+static int source_file(const char *cmd_file, int sci_idx)
+{
+ FILE *f;
+ char buf[256];
+ char *cp;
+ int exit_status = 0;
+ int retval;
+ int noecho;
+
+ if (strcmp(cmd_file, "-") == 0)
+ f = stdin;
+ else {
+ f = fopen(cmd_file, "r");
+ if (!f) {
+ perror(cmd_file);
+ exit(1);
+ }
+ }
+ fflush(stdout);
+ fflush(stderr);
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+ while (!feof(f)) {
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ break;
+ if (buf[0] == '#')
+ continue;
+ noecho = 0;
+ if (buf[0] == '-') {
+ noecho = 1;
+ buf[0] = ' ';
+ }
+ cp = strchr(buf, '\n');
+ if (cp)
+ *cp = 0;
+ cp = strchr(buf, '\r');
+ if (cp)
+ *cp = 0;
+ if (!noecho)
+ printf("test_icount: %s\n", buf);
+ retval = ss_execute_line(sci_idx, buf);
+ if (retval) {
+ ss_perror(sci_idx, retval, buf);
+ exit_status++;
+ }
+ }
+ return exit_status;
+}
+
+int main(int argc, char **argv)
+{
+ int c, code;
+ char *request = (char *)NULL;
+ char *cmd_file = 0;
+ int sci_idx;
+ int exit_status = 0;
+
+ while ((c = getopt (argc, argv, "wR:f:")) != EOF) {
+ switch (c) {
+ case 'R':
+ request = optarg;
+ break;
+ case 'f':
+ cmd_file = optarg;
+ break;
+ default:
+ com_err(argv[0], 0, "Usage: test_ss [-R request] "
+ "[-f cmd_file]");
+ exit(1);
+ }
+ }
+
+ sci_idx = ss_create_invocation(subsystem_name, version,
+ (char *)NULL, &test_cmds, &code);
+ if (code) {
+ ss_perror(sci_idx, code, "creating invocation");
+ exit(1);
+ }
+
+ (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code);
+ if (code) {
+ ss_perror (sci_idx, code, "adding standard requests");
+ exit (1);
+ }
+
+ printf("test_ss %s. Type '?' for a list of commands.\n\n",
+ version);
+
+ if (request) {
+ code = ss_execute_line(sci_idx, request);
+ if (code) {
+ ss_perror(sci_idx, code, request);
+ exit_status++;
+ }
+ } else if (cmd_file) {
+ exit_status = source_file(cmd_file, sci_idx);
+ } else {
+ ss_listen(sci_idx);
+ }
+
+ exit(exit_status);
+}
+
+
+void test_cmd (argc, argv)
+ int argc;
+ char **argv;
+{
+ printf("Hello, world!\n");
+ printf("Args: ");
+ while (++argv, --argc) {
+ printf("'%s'", *argv);
+ if (argc > 1)
+ fputs(", ", stdout);
+ }
+ putchar ('\n');
+}
diff --git a/lib/support/Android.bp b/lib/support/Android.bp
new file mode 100644
index 0000000..af9b28d
--- /dev/null
+++ b/lib/support/Android.bp
@@ -0,0 +1,78 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "external_e2fsprogs_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-GPL
+ // SPDX-license-identifier-MIT
+ default_applicable_licenses: ["external_e2fsprogs_license"],
+}
+
+cc_library {
+ name: "libext2_quota",
+ host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "devname.c",
+ "dict.c",
+ "mkquota.c",
+ "parse_qtype.c",
+ "plausible.c",
+ "profile.c",
+ "profile_helpers.c",
+ "prof_err.c",
+ "quotaio.c",
+ "quotaio_tree.c",
+ "quotaio_v2.c",
+ ],
+ shared_libs: [
+ "libext2fs",
+ "libext2_blkid",
+ "libext2_com_err",
+ ],
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
+
+cc_library_shared {
+ name: "libext2_profile",
+ host_supported: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+
+ srcs: [
+ "prof_err.c",
+ "profile.c",
+ ],
+ shared_libs: ["libext2_com_err"],
+
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+}
+
+cc_library {
+ name: "libext2_support",
+ host_supported: true,
+ defaults: ["e2fsprogs-defaults"],
+
+ srcs: [
+ "cstring.c",
+ ],
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+}
diff --git a/lib/support/Makefile.in b/lib/support/Makefile.in
new file mode 100644
index 0000000..b622909
--- /dev/null
+++ b/lib/support/Makefile.in
@@ -0,0 +1,184 @@
+# Makefile for e2fsprog's internal support
+#
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/support
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+
+@MCONFIG@
+
+all::
+
+OBJS= cstring.o \
+ mkquota.o \
+ plausible.o \
+ profile.o \
+ parse_qtype.o \
+ print_fs_flags.o \
+ profile_helpers.o \
+ prof_err.o \
+ quotaio.o \
+ quotaio_v2.o \
+ quotaio_tree.o \
+ dict.o \
+ devname.o
+
+SRCS= $(srcdir)/argv_parse.c \
+ $(srcdir)/cstring.c \
+ $(srcdir)/mkquota.c \
+ $(srcdir)/parse_qtype.c \
+ $(srcdir)/plausible.c \
+ $(srcdir)/print_fs_flags.c \
+ $(srcdir)/profile.c \
+ $(srcdir)/profile_helpers.c \
+ prof_err.c \
+ $(srcdir)/quotaio.c \
+ $(srcdir)/quotaio_tree.c \
+ $(srcdir)/quotaio_v2.c \
+ $(srcdir)/dict.c \
+ $(srcdir)/devname.c
+
+LIBRARY= libsupport
+LIBDIR= support
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_PROFILE@
+
+COMPILE_ET= _ET_DIR_OVERRIDE=$(srcdir)/../et ../et/compile_et
+
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
+
+installdirs::
+
+install:: all
+
+uninstall::
+
+prof_err.c prof_err.h: prof_err.et
+ $(E) " COMPILE_ET prof_err.et"
+ $(Q) $(COMPILE_ET) $(srcdir)/prof_err.et
+
+test_profile: $(srcdir)/profile.c profile_helpers.o argv_parse.o \
+ prof_err.o profile.h $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o test_profile -DDEBUG_PROGRAM $(srcdir)/profile.c prof_err.o \
+ profile_helpers.o argv_parse.o $(STATIC_LIBCOM_ERR) \
+ $(ALL_CFLAGS)
+
+test_cstring: $(srcdir)/cstring.c
+ $(E) " CC $@"
+ $(Q) $(CC) -o test_cstring -DDEBUG_PROGRAM $(srcdir)/cstring.c \
+ $(ALL_CFLAGS)
+
+clean::
+ $(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/* \
+ ../libsupport.a ../libsupport_p.a $(SMANPAGES) \
+ prof_err.c prof_err.h test_profile test_cstring
+
+#fullcheck check:: tst_uuid
+# LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_uuid
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend Makefile \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+#
+# Hack to parallel makes recognize dependencies correctly.
+#
+../../lib/libsupport.a: libsupport.a
+../../lib/libsupport.so: image
+../../lib/libsupport.dylib: image
+
+$(OBJS):
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+argv_parse.o: $(srcdir)/argv_parse.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/argv_parse.h
+cstring.o: $(srcdir)/cstring.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/cstring.h
+mkquota.o: $(srcdir)/mkquota.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(top_srcdir)/lib/e2p/e2p.h \
+ $(srcdir)/quotaio.h $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h \
+ $(srcdir)/quotaio_v2.h $(srcdir)/common.h $(srcdir)/dict.h
+parse_qtype.o: $(srcdir)/parse_qtype.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/quotaio.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/dqblk_v2.h \
+ $(srcdir)/quotaio_tree.h
+plausible.o: $(srcdir)/plausible.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/plausible.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/nls-enable.h
+print_fs_flags.o: $(srcdir)/print_fs_flags.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h
+profile.o: $(srcdir)/profile.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/profile.h prof_err.h
+profile_helpers.o: $(srcdir)/profile_helpers.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/profile.h $(srcdir)/profile_helpers.h prof_err.h
+prof_err.o: prof_err.c
+quotaio.o: $(srcdir)/quotaio.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/quotaio.h \
+ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
+ $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/dqblk_v2.h \
+ $(srcdir)/quotaio_tree.h
+quotaio_tree.o: $(srcdir)/quotaio_tree.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/quotaio_tree.h \
+ $(srcdir)/quotaio.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/dqblk_v2.h
+quotaio_v2.o: $(srcdir)/quotaio_v2.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/common.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/quotaio_v2.h \
+ $(srcdir)/quotaio.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
+ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext3_extents.h \
+ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
+ $(top_srcdir)/lib/ext2fs/bitops.h $(srcdir)/dqblk_v2.h \
+ $(srcdir)/quotaio_tree.h
+dict.o: $(srcdir)/dict.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/dict.h
+devname.o: $(srcdir)/devname.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/devname.h $(srcdir)/nls-enable.h
diff --git a/lib/support/argv_parse.c b/lib/support/argv_parse.c
new file mode 100644
index 0000000..1f50f9e
--- /dev/null
+++ b/lib/support/argv_parse.c
@@ -0,0 +1,168 @@
+/*
+ * argv_parse.c --- utility function for parsing a string into a
+ * argc, argv array.
+ *
+ * This file defines a function argv_parse() which parsing a
+ * passed-in string, handling double quotes and backslashes, and
+ * creates an allocated argv vector which can be freed using the
+ * argv_free() function.
+ *
+ * See argv_parse.h for the formal definition of the functions.
+ *
+ * Copyright 1999 by Theodore Ts'o.
+ *
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose with or without fee is hereby granted, provided that
+ * the above copyright notice and this permission notice appear in all
+ * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE
+ * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't
+ * it sick that the U.S. culture of lawsuit-happy lawyers requires
+ * this kind of disclaimer?)
+ *
+ * Version 1.1, modified 2/27/1999
+ */
+
+#include "config.h"
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+#include "argv_parse.h"
+
+#define STATE_WHITESPACE 1
+#define STATE_TOKEN 2
+#define STATE_QUOTED 3
+
+/*
+ * Returns 0 on success, -1 on failure.
+ */
+int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv)
+{
+ int argc = 0, max_argc = 0;
+ char **argv, **new_argv, *buf, ch;
+ char *cp = 0, *outcp = 0;
+ int state = STATE_WHITESPACE;
+
+ buf = malloc(strlen(in_buf)+1);
+ if (!buf)
+ return -1;
+
+ max_argc = 0; argc = 0; argv = 0;
+ outcp = buf;
+ for (cp = in_buf; (ch = *cp); cp++) {
+ if (state == STATE_WHITESPACE) {
+ if (isspace((int) ch))
+ continue;
+ /* Not whitespace, so start a new token */
+ state = STATE_TOKEN;
+ if (argc >= max_argc) {
+ max_argc += 3;
+ new_argv = realloc(argv,
+ (max_argc+1)*sizeof(char *));
+ if (!new_argv) {
+ free(argv);
+ free(buf);
+ return -1;
+ }
+ argv = new_argv;
+ }
+ argv[argc++] = outcp;
+ }
+ if (state == STATE_QUOTED) {
+ if (ch == '"')
+ state = STATE_TOKEN;
+ else
+ *outcp++ = ch;
+ continue;
+ }
+ /* Must be processing characters in a word */
+ if (isspace((int) ch)) {
+ /*
+ * Terminate the current word and start
+ * looking for the beginning of the next word.
+ */
+ *outcp++ = 0;
+ state = STATE_WHITESPACE;
+ continue;
+ }
+ if (ch == '"') {
+ state = STATE_QUOTED;
+ continue;
+ }
+ if (ch == '\\') {
+ ch = *++cp;
+ switch (ch) {
+ case '\0':
+ ch = '\\'; cp--; break;
+ case 'n':
+ ch = '\n'; break;
+ case 't':
+ ch = '\t'; break;
+ case 'b':
+ ch = '\b'; break;
+ }
+ }
+ *outcp++ = ch;
+ }
+ if (state != STATE_WHITESPACE)
+ *outcp++ = '\0';
+ if (argv == 0) {
+ argv = malloc(sizeof(char *));
+ free(buf);
+ if (!argv)
+ return -1;
+ }
+ argv[argc] = 0;
+ if (ret_argc)
+ *ret_argc = argc;
+ if (ret_argv)
+ *ret_argv = argv;
+ return 0;
+}
+
+void argv_free(char **argv)
+{
+ free(*argv);
+ free(argv);
+}
+
+#ifdef DEBUG
+/*
+ * For debugging
+ */
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ int ac, ret;
+ char **av, **cpp;
+ char buf[256];
+
+ while (!feof(stdin)) {
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ break;
+ ret = argv_parse(buf, &ac, &av);
+ if (ret != 0) {
+ printf("Argv_parse returned %d!\n", ret);
+ continue;
+ }
+ printf("Argv_parse returned %d arguments...\n", ac);
+ for (cpp = av; *cpp; cpp++) {
+ if (cpp != av)
+ printf(", ");
+ printf("'%s'", *cpp);
+ }
+ printf("\n");
+ argv_free(av);
+ }
+ exit(0);
+}
+#endif /* DEBUG */
diff --git a/lib/support/argv_parse.h b/lib/support/argv_parse.h
new file mode 100644
index 0000000..86f4564
--- /dev/null
+++ b/lib/support/argv_parse.h
@@ -0,0 +1,43 @@
+/*
+ * argv_parse.h --- header file for the argv parser.
+ *
+ * This file defines the interface for the functions argv_parse() and
+ * argv_free().
+ *
+ ***********************************************************************
+ * int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv)
+ *
+ * This function takes as its first argument a string which it will
+ * parse into an argv argument vector, with each white-space separated
+ * word placed into its own slot in the argv. This function handles
+ * double quotes and backslashes so that the parsed words can contain
+ * special characters. The count of the number words found in the
+ * parsed string, as well as the argument vector, are returned into
+ * ret_argc and ret_argv, respectively.
+ ***********************************************************************
+ * extern void argv_free(char **argv);
+ *
+ * This function frees the argument vector created by argv_parse().
+ ***********************************************************************
+ *
+ * Copyright 1999 by Theodore Ts'o.
+ *
+ * Permission to use, copy, modify, and distribute this software for
+ * any purpose with or without fee is hereby granted, provided that
+ * the above copyright notice and this permission notice appear in all
+ * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE
+ * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't
+ * it sick that the U.S. culture of lawsuit-happy lawyers requires
+ * this kind of disclaimer?)
+ *
+ * Version 1.1, modified 2/27/1999
+ */
+
+extern int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv);
+extern void argv_free(char **argv);
diff --git a/lib/support/common.h b/lib/support/common.h
new file mode 100644
index 0000000..00a9375
--- /dev/null
+++ b/lib/support/common.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Various things common for all utilities
+ *
+ */
+
+#ifndef __QUOTA_COMMON_H__
+#define __QUOTA_COMMON_H__
+
+#if EXT2_FLAT_INCLUDES
+#include "e2_types.h"
+#else
+#include <ext2fs/ext2_types.h>
+#endif /* EXT2_FLAT_INCLUDES */
+
+/* #define DEBUG_QUOTA 1 */
+
+#ifndef __attribute__
+# if !defined __GNUC__ || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+# endif
+#endif
+
+#define log_err(format, arg ...) \
+ fprintf(stderr, "[ERROR] %s:%d:%s: " format "\n", \
+ __FILE__, __LINE__, __func__, ## arg)
+
+#ifdef DEBUG_QUOTA
+# define log_debug(format, arg ...) \
+ fprintf(stderr, "[DEBUG] %s:%d:%s: " format "\n", \
+ __FILE__, __LINE__, __func__, ## arg)
+#else
+# define log_debug(...)
+#endif
+
+#endif /* __QUOTA_COMMON_H__ */
diff --git a/lib/support/cstring.c b/lib/support/cstring.c
new file mode 100644
index 0000000..57f4522
--- /dev/null
+++ b/lib/support/cstring.c
@@ -0,0 +1,162 @@
+/*
+ * cstring.c -- parse and print strings using the C escape sequences
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <string.h>
+
+#include "cstring.h"
+
+int parse_c_string(char *str)
+{
+ char *to, *from, ch;
+ int v;
+
+ to = from = str;
+
+ for (to = from = (char *) str;
+ *from && *from != '"'; to++, from++) {
+ if (*from == '\\') {
+ ch = *(++from);
+ switch (ch) {
+ case 'a':
+ *to = '\a';
+ break;
+ case 'b':
+ *to = '\b';
+ break;
+ case 'f':
+ *to = '\f';
+ break;
+ case 'n':
+ *to = '\n';
+ break;
+ case 't':
+ *to = '\t';
+ break;
+ case 'v':
+ *to = '\v';
+ break;
+ case 'x':
+ ch = *(from + 1);
+ if (ch >= 'a' && ch <= 'f')
+ ch = ch - 'a' + 'A';
+ if (ch >= '0' && ch <= '9')
+ v = ch - '0';
+ else if (ch >= 'A' && ch <= 'F')
+ v = ch + 10 - 'A';
+ else {
+ *to = 'x';
+ break;
+ }
+ from++;
+ ch = *(from + 1);
+ if (ch >= 'a' && ch <= 'f')
+ ch = ch - 'a' + 'A';
+ if (ch >= '0' && ch <= '9')
+ v = (v * 16) + (ch - '0');
+ else if (ch >= 'A' && ch <= 'F')
+ v = (v * 16) + (ch + 10 - 'A');
+ else {
+ *to = 'x';
+ from--;
+ break;
+ }
+ from++;
+ *to = v;
+ break;
+ default:
+ if (ch >= '0' && ch <= '9') {
+ v = ch - '0';
+ ch = *(from + 1);
+ if (ch >= '0' && ch <= '9') {
+ from++;
+ v = (8 * v) + (ch - '0');
+ ch = *(from + 1);
+ if (ch >= '0' && ch <= '9') {
+ from++;
+ v = (8 * v) + (ch - '0');
+ }
+ }
+ ch = v;
+ }
+ *to = ch;
+ }
+ continue;
+ }
+ *to = *from;
+ }
+ *to = '\0';
+ return to - (char *) str;
+}
+
+void print_c_string(FILE *f, const char *cp, int len)
+{
+ unsigned char ch;
+
+ if (len < 0)
+ len = strlen(cp);
+
+ while (len--) {
+ ch = *cp++;
+ if (ch == '\a')
+ fputs("\\a", f);
+ else if (ch == '\b')
+ fputs("\\b", f);
+ else if (ch == '\f')
+ fputs("\\f", f);
+ else if (ch == '\n')
+ fputs("\\n", f);
+ else if (ch == '\t')
+ fputs("\\t", f);
+ else if (ch == '\v')
+ fputs("\\v", f);
+ else if (ch == '\\')
+ fputs("\\\\", f);
+ else if (ch == '\'')
+ fputs("\\\'", f);
+ else if (ch == '\"')
+ fputs("\\\"", f);
+ else if ((ch < 32) || (ch > 126))
+ fprintf(f, "\\%03o", ch);
+ else
+ fputc(ch, f);
+ }
+}
+
+#ifdef DEBUG_PROGRAM
+int main(int argc, char **argv)
+{
+ char buf[4096];
+ int c, raw = 0;
+
+ while ((c = getopt(argc, argv, "r")) != EOF) {
+ switch (c) {
+ case 'r':
+ raw++;
+ break;
+ default:
+ fprintf(stderr, "Usage: %s [-r]\n", argv[0]);
+ exit(1);
+ }
+ }
+
+ while (!feof(stdin)) {
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ break;
+ c = parse_c_string(buf);
+ if (raw)
+ fputs(buf, stdout);
+ else {
+ print_c_string(stdout, buf, c);
+ printf(" <%d>\n", c);
+ }
+ }
+}
+#endif
diff --git a/lib/support/cstring.h b/lib/support/cstring.h
new file mode 100644
index 0000000..7f80f41
--- /dev/null
+++ b/lib/support/cstring.h
@@ -0,0 +1,6 @@
+/*
+ * cstring.h -- header file for C string parse/print utilities
+ */
+
+extern int parse_c_string(char *str);
+extern void print_c_string(FILE *f, const char *cp, int len);
diff --git a/lib/support/devname.c b/lib/support/devname.c
new file mode 100644
index 0000000..e0306dd
--- /dev/null
+++ b/lib/support/devname.c
@@ -0,0 +1,65 @@
+/*
+ * devname.c --- Support function to translate a user provided string
+ * identifying a device to an actual device path
+ *
+ * Copyright (C) 2022 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "config.h"
+#include "devname.h"
+#include "nls-enable.h"
+
+/*
+ * blkid_get_devname() is primarily intended for parsing "NAME=value"
+ * tokens. It will return the device matching the specified token, NULL if
+ * nothing is found, or copy of the string if it's not in "NAME=value"
+ * format.
+ * get_devname() takes the same parameters and works the same way as
+ * blkid_get_devname() except it can handle '=' in the file name.
+ */
+char *get_devname(blkid_cache cache, const char *token, const char *value)
+{
+ int is_file = 0;
+ char *ret = NULL;
+
+ if (!token)
+ goto out;
+
+ if (value) {
+ ret = blkid_get_devname(cache, token, value);
+ goto out;
+ }
+
+ if (access(token, F_OK) == 0)
+ is_file = 1;
+
+ ret = blkid_get_devname(cache, token, NULL);
+ if (ret) {
+ /*
+ * In case of collision prefer the result from
+ * blkid_get_devname() to avoid a file masking file system with
+ * existing tag.
+ */
+ if (is_file && (strcmp(ret, token) != 0)) {
+ fprintf(stderr,
+ _("Collision found: '%s' refers to both '%s' "
+ "and a file '%s'. Using '%s'!\n"),
+ token, ret, token, ret);
+ }
+ goto out;
+ }
+
+ if (is_file)
+ ret = strdup(token);
+out:
+ return ret;
+}
diff --git a/lib/support/devname.h b/lib/support/devname.h
new file mode 100644
index 0000000..cc19561
--- /dev/null
+++ b/lib/support/devname.h
@@ -0,0 +1,19 @@
+/*
+ * devname.c --- Figure out if a pathname is ext* or something else.
+ *
+ * Copyright (C) 2022 Red Hat, Inc., Lukas Czerner <lczerner@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef DEVNAME_H_
+#define DEVNAME_H_
+
+#include "blkid/blkid.h"
+
+char *get_devname(blkid_cache cache, const char *token, const char *value);
+
+#endif /* DEVNAME_H_ */
diff --git a/lib/support/dict.c b/lib/support/dict.c
new file mode 100644
index 0000000..93fdd0b
--- /dev/null
+++ b/lib/support/dict.c
@@ -0,0 +1,1542 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $
+ * $Name: kazlib_1_20 $
+ * The work has been modified.
+ */
+
+#define DICT_NODEBUG
+
+#ifdef __GNUC__
+#define EXT2FS_ATTR(x) __attribute__(x)
+#else
+#define EXT2FS_ATTR(x)
+#endif
+
+#include "config.h"
+#include <stdlib.h>
+#include <stddef.h>
+#ifdef DICT_NODEBUG
+#define dict_assert(x)
+#else
+#include <assert.h>
+#define dict_assert(x) assert(x)
+#endif
+#define DICT_IMPLEMENTATION
+#include "dict.h"
+
+#ifdef KAZLIB_RCSID
+static const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $";
+#endif
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define nodecount dict_nodecount
+#define maxcount dict_maxcount
+#define compare dict_compare
+#define allocnode dict_allocnode
+#define freenode dict_freenode
+#define context dict_context
+#define dupes dict_dupes
+
+#define dictptr dict_dictptr
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+#define DICT_DEPTH_MAX 64
+
+static dnode_t *dnode_alloc(void *context);
+static void dnode_free(dnode_t *node, void *context);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree. The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C. The left subtree of C is inherited as the new right subtree
+ * for P. The ordering of the keys within the tree is thus preserved.
+ */
+
+static void rotate_left(dnode_t *upper)
+{
+ dnode_t *lower, *lowleft, *upparent;
+
+ lower = upper->right;
+ upper->right = lowleft = lower->left;
+ lowleft->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ /* don't need to check for root node here because root->parent is
+ the sentinel nil node, and root->parent->left points back to root */
+
+ if (upper == upparent->left) {
+ upparent->left = lower;
+ } else {
+ dict_assert (upper == upparent->right);
+ upparent->right = lower;
+ }
+
+ lower->left = upper;
+ upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+
+static void rotate_right(dnode_t *upper)
+{
+ dnode_t *lower, *lowright, *upparent;
+
+ lower = upper->left;
+ upper->left = lowright = lower->right;
+ lowright->parent = upper;
+
+ lower->parent = upparent = upper->parent;
+
+ if (upper == upparent->right) {
+ upparent->right = lower;
+ } else {
+ dict_assert (upper == upparent->left);
+ upparent->left = lower;
+ }
+
+ lower->right = upper;
+ upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it. Used by dict_free().
+ */
+
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+ if (node == nil)
+ return;
+ free_nodes(dict, node->left, nil);
+ free_nodes(dict, node->right, nil);
+ dict->freenode(node, dict->context);
+}
+
+/*
+ * This procedure performs a verification that the given subtree is a binary
+ * search tree. It performs an inorder traversal of the tree using the
+ * dict_next() successor function, verifying that the key of each node is
+ * strictly lower than that of its successor, if duplicates are not allowed,
+ * or lower or equal if duplicates are allowed. This function is used for
+ * debugging purposes.
+ */
+#ifndef DICT_NODEBUG
+static int verify_bintree(dict_t *dict)
+{
+ dnode_t *first, *next;
+
+ first = dict_first(dict);
+
+ if (dict->dupes) {
+ while (first && (next = dict_next(dict, first))) {
+ if (dict->compare(first->key, next->key) > 0)
+ return 0;
+ first = next;
+ }
+ } else {
+ while (first && (next = dict_next(dict, first))) {
+ if (dict->compare(first->key, next->key) >= 0)
+ return 0;
+ first = next;
+ }
+ }
+ return 1;
+}
+
+/*
+ * This function recursively verifies that the given binary subtree satisfies
+ * three of the red black properties. It checks that every red node has only
+ * black children. It makes sure that each node is either red or black. And it
+ * checks that every path has the same count of black nodes from root to leaf.
+ * It returns the blackheight of the given subtree; this allows blackheights to
+ * be computed recursively and compared for left and right siblings for
+ * mismatches. It does not check for every nil node being black, because there
+ * is only one sentinel nil node. The return value of this function is the
+ * black height of the subtree rooted at the node ``root'', or zero if the
+ * subtree is not red-black.
+ */
+
+static unsigned int verify_redblack(dnode_t *nil, dnode_t *root)
+{
+ unsigned height_left, height_right;
+
+ if (root != nil) {
+ height_left = verify_redblack(nil, root->left);
+ height_right = verify_redblack(nil, root->right);
+ if (height_left == 0 || height_right == 0)
+ return 0;
+ if (height_left != height_right)
+ return 0;
+ if (root->color == dnode_red) {
+ if (root->left->color != dnode_black)
+ return 0;
+ if (root->right->color != dnode_black)
+ return 0;
+ return height_left;
+ }
+ if (root->color != dnode_black)
+ return 0;
+ return height_left + 1;
+ }
+ return 1;
+}
+
+/*
+ * Compute the actual count of nodes by traversing the tree and
+ * return it. This could be compared against the stored count to
+ * detect a mismatch.
+ */
+
+static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root)
+{
+ if (root == nil)
+ return 0;
+ else
+ return 1 + verify_node_count(nil, root->left)
+ + verify_node_count(nil, root->right);
+}
+#endif
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+ if (root != nil) {
+ return root == node
+ || verify_dict_has_node(nil, root->left, node)
+ || verify_dict_has_node(nil, root->right, node);
+ }
+ return 0;
+}
+
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Dynamically allocate and initialize a dictionary object.
+ */
+
+dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp)
+{
+ dict_t *new = malloc(sizeof *new);
+
+ if (new) {
+ new->compare = comp;
+ new->allocnode = dnode_alloc;
+ new->freenode = dnode_free;
+ new->context = NULL;
+ new->cmp_ctx = NULL;
+ new->nodecount = 0;
+ new->maxcount = maxcount;
+ new->nilnode.left = &new->nilnode;
+ new->nilnode.right = &new->nilnode;
+ new->nilnode.parent = &new->nilnode;
+ new->nilnode.color = dnode_black;
+ new->dupes = 0;
+ }
+ return new;
+}
+#endif /* E2FSCK_NOTUSED */
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+void dict_set_allocator(dict_t *dict, dnode_alloc_t al,
+ dnode_free_t fr, void *context)
+{
+ dict_assert (dict_count(dict) == 0);
+ dict_assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL));
+
+ dict->allocnode = al ? al : dnode_alloc;
+ dict->freenode = fr ? fr : dnode_free;
+ dict->context = context;
+}
+
+void dict_set_cmp_context(dict_t *dict, const void *cmp_ctx)
+{
+ dict_assert (!dict->cmp_ctx);
+ dict_assert (dict_count(dict) == 0);
+
+ dict->cmp_ctx = cmp_ctx;
+}
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Free a dynamically allocated dictionary object. Removing the nodes
+ * from the tree before deleting it is required.
+ */
+
+void dict_destroy(dict_t *dict)
+{
+ dict_assert (dict_isempty(dict));
+ free(dict);
+}
+#endif
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+
+void dict_free_nodes(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+ free_nodes(dict, root, nil);
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+}
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Obsolescent function, equivalent to dict_free_nodes
+ */
+void dict_free(dict_t *dict)
+{
+#ifdef KAZLIB_OBSOLESCENT_DEBUG
+ dict_assert ("call to obsolescent function dict_free()" && 0);
+#endif
+ dict_free_nodes(dict);
+}
+#endif
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+
+dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
+{
+ dict->compare = comp;
+ dict->allocnode = dnode_alloc;
+ dict->freenode = dnode_free;
+ dict->context = NULL;
+ dict->nodecount = 0;
+ dict->maxcount = maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = 0;
+ return dict;
+}
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Initialize a dictionary in the likeness of another dictionary
+ */
+
+void dict_init_like(dict_t *dict, const dict_t *template)
+{
+ dict->compare = template->compare;
+ dict->allocnode = template->allocnode;
+ dict->freenode = template->freenode;
+ dict->context = template->context;
+ dict->nodecount = 0;
+ dict->maxcount = template->maxcount;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict->nilnode.color = dnode_black;
+ dict->dupes = template->dupes;
+
+ dict_assert (dict_similar(dict, template));
+}
+
+/*
+ * Remove all nodes from the dictionary (without freeing them in any way).
+ */
+
+static void dict_clear(dict_t *dict)
+{
+ dict->nodecount = 0;
+ dict->nilnode.left = &dict->nilnode;
+ dict->nilnode.right = &dict->nilnode;
+ dict->nilnode.parent = &dict->nilnode;
+ dict_assert (dict->nilnode.color == dnode_black);
+}
+#endif /* E2FSCK_NOTUSED */
+
+
+/*
+ * Verify the integrity of the dictionary structure. This is provided for
+ * debugging purposes, and should be placed in assert statements. Just because
+ * this function succeeds doesn't mean that the tree is not corrupt. Certain
+ * corruptions in the tree may simply cause undefined behavior.
+ */
+#ifndef DICT_NODEBUG
+int dict_verify(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+
+ /* check that the sentinel node and root node are black */
+ if (root->color != dnode_black)
+ return 0;
+ if (nil->color != dnode_black)
+ return 0;
+ if (nil->right != nil)
+ return 0;
+ /* nil->left is the root node; check that its parent pointer is nil */
+ if (nil->left->parent != nil)
+ return 0;
+ /* perform a weak test that the tree is a binary search tree */
+ if (!verify_bintree(dict))
+ return 0;
+ /* verify that the tree is a red-black tree */
+ if (!verify_redblack(nil, root))
+ return 0;
+ if (verify_node_count(nil, root) != dict_count(dict))
+ return 0;
+ return 1;
+}
+#endif /* DICT_NODEBUG */
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Determine whether two dictionaries are similar: have the same comparison and
+ * allocator functions, and same status as to whether duplicates are allowed.
+ */
+int dict_similar(const dict_t *left, const dict_t *right)
+{
+ if (left->compare != right->compare)
+ return 0;
+
+ if (left->allocnode != right->allocnode)
+ return 0;
+
+ if (left->freenode != right->freenode)
+ return 0;
+
+ if (left->context != right->context)
+ return 0;
+
+ if (left->dupes != right->dupes)
+ return 0;
+
+ return 1;
+}
+#endif /* E2FSCK_NOTUSED */
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+
+dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *saved;
+ int result;
+
+ /* simple binary search adapted for trees that contain duplicate keys */
+
+ while (root != nil) {
+ result = dict->compare(dict->cmp_ctx, key, root->key);
+ if (result < 0)
+ root = root->left;
+ else if (result > 0)
+ root = root->right;
+ else {
+ if (!dict->dupes) { /* no duplicates, return match */
+ return root;
+ } else { /* could be dupes, find leftmost one */
+ do {
+ saved = root;
+ root = root->left;
+ while (root != nil
+ && dict->compare(dict->cmp_ctx, key, root->key))
+ root = root->right;
+ } while (root != nil);
+ return saved;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Look for the node corresponding to the lowest key that is equal to or
+ * greater than the given key. If there is no such node, return null.
+ */
+
+dnode_t *dict_lower_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+ int result = dict->compare(dict->cmp_ctx, key, root->key);
+
+ if (result > 0) {
+ root = root->right;
+ } else if (result < 0) {
+ tentative = root;
+ root = root->left;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->left;
+ }
+ }
+ }
+
+ return tentative;
+}
+
+/*
+ * Look for the node corresponding to the greatest key that is equal to or
+ * lower than the given key. If there is no such node, return null.
+ */
+
+dnode_t *dict_upper_bound(dict_t *dict, const void *key)
+{
+ dnode_t *root = dict_root(dict);
+ dnode_t *nil = dict_nil(dict);
+ dnode_t *tentative = 0;
+
+ while (root != nil) {
+ int result = dict->compare(dict->cmp_ctx, key, root->key);
+
+ if (result < 0) {
+ root = root->left;
+ } else if (result > 0) {
+ tentative = root;
+ root = root->right;
+ } else {
+ if (!dict->dupes) {
+ return root;
+ } else {
+ tentative = root;
+ root = root->right;
+ }
+ }
+ }
+
+ return tentative;
+}
+#endif
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+
+void dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+ dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+ dnode_t *parent = nil, *uncle, *grandpa;
+ int result = -1;
+
+ node->key = key;
+
+ dict_assert (!dict_isfull(dict));
+ dict_assert (!dict_contains(dict, node));
+ dict_assert (!dnode_is_in_a_dict(node));
+
+ /* basic binary tree insert */
+
+ while (where != nil) {
+ parent = where;
+ result = dict->compare(dict->cmp_ctx, key, where->key);
+ /* trap attempts at duplicate key insertion unless it's explicitly allowed */
+ dict_assert (dict->dupes || result != 0);
+ if (result < 0)
+ where = where->left;
+ else
+ where = where->right;
+ }
+
+ dict_assert (where == nil);
+
+ if (result < 0)
+ parent->left = node;
+ else
+ parent->right = node;
+
+ node->parent = parent;
+ node->left = nil;
+ node->right = nil;
+
+ dict->nodecount++;
+
+ /* red black adjustments */
+
+ node->color = dnode_red;
+
+ while (parent->color == dnode_red) {
+ grandpa = parent->parent;
+ if (parent == grandpa->left) {
+ uncle = grandpa->right;
+ if (uncle->color == dnode_red) { /* red parent, red uncle */
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else { /* red parent, black uncle */
+ if (node == parent->right) {
+ rotate_left(parent);
+ parent = node;
+ dict_assert (grandpa == parent->parent);
+ /* rotation between parent and child preserves grandpa */
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_right(grandpa);
+ break;
+ }
+ } else { /* symmetric cases: parent == parent->parent->right */
+ uncle = grandpa->left;
+ if (uncle->color == dnode_red) {
+ parent->color = dnode_black;
+ uncle->color = dnode_black;
+ grandpa->color = dnode_red;
+ node = grandpa;
+ parent = grandpa->parent;
+ } else {
+ if (node == parent->left) {
+ rotate_right(parent);
+ parent = node;
+ dict_assert (grandpa == parent->parent);
+ }
+ parent->color = dnode_black;
+ grandpa->color = dnode_red;
+ rotate_left(grandpa);
+ break;
+ }
+ }
+ }
+
+ dict_root(dict)->color = dnode_black;
+
+ dict_assert (dict_verify(dict));
+}
+
+#ifdef E2FSCK_NOTUSED
+/*
+ * Delete the given node from the dictionary. If the given node does not belong
+ * to the given dictionary, undefined behavior results. A pointer to the
+ * deleted node is returned.
+ */
+
+dnode_t *dict_delete(dict_t *dict, dnode_t *delete)
+{
+ dnode_t *nil = dict_nil(dict), *child, *delparent = delete->parent;
+
+ /* basic deletion */
+
+ dict_assert (!dict_isempty(dict));
+ dict_assert (dict_contains(dict, delete));
+
+ /*
+ * If the node being deleted has two children, then we replace it with its
+ * successor (i.e. the leftmost node in the right subtree.) By doing this,
+ * we avoid the traditional algorithm under which the successor's key and
+ * value *only* move to the deleted node and the successor is spliced out
+ * from the tree. We cannot use this approach because the user may hold
+ * pointers to the successor, or nodes may be inextricably tied to some
+ * other structures by way of embedding, etc. So we must splice out the
+ * node we are given, not some other node, and must not move contents from
+ * one node to another behind the user's back.
+ */
+
+ if (delete->left != nil && delete->right != nil) {
+ dnode_t *next = dict_next(dict, delete);
+ dnode_t *nextparent = next->parent;
+ dnode_color_t nextcolor = next->color;
+
+ dict_assert (next != nil);
+ dict_assert (next->parent != nil);
+ dict_assert (next->left == nil);
+
+ /*
+ * First, splice out the successor from the tree completely, by
+ * moving up its right child into its place.
+ */
+
+ child = next->right;
+ child->parent = nextparent;
+
+ if (nextparent->left == next) {
+ nextparent->left = child;
+ } else {
+ dict_assert (nextparent->right == next);
+ nextparent->right = child;
+ }
+
+ /*
+ * Now that the successor has been extricated from the tree, install it
+ * in place of the node that we want deleted.
+ */
+
+ next->parent = delparent;
+ next->left = delete->left;
+ next->right = delete->right;
+ next->left->parent = next;
+ next->right->parent = next;
+ next->color = delete->color;
+ delete->color = nextcolor;
+
+ if (delparent->left == delete) {
+ delparent->left = next;
+ } else {
+ dict_assert (delparent->right == delete);
+ delparent->right = next;
+ }
+
+ } else {
+ dict_assert (delete != nil);
+ dict_assert (delete->left == nil || delete->right == nil);
+
+ child = (delete->left != nil) ? delete->left : delete->right;
+
+ child->parent = delparent = delete->parent;
+
+ if (delete == delparent->left) {
+ delparent->left = child;
+ } else {
+ dict_assert (delete == delparent->right);
+ delparent->right = child;
+ }
+ }
+
+ delete->parent = NULL;
+ delete->right = NULL;
+ delete->left = NULL;
+
+ dict->nodecount--;
+
+ dict_assert (verify_bintree(dict));
+
+ /* red-black adjustments */
+
+ if (delete->color == dnode_black) {
+ dnode_t *parent, *sister;
+
+ dict_root(dict)->color = dnode_red;
+
+ while (child->color == dnode_black) {
+ parent = child->parent;
+ if (child == parent->left) {
+ sister = parent->right;
+ dict_assert (sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_left(parent);
+ sister = parent->right;
+ dict_assert (sister != nil);
+ }
+ if (sister->left->color == dnode_black
+ && sister->right->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->right->color == dnode_black) {
+ dict_assert (sister->left->color == dnode_red);
+ sister->left->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_right(sister);
+ sister = parent->right;
+ dict_assert (sister != nil);
+ }
+ sister->color = parent->color;
+ sister->right->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_left(parent);
+ break;
+ }
+ } else { /* symmetric case: child == child->parent->right */
+ dict_assert (child == parent->right);
+ sister = parent->left;
+ dict_assert (sister != nil);
+ if (sister->color == dnode_red) {
+ sister->color = dnode_black;
+ parent->color = dnode_red;
+ rotate_right(parent);
+ sister = parent->left;
+ dict_assert (sister != nil);
+ }
+ if (sister->right->color == dnode_black
+ && sister->left->color == dnode_black) {
+ sister->color = dnode_red;
+ child = parent;
+ } else {
+ if (sister->left->color == dnode_black) {
+ dict_assert (sister->right->color == dnode_red);
+ sister->right->color = dnode_black;
+ sister->color = dnode_red;
+ rotate_left(sister);
+ sister = parent->left;
+ dict_assert (sister != nil);
+ }
+ sister->color = parent->color;
+ sister->left->color = dnode_black;
+ parent->color = dnode_black;
+ rotate_right(parent);
+ break;
+ }
+ }
+ }
+
+ child->color = dnode_black;
+ dict_root(dict)->color = dnode_black;
+ }
+
+ dict_assert (dict_verify(dict));
+
+ return delete;
+}
+#endif /* E2FSCK_NOTUSED */
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+
+int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+ dnode_t *node = dict->allocnode(dict->context);
+
+ if (node) {
+ dnode_init(node, data);
+ dict_insert(dict, node, key);
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef E2FSCK_NOTUSED
+void dict_delete_free(dict_t *dict, dnode_t *node)
+{
+ dict_delete(dict, node);
+ dict->freenode(node, dict->context);
+}
+#endif
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_first(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+ if (root != nil)
+ while ((left = root->left) != nil)
+ root = left;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the node with the highest (rightmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+dnode_t *dict_last(dict_t *dict)
+{
+ dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *right;
+
+ if (root != nil)
+ while ((right = root->right) != nil)
+ root = right;
+
+ return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * next key in the the left to right ordering. If the node has
+ * no successor, a null pointer is returned rather than a pointer to
+ * the nil node.
+ */
+
+dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *left;
+
+ if (curr->right != nil) {
+ curr = curr->right;
+ while ((left = curr->left) != nil)
+ curr = left;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->right) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+/*
+ * Return the given node's predecessor, in the key order.
+ * The nil sentinel node is returned if there is no predecessor.
+ */
+
+dnode_t *dict_prev(dict_t *dict, dnode_t *curr)
+{
+ dnode_t *nil = dict_nil(dict), *parent, *right;
+
+ if (curr->left != nil) {
+ curr = curr->left;
+ while ((right = curr->right) != nil)
+ curr = right;
+ return curr;
+ }
+
+ parent = curr->parent;
+
+ while (parent != nil && curr == parent->left) {
+ curr = parent;
+ parent = curr->parent;
+ }
+
+ return (parent == nil) ? NULL : parent;
+}
+
+void dict_allow_dupes(dict_t *dict)
+{
+ dict->dupes = 1;
+}
+
+#undef dict_count
+#undef dict_isempty
+#undef dict_isfull
+#undef dnode_get
+#undef dnode_put
+#undef dnode_getkey
+
+dictcount_t dict_count(dict_t *dict)
+{
+ return dict->nodecount;
+}
+
+int dict_isempty(dict_t *dict)
+{
+ return dict->nodecount == 0;
+}
+
+int dict_isfull(dict_t *dict)
+{
+ return dict->nodecount == dict->maxcount;
+}
+
+int dict_contains(dict_t *dict, dnode_t *node)
+{
+ return verify_dict_has_node(dict_nil(dict), dict_root(dict), node);
+}
+
+static dnode_t *dnode_alloc(void *context EXT2FS_ATTR((unused)))
+{
+ return malloc(sizeof *dnode_alloc(NULL));
+}
+
+static void dnode_free(dnode_t *node, void *context EXT2FS_ATTR((unused)))
+{
+ free(node);
+}
+
+dnode_t *dnode_create(void *data)
+{
+ dnode_t *new = malloc(sizeof *new);
+ if (new) {
+ new->data = data;
+ new->parent = NULL;
+ new->left = NULL;
+ new->right = NULL;
+ }
+ return new;
+}
+
+dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+ dnode->parent = NULL;
+ dnode->left = NULL;
+ dnode->right = NULL;
+ return dnode;
+}
+
+void dnode_destroy(dnode_t *dnode)
+{
+ dict_assert (!dnode_is_in_a_dict(dnode));
+ free(dnode);
+}
+
+void *dnode_get(dnode_t *dnode)
+{
+ return dnode->data;
+}
+
+const void *dnode_getkey(dnode_t *dnode)
+{
+ return dnode->key;
+}
+
+#ifdef E2FSCK_NOTUSED
+void dnode_put(dnode_t *dnode, void *data)
+{
+ dnode->data = data;
+}
+#endif
+
+#ifndef DICT_NODEBUG
+int dnode_is_in_a_dict(dnode_t *dnode)
+{
+ return (dnode->parent && dnode->left && dnode->right);
+}
+#endif
+
+#ifdef E2FSCK_NOTUSED
+void dict_process(dict_t *dict, void *context, dnode_process_t function)
+{
+ dnode_t *node = dict_first(dict), *next;
+
+ while (node != NULL) {
+ /* check for callback function deleting */
+ /* the next node from under us */
+ dict_assert (dict_contains(dict, node));
+ next = dict_next(dict, node);
+ function(dict, node, context);
+ node = next;
+ }
+}
+
+static void load_begin_internal(dict_load_t *load, dict_t *dict)
+{
+ load->dictptr = dict;
+ load->nilnode.left = &load->nilnode;
+ load->nilnode.right = &load->nilnode;
+}
+
+void dict_load_begin(dict_load_t *load, dict_t *dict)
+{
+ dict_assert (dict_isempty(dict));
+ load_begin_internal(load, dict);
+}
+
+void dict_load_next(dict_load_t *load, dnode_t *newnode, const void *key)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *nil = &load->nilnode;
+
+ dict_assert (!dnode_is_in_a_dict(newnode));
+ dict_assert (dict->nodecount < dict->maxcount);
+
+#ifndef DICT_NODEBUG
+ if (dict->nodecount > 0) {
+ if (dict->dupes)
+ dict_assert (dict->compare(nil->left->key, key) <= 0);
+ else
+ dict_assert (dict->compare(nil->left->key, key) < 0);
+ }
+#endif
+
+ newnode->key = key;
+ nil->right->left = newnode;
+ nil->right = newnode;
+ newnode->left = nil;
+ dict->nodecount++;
+}
+
+void dict_load_end(dict_load_t *load)
+{
+ dict_t *dict = load->dictptr;
+ dnode_t *tree[DICT_DEPTH_MAX] = { 0 };
+ dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next;
+ dnode_t *complete = 0;
+ dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount;
+ dictcount_t botrowcount;
+ unsigned baselevel = 0, level = 0, i;
+
+ dict_assert (dnode_red == 0 && dnode_black == 1);
+
+ while (fullcount >= nodecount && fullcount)
+ fullcount >>= 1;
+
+ botrowcount = nodecount - fullcount;
+
+ for (curr = loadnil->left; curr != loadnil; curr = next) {
+ next = curr->left;
+
+ if (complete == NULL && botrowcount-- == 0) {
+ dict_assert (baselevel == 0);
+ dict_assert (level == 0);
+ baselevel = level = 1;
+ complete = tree[0];
+
+ if (complete != 0) {
+ tree[0] = 0;
+ complete->right = dictnil;
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ }
+ }
+
+ if (complete == NULL) {
+ curr->left = dictnil;
+ curr->right = dictnil;
+ curr->color = level % 2;
+ complete = curr;
+
+ dict_assert (level == baselevel);
+ while (tree[level] != 0) {
+ tree[level]->right = complete;
+ complete->parent = tree[level];
+ complete = tree[level];
+ tree[level++] = 0;
+ }
+ } else {
+ curr->left = complete;
+ curr->color = (level + 1) % 2;
+ complete->parent = curr;
+ tree[level] = curr;
+ complete = 0;
+ level = baselevel;
+ }
+ }
+
+ if (complete == NULL)
+ complete = dictnil;
+
+ for (i = 0; i < DICT_DEPTH_MAX; i++) {
+ if (tree[i] != 0) {
+ tree[i]->right = complete;
+ complete->parent = tree[i];
+ complete = tree[i];
+ }
+ }
+
+ dictnil->color = dnode_black;
+ dictnil->right = dictnil;
+ complete->parent = dictnil;
+ complete->color = dnode_black;
+ dict_root(dict) = complete;
+
+ dict_assert (dict_verify(dict));
+}
+
+void dict_merge(dict_t *dest, dict_t *source)
+{
+ dict_load_t load;
+ dnode_t *leftnode = dict_first(dest), *rightnode = dict_first(source);
+
+ dict_assert (dict_similar(dest, source));
+
+ if (source == dest)
+ return;
+
+ dest->nodecount = 0;
+ load_begin_internal(&load, dest);
+
+ for (;;) {
+ if (leftnode != NULL && rightnode != NULL) {
+ if (dest->compare(leftnode->key, rightnode->key) < 0)
+ goto copyleft;
+ else
+ goto copyright;
+ } else if (leftnode != NULL) {
+ goto copyleft;
+ } else if (rightnode != NULL) {
+ goto copyright;
+ } else {
+ dict_assert (leftnode == NULL && rightnode == NULL);
+ break;
+ }
+
+ copyleft:
+ {
+ dnode_t *next = dict_next(dest, leftnode);
+#ifndef DICT_NODEBUG
+ leftnode->left = NULL; /* suppress assertion in dict_load_next */
+#endif
+ dict_load_next(&load, leftnode, leftnode->key);
+ leftnode = next;
+ continue;
+ }
+
+ copyright:
+ {
+ dnode_t *next = dict_next(source, rightnode);
+#ifndef DICT_NODEBUG
+ rightnode->left = NULL;
+#endif
+ dict_load_next(&load, rightnode, rightnode->key);
+ rightnode = next;
+ continue;
+ }
+ }
+
+ dict_clear(source);
+ dict_load_end(&load);
+}
+#endif /* E2FSCK_NOTUSED */
+
+#ifdef KAZLIB_TEST_MAIN
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+typedef char input_t[256];
+
+static int tokenize(char *string, ...)
+{
+ char **tokptr;
+ va_list arglist;
+ int tokcount = 0;
+
+ va_start(arglist, string);
+ tokptr = va_arg(arglist, char **);
+ while (tokptr) {
+ while (*string && isspace((unsigned char) *string))
+ string++;
+ if (!*string)
+ break;
+ *tokptr = string;
+ while (*string && !isspace((unsigned char) *string))
+ string++;
+ tokptr = va_arg(arglist, char **);
+ tokcount++;
+ if (!*string)
+ break;
+ *string++ = 0;
+ }
+ va_end(arglist);
+
+ return tokcount;
+}
+
+static int comparef(const void *cmp_ctx, const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+static char *dupstring(char *str)
+{
+ int sz = strlen(str) + 1;
+ char *new = malloc(sz);
+ if (new)
+ memcpy(new, str, sz);
+ return new;
+}
+
+static dnode_t *new_node(void *c)
+{
+ static dnode_t few[5];
+ static int count;
+
+ if (count < 5)
+ return few + count++;
+
+ return NULL;
+}
+
+static void del_node(dnode_t *n, void *c)
+{
+}
+
+static int prompt = 0;
+
+static void construct(dict_t *d)
+{
+ input_t in;
+ int done = 0;
+ dict_load_t dl;
+ dnode_t *dn;
+ char *tok1, *tok2, *val;
+ const char *key;
+ char *help =
+ "p turn prompt on\n"
+ "q finish construction\n"
+ "a <key> <val> add new entry\n";
+
+ if (!dict_isempty(d))
+ puts("warning: dictionary not empty!");
+
+ dict_load_begin(&dl, d);
+
+ while (!done) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch (in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 'q':
+ done = 1;
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ }
+ key = dupstring(tok1);
+ val = dupstring(tok2);
+ dn = dnode_create(val);
+
+ if (!key || !val || !dn) {
+ puts("out of memory");
+ free((void *) key);
+ free(val);
+ if (dn)
+ dnode_destroy(dn);
+ }
+
+ dict_load_next(&dl, dn, key);
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ dict_load_end(&dl);
+}
+
+int main(void)
+{
+ input_t in;
+ dict_t darray[10];
+ dict_t *d = &darray[0];
+ dnode_t *dn;
+ int i;
+ char *tok1, *tok2, *val;
+ const char *key;
+
+ char *help =
+ "a <key> <val> add value to dictionary\n"
+ "d <key> delete value from dictionary\n"
+ "l <key> lookup value in dictionary\n"
+ "( <key> lookup lower bound\n"
+ ") <key> lookup upper bound\n"
+ "# <num> switch to alternate dictionary (0-9)\n"
+ "j <num> <num> merge two dictionaries\n"
+ "f free the whole dictionary\n"
+ "k allow duplicate keys\n"
+ "c show number of entries\n"
+ "t dump whole dictionary in sort order\n"
+ "m make dictionary out of sorted items\n"
+ "p turn prompt on\n"
+ "s switch to non-functioning allocator\n"
+ "q quit";
+
+ for (i = 0; i < sizeof darray / sizeof *darray; i++)
+ dict_init(&darray[i], DICTCOUNT_T_MAX, comparef);
+
+ for (;;) {
+ if (prompt)
+ putchar('>');
+ fflush(stdout);
+
+ if (!fgets(in, sizeof(input_t), stdin))
+ break;
+
+ switch(in[0]) {
+ case '?':
+ puts(help);
+ break;
+ case 'a':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ }
+ key = dupstring(tok1);
+ val = dupstring(tok2);
+
+ if (!key || !val) {
+ puts("out of memory");
+ free((void *) key);
+ free(val);
+ }
+
+ if (!dict_alloc_insert(d, key, val)) {
+ puts("dict_alloc_insert failed");
+ free((void *) key);
+ free(val);
+ break;
+ }
+ break;
+ case 'd':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ dn = dict_lookup(d, tok1);
+ if (!dn) {
+ puts("dict_lookup failed");
+ break;
+ }
+ val = dnode_get(dn);
+ key = dnode_getkey(dn);
+ dict_delete_free(d, dn);
+
+ free(val);
+ free((void *) key);
+ break;
+ case 'f':
+ dict_free(d);
+ break;
+ case 'l':
+ case '(':
+ case ')':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ }
+ dn = 0;
+ switch (in[0]) {
+ case 'l':
+ dn = dict_lookup(d, tok1);
+ break;
+ case '(':
+ dn = dict_lower_bound(d, tok1);
+ break;
+ case ')':
+ dn = dict_upper_bound(d, tok1);
+ break;
+ }
+ if (!dn) {
+ puts("lookup failed");
+ break;
+ }
+ val = dnode_get(dn);
+ puts(val);
+ break;
+ case 'm':
+ construct(d);
+ break;
+ case 'k':
+ dict_allow_dupes(d);
+ break;
+ case 'c':
+ printf("%lu\n", (unsigned long) dict_count(d));
+ break;
+ case 't':
+ for (dn = dict_first(d); dn; dn = dict_next(d, dn)) {
+ printf("%s\t%s\n", (char *) dnode_getkey(dn),
+ (char *) dnode_get(dn));
+ }
+ break;
+ case 'q':
+ exit(0);
+ break;
+ case '\0':
+ break;
+ case 'p':
+ prompt = 1;
+ break;
+ case 's':
+ dict_set_allocator(d, new_node, del_node, NULL);
+ break;
+ case '#':
+ if (tokenize(in+1, &tok1, (char **) 0) != 1) {
+ puts("what?");
+ break;
+ } else {
+ int dictnum = atoi(tok1);
+ if (dictnum < 0 || dictnum > 9) {
+ puts("invalid number");
+ break;
+ }
+ d = &darray[dictnum];
+ }
+ break;
+ case 'j':
+ if (tokenize(in+1, &tok1, &tok2, (char **) 0) != 2) {
+ puts("what?");
+ break;
+ } else {
+ int dict1 = atoi(tok1), dict2 = atoi(tok2);
+ if (dict1 < 0 || dict1 > 9 || dict2 < 0 || dict2 > 9) {
+ puts("invalid number");
+ break;
+ }
+ dict_merge(&darray[dict1], &darray[dict2]);
+ }
+ break;
+ default:
+ putchar('?');
+ putchar('\n');
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/lib/support/dict.h b/lib/support/dict.h
new file mode 100644
index 0000000..2d87cc0
--- /dev/null
+++ b/lib/support/dict.h
@@ -0,0 +1,147 @@
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * Free Software License:
+ *
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $
+ * $Name: kazlib_1_20 $
+ * The work has been modified.
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+#include <limits.h>
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#include "sfx.h"
+#endif
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ struct dnode_t *dict_left;
+ struct dnode_t *dict_right;
+ struct dnode_t *dict_parent;
+ dnode_color_t dict_color;
+ const void *dict_key;
+ void *dict_data;
+#else
+ int dict_dummy;
+#endif
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *, const void *);
+typedef dnode_t *(*dnode_alloc_t)(void *);
+typedef void (*dnode_free_t)(dnode_t *, void *);
+
+typedef struct dict_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ dnode_t dict_nilnode;
+ dictcount_t dict_nodecount;
+ dictcount_t dict_maxcount;
+ dict_comp_t dict_compare;
+ dnode_alloc_t dict_allocnode;
+ dnode_free_t dict_freenode;
+ void *dict_context;
+ const void *cmp_ctx;
+ int dict_dupes;
+#else
+ int dict_dummmy;
+#endif
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+ dict_t *dict_dictptr;
+ dnode_t dict_nilnode;
+#else
+ int dict_dummmy;
+#endif
+} dict_load_t;
+
+extern dict_t *dict_create(dictcount_t, dict_comp_t);
+extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
+extern void dict_set_cmp_context(dict_t *, const void *);
+extern void dict_destroy(dict_t *);
+extern void dict_free_nodes(dict_t *);
+extern void dict_free(dict_t *);
+extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
+extern void dict_init_like(dict_t *, const dict_t *);
+extern int dict_verify(dict_t *);
+extern int dict_similar(const dict_t *, const dict_t *);
+extern dnode_t *dict_lookup(dict_t *, const void *);
+extern dnode_t *dict_lower_bound(dict_t *, const void *);
+extern dnode_t *dict_upper_bound(dict_t *, const void *);
+extern void dict_insert(dict_t *, dnode_t *, const void *);
+extern dnode_t *dict_delete(dict_t *, dnode_t *);
+extern int dict_alloc_insert(dict_t *, const void *, void *);
+extern void dict_delete_free(dict_t *, dnode_t *);
+extern dnode_t *dict_first(dict_t *);
+extern dnode_t *dict_last(dict_t *);
+extern dnode_t *dict_next(dict_t *, dnode_t *);
+extern dnode_t *dict_prev(dict_t *, dnode_t *);
+extern dictcount_t dict_count(dict_t *);
+extern int dict_isempty(dict_t *);
+extern int dict_isfull(dict_t *);
+extern int dict_contains(dict_t *, dnode_t *);
+extern void dict_allow_dupes(dict_t *);
+extern int dnode_is_in_a_dict(dnode_t *);
+extern dnode_t *dnode_create(void *);
+extern dnode_t *dnode_init(dnode_t *, void *);
+extern void dnode_destroy(dnode_t *);
+extern void *dnode_get(dnode_t *);
+extern const void *dnode_getkey(dnode_t *);
+extern void dnode_put(dnode_t *, void *);
+extern void dict_process(dict_t *, void *, dnode_process_t);
+extern void dict_load_begin(dict_load_t *, dict_t *);
+extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
+extern void dict_load_end(dict_load_t *);
+extern void dict_merge(dict_t *, dict_t *);
+
+#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
+#ifdef KAZLIB_SIDEEFFECT_DEBUG
+#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
+#else
+#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
+#endif
+#define dict_count(D) ((D)->dict_nodecount)
+#define dict_isempty(D) ((D)->dict_nodecount == 0)
+#define dnode_get(N) ((N)->dict_data)
+#define dnode_getkey(N) ((N)->dict_key)
+#define dnode_put(N, X) ((N)->dict_data = (X))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/support/dqblk_v2.h b/lib/support/dqblk_v2.h
new file mode 100644
index 0000000..d12512a
--- /dev/null
+++ b/lib/support/dqblk_v2.h
@@ -0,0 +1,31 @@
+/*
+ * Header file for disk format of new quotafile format
+ *
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ */
+
+#ifndef __QUOTA_DQBLK_V2_H__
+#define __QUOTA_DQBLK_V2_H__
+
+#include "quotaio_tree.h"
+
+/* Structure for format specific information */
+struct v2_mem_dqinfo {
+ struct qtree_mem_dqinfo dqi_qtree;
+ unsigned int dqi_flags; /* Flags set in quotafile */
+ unsigned int dqi_used_entries; /* Number of entries in file -
+ updated by scan_dquots */
+ unsigned int dqi_data_blocks; /* Number of data blocks in file -
+ updated by scan_dquots */
+};
+
+struct v2_mem_dqblk {
+ long long dqb_off; /* Offset of dquot in file */
+};
+
+struct quotafile_ops; /* Will be defined later in quotaio.h */
+
+/* Operations above this format */
+extern struct quotafile_ops quotafile_ops_2;
+
+#endif /* __QUOTA_DQBLK_V2_H__ */
diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
new file mode 100644
index 0000000..9339c99
--- /dev/null
+++ b/lib/support/mkquota.c
@@ -0,0 +1,707 @@
+/*
+ * mkquota.c --- create quota files for a filesystem
+ *
+ * Aditya Kali <adityakali@google.com>
+ */
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "e2p/e2p.h"
+
+#include "quotaio.h"
+#include "quotaio_v2.h"
+#include "quotaio_tree.h"
+#include "common.h"
+#include "dict.h"
+
+/* Needed for architectures where sizeof(int) != sizeof(void *) */
+#define UINT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
+#define VOIDPTR_TO_UINT(ptr) ((unsigned int)(intptr_t)(ptr))
+
+#if DEBUG_QUOTA
+static void print_inode(struct ext2_inode *inode)
+{
+ if (!inode)
+ return;
+
+ fprintf(stderr, " i_mode = %d\n", inode->i_mode);
+ fprintf(stderr, " i_uid = %d\n", inode->i_uid);
+ fprintf(stderr, " i_size = %d\n", inode->i_size);
+ fprintf(stderr, " i_atime = %d\n", inode->i_atime);
+ fprintf(stderr, " i_ctime = %d\n", inode->i_ctime);
+ fprintf(stderr, " i_mtime = %d\n", inode->i_mtime);
+ fprintf(stderr, " i_dtime = %d\n", inode->i_dtime);
+ fprintf(stderr, " i_gid = %d\n", inode->i_gid);
+ fprintf(stderr, " i_links_count = %d\n", inode->i_links_count);
+ fprintf(stderr, " i_blocks = %d\n", inode->i_blocks);
+ fprintf(stderr, " i_flags = %d\n", inode->i_flags);
+
+ return;
+}
+
+static void print_dquot(const char *desc, struct dquot *dq)
+{
+ if (desc)
+ fprintf(stderr, "%s: ", desc);
+ fprintf(stderr, "%u %lld:%lld:%lld %lld:%lld:%lld\n",
+ dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
+ (long long) dq->dq_dqb.dqb_bsoftlimit,
+ (long long) dq->dq_dqb.dqb_bhardlimit,
+ (long long) dq->dq_dqb.dqb_curinodes,
+ (long long) dq->dq_dqb.dqb_isoftlimit,
+ (long long) dq->dq_dqb.dqb_ihardlimit);
+}
+#else
+static void print_dquot(const char *desc EXT2FS_ATTR((unused)),
+ struct dquot *dq EXT2FS_ATTR((unused)))
+{
+}
+#endif
+
+/*
+ * Returns 0 if not able to find the quota file, otherwise returns its
+ * inode number.
+ */
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype)
+{
+ char qf_name[256];
+ errcode_t ret;
+ ext2_ino_t ino;
+
+ if (qtype >= MAXQUOTAS)
+ return -EINVAL;
+
+ quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name);
+
+ ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
+ &ino);
+ if (ret)
+ return 0;
+
+ return ino;
+}
+
+/*
+ * Set the value for reserved quota inode number field in superblock.
+ */
+void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype)
+{
+ ext2_ino_t *inump;
+
+ inump = quota_sb_inump(fs->super, qtype);
+
+ log_debug("setting quota ino in superblock: ino=%u, type=%d", ino,
+ qtype);
+ if (inump == NULL)
+ return;
+ *inump = ino;
+ ext2fs_mark_super_dirty(fs);
+}
+
+errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype)
+{
+ ext2_ino_t qf_ino;
+ errcode_t retval;
+
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval) {
+ log_debug("Couldn't read bitmaps: %s", error_message(retval));
+ return retval;
+ }
+
+ qf_ino = *quota_sb_inump(fs->super, qtype);
+ if (qf_ino == 0)
+ return 0;
+ retval = quota_inode_truncate(fs, qf_ino);
+ if (retval)
+ return retval;
+ if (qf_ino >= EXT2_FIRST_INODE(fs->super)) {
+ struct ext2_inode inode;
+
+ retval = ext2fs_read_inode(fs, qf_ino, &inode);
+ if (!retval) {
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ ext2fs_write_inode(fs, qf_ino, &inode);
+ }
+ ext2fs_inode_alloc_stats2(fs, qf_ino, -1, 0);
+ ext2fs_mark_ib_dirty(fs);
+
+ }
+ quota_set_sb_inum(fs, 0, qtype);
+
+ ext2fs_mark_super_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ retval = ext2fs_write_bitmaps(fs);
+ if (retval) {
+ log_debug("Couldn't write bitmaps: %s", error_message(retval));
+ return retval;
+ }
+ return 0;
+}
+
+static void write_dquots(dict_t *dict, struct quota_handle *qh)
+{
+ dnode_t *n;
+ struct dquot *dq;
+
+ for (n = dict_first(dict); n; n = dict_next(dict, n)) {
+ dq = dnode_get(n);
+ if (dq) {
+ print_dquot("write", dq);
+ dq->dq_h = qh;
+ update_grace_times(dq);
+ qh->qh_ops->commit_dquot(dq);
+ }
+ }
+}
+
+errcode_t quota_write_inode(quota_ctx_t qctx, unsigned int qtype_bits)
+{
+ int retval = 0;
+ enum quota_type qtype;
+ dict_t *dict;
+ ext2_filsys fs;
+ struct quota_handle *h = NULL;
+ int fmt = QFMT_VFS_V1;
+
+ if (!qctx)
+ return 0;
+
+ fs = qctx->fs;
+ retval = ext2fs_get_mem(sizeof(struct quota_handle), &h);
+ if (retval) {
+ log_debug("Unable to allocate quota handle: %s",
+ error_message(retval));
+ goto out;
+ }
+
+ retval = ext2fs_read_bitmaps(fs);
+ if (retval) {
+ log_debug("Couldn't read bitmaps: %s", error_message(retval));
+ goto out;
+ }
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ if (((1 << qtype) & qtype_bits) == 0)
+ continue;
+
+ dict = qctx->quota_dict[qtype];
+ if (!dict)
+ continue;
+
+ retval = quota_file_create(h, fs, qtype, fmt);
+ if (retval) {
+ log_debug("Cannot initialize io on quotafile: %s",
+ error_message(retval));
+ goto out;
+ }
+
+ write_dquots(dict, h);
+ retval = quota_file_close(qctx, h);
+ if (retval) {
+ log_debug("Cannot finish IO on new quotafile: %s",
+ strerror(errno));
+ if (h->qh_qf.e2_file)
+ ext2fs_file_close(h->qh_qf.e2_file);
+ (void) quota_inode_truncate(fs, h->qh_qf.ino);
+ goto out;
+ }
+
+ /* Set quota inode numbers in superblock. */
+ quota_set_sb_inum(fs, h->qh_qf.ino, qtype);
+ ext2fs_mark_super_dirty(fs);
+ ext2fs_mark_bb_dirty(fs);
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ }
+
+ retval = ext2fs_write_bitmaps(fs);
+ if (retval) {
+ log_debug("Couldn't write bitmaps: %s", error_message(retval));
+ goto out;
+ }
+out:
+ if (h)
+ ext2fs_free_mem(&h);
+ return retval;
+}
+
+/******************************************************************/
+/* Helper functions for computing quota in memory. */
+/******************************************************************/
+
+static int dict_uint_cmp(const void *cmp_ctx EXT2FS_ATTR((unused)),
+ const void *a, const void *b)
+{
+ unsigned int c, d;
+
+ c = VOIDPTR_TO_UINT(a);
+ d = VOIDPTR_TO_UINT(b);
+
+ if (c == d)
+ return 0;
+ else if (c > d)
+ return 1;
+ else
+ return -1;
+}
+
+static inline int project_quota_valid(quota_ctx_t qctx)
+{
+ return (EXT2_INODE_SIZE(qctx->fs->super) > EXT2_GOOD_OLD_INODE_SIZE);
+}
+
+static inline qid_t get_qid(struct ext2_inode_large *inode, enum quota_type qtype)
+{
+ unsigned int inode_size;
+
+ switch (qtype) {
+ case USRQUOTA:
+ return inode_uid(*inode);
+ case GRPQUOTA:
+ return inode_gid(*inode);
+ case PRJQUOTA:
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize;
+ if (inode_includes(inode_size, i_projid))
+ return inode_projid(*inode);
+ return 0;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static void quota_dnode_free(dnode_t *node,
+ void *context EXT2FS_ATTR((unused)))
+{
+ void *ptr = node ? dnode_get(node) : 0;
+
+ ext2fs_free_mem(&ptr);
+ free(node);
+}
+
+/*
+ * Set up the quota tracking data structures.
+ */
+errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs,
+ unsigned int qtype_bits)
+{
+ errcode_t err;
+ dict_t *dict;
+ quota_ctx_t ctx;
+ enum quota_type qtype;
+
+ err = ext2fs_get_mem(sizeof(struct quota_ctx), &ctx);
+ if (err) {
+ log_debug("Failed to allocate quota context");
+ return err;
+ }
+
+ memset(ctx, 0, sizeof(struct quota_ctx));
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ ctx->quota_file[qtype] = NULL;
+ if (qtype_bits) {
+ if (((1 << qtype) & qtype_bits) == 0)
+ continue;
+ } else {
+ if (*quota_sb_inump(fs->super, qtype) == 0)
+ continue;
+ }
+ err = ext2fs_get_mem(sizeof(dict_t), &dict);
+ if (err) {
+ log_debug("Failed to allocate dictionary");
+ quota_release_context(&ctx);
+ return err;
+ }
+ ctx->quota_dict[qtype] = dict;
+ dict_init(dict, DICTCOUNT_T_MAX, dict_uint_cmp);
+ dict_set_allocator(dict, NULL, quota_dnode_free, NULL);
+ }
+
+ ctx->fs = fs;
+ *qctx = ctx;
+ return 0;
+}
+
+void quota_release_context(quota_ctx_t *qctx)
+{
+ errcode_t err;
+ dict_t *dict;
+ enum quota_type qtype;
+ quota_ctx_t ctx;
+
+ if (!qctx)
+ return;
+
+ ctx = *qctx;
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ dict = ctx->quota_dict[qtype];
+ ctx->quota_dict[qtype] = 0;
+ if (dict) {
+ dict_free_nodes(dict);
+ free(dict);
+ }
+ if (ctx->quota_file[qtype]) {
+ err = quota_file_close(ctx, ctx->quota_file[qtype]);
+ if (err) {
+ log_err("Cannot close quotafile: %s",
+ strerror(errno));
+ ext2fs_free_mem(&ctx->quota_file[qtype]);
+ }
+ }
+ }
+ *qctx = NULL;
+ free(ctx);
+}
+
+static struct dquot *get_dq(dict_t *dict, __u32 key)
+{
+ struct dquot *dq;
+ dnode_t *n;
+
+ n = dict_lookup(dict, UINT_TO_VOIDPTR(key));
+ if (n)
+ dq = dnode_get(n);
+ else {
+ if (ext2fs_get_mem(sizeof(struct dquot), &dq)) {
+ log_err("Unable to allocate dquot");
+ return NULL;
+ }
+ memset(dq, 0, sizeof(struct dquot));
+ dict_alloc_insert(dict, UINT_TO_VOIDPTR(key), dq);
+ dq->dq_id = key;
+ }
+ return dq;
+}
+
+
+/*
+ * Called to update the blocks used by a particular inode
+ */
+void quota_data_add(quota_ctx_t qctx, struct ext2_inode_large *inode,
+ ext2_ino_t ino EXT2FS_ATTR((unused)),
+ qsize_t space)
+{
+ struct dquot *dq;
+ dict_t *dict;
+ enum quota_type qtype;
+
+ if (!qctx)
+ return;
+
+ log_debug("ADD_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
+ inode_uid(*inode),
+ inode_gid(*inode), space);
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ if (qtype == PRJQUOTA && !project_quota_valid(qctx))
+ continue;
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ dq = get_dq(dict, get_qid(inode, qtype));
+ if (dq)
+ dq->dq_dqb.dqb_curspace += space;
+ }
+ }
+}
+
+/*
+ * Called to remove some blocks used by a particular inode
+ */
+void quota_data_sub(quota_ctx_t qctx, struct ext2_inode_large *inode,
+ ext2_ino_t ino EXT2FS_ATTR((unused)),
+ qsize_t space)
+{
+ struct dquot *dq;
+ dict_t *dict;
+ enum quota_type qtype;
+
+ if (!qctx)
+ return;
+
+ log_debug("SUB_DATA: Inode: %u, UID/GID: %u/%u, space: %ld", ino,
+ inode_uid(*inode),
+ inode_gid(*inode), space);
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ if (qtype == PRJQUOTA && !project_quota_valid(qctx))
+ continue;
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ dq = get_dq(dict, get_qid(inode, qtype));
+ if (dq)
+ dq->dq_dqb.dqb_curspace -= space;
+ }
+ }
+}
+
+/*
+ * Called to count the files used by an inode's user/group
+ */
+void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode_large *inode,
+ ext2_ino_t ino EXT2FS_ATTR((unused)), int adjust)
+{
+ struct dquot *dq;
+ dict_t *dict;
+ enum quota_type qtype;
+
+ if (!qctx)
+ return;
+
+ log_debug("ADJ_INODE: Inode: %u, UID/GID: %u/%u, adjust: %d", ino,
+ inode_uid(*inode),
+ inode_gid(*inode), adjust);
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
+ if (qtype == PRJQUOTA && !project_quota_valid(qctx))
+ continue;
+ dict = qctx->quota_dict[qtype];
+ if (dict) {
+ dq = get_dq(dict, get_qid(inode, qtype));
+ if (dq)
+ dq->dq_dqb.dqb_curinodes += adjust;
+ }
+ }
+}
+
+errcode_t quota_compute_usage(quota_ctx_t qctx)
+{
+ ext2_filsys fs;
+ ext2_ino_t ino;
+ errcode_t ret;
+ struct ext2_inode_large *inode;
+ int inode_size;
+ qsize_t space;
+ ext2_inode_scan scan;
+
+ if (!qctx)
+ return 0;
+
+ fs = qctx->fs;
+ ret = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (ret) {
+ log_err("while opening inode scan. ret=%ld", ret);
+ return ret;
+ }
+ inode_size = fs->super->s_inode_size;
+ inode = malloc(inode_size);
+ if (!inode) {
+ ext2fs_close_inode_scan(scan);
+ return ENOMEM;
+ }
+ while (1) {
+ ret = ext2fs_get_next_inode_full(scan, &ino,
+ EXT2_INODE(inode), inode_size);
+ if (ret) {
+ log_err("while getting next inode. ret=%ld", ret);
+ ext2fs_close_inode_scan(scan);
+ free(inode);
+ return ret;
+ }
+ if (ino == 0)
+ break;
+ if (!inode->i_links_count)
+ continue;
+ if (ino == EXT2_ROOT_INO ||
+ (ino >= EXT2_FIRST_INODE(fs->super) &&
+ ino != quota_type2inum(PRJQUOTA, fs->super) &&
+ ino != fs->super->s_orphan_file_inum)) {
+ space = ext2fs_get_stat_i_blocks(fs,
+ EXT2_INODE(inode)) << 9;
+ quota_data_add(qctx, inode, ino, space);
+ quota_data_inodes(qctx, inode, ino, +1);
+ }
+ }
+
+ ext2fs_close_inode_scan(scan);
+ free(inode);
+ return 0;
+}
+
+struct scan_dquots_data {
+ dict_t *quota_dict;
+ int update_limits; /* update limits from disk */
+ int update_usage;
+ int check_consistency;
+ int usage_is_inconsistent;
+};
+
+static int scan_dquots_callback(struct dquot *dquot, void *cb_data)
+{
+ struct scan_dquots_data *scan_data = cb_data;
+ dict_t *quota_dict = scan_data->quota_dict;
+ struct dquot *dq;
+
+ dq = get_dq(quota_dict, dquot->dq_id);
+ if (!dq)
+ return -1;
+ dq->dq_id = dquot->dq_id;
+ dq->dq_flags |= DQF_SEEN;
+
+ print_dquot("mem", dq);
+ print_dquot("dsk", dquot);
+
+ /* Check if there is inconsistency */
+ if (scan_data->check_consistency &&
+ (dq->dq_dqb.dqb_curspace != dquot->dq_dqb.dqb_curspace ||
+ dq->dq_dqb.dqb_curinodes != dquot->dq_dqb.dqb_curinodes)) {
+ scan_data->usage_is_inconsistent = 1;
+ fprintf(stderr, "[QUOTA WARNING] Usage inconsistent for ID %u:"
+ "actual (%lld, %lld) != expected (%lld, %lld)\n",
+ dq->dq_id, (long long) dq->dq_dqb.dqb_curspace,
+ (long long) dq->dq_dqb.dqb_curinodes,
+ (long long) dquot->dq_dqb.dqb_curspace,
+ (long long) dquot->dq_dqb.dqb_curinodes);
+ }
+
+ if (scan_data->update_limits) {
+ dq->dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
+ dq->dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
+ dq->dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
+ dq->dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
+ }
+
+ if (scan_data->update_usage) {
+ dq->dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
+ dq->dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
+ }
+
+ return 0;
+}
+
+/*
+ * Read quotas from disk and updates the in-memory information determined by
+ * 'flags' from the on-disk data.
+ */
+errcode_t quota_read_all_dquots(quota_ctx_t qctx, ext2_ino_t qf_ino,
+ enum quota_type qtype, unsigned int flags)
+{
+ struct scan_dquots_data scan_data;
+ struct quota_handle *qh;
+ errcode_t err;
+
+ if (!qctx)
+ return 0;
+
+ err = ext2fs_get_mem(sizeof(struct quota_handle), &qh);
+ if (err) {
+ log_debug("Unable to allocate quota handle");
+ return err;
+ }
+
+ err = quota_file_open(qctx, qh, qf_ino, qtype, -1, 0);
+ if (err) {
+ log_debug("Open quota file failed");
+ goto out;
+ }
+
+ scan_data.quota_dict = qctx->quota_dict[qh->qh_type];
+ scan_data.check_consistency = 0;
+ scan_data.update_limits = !!(flags & QREAD_LIMITS);
+ scan_data.update_usage = !!(flags & QREAD_USAGE);
+ qh->qh_ops->scan_dquots(qh, scan_dquots_callback, &scan_data);
+
+ err = quota_file_close(qctx, qh);
+ if (err) {
+ log_debug("Cannot finish IO on new quotafile: %s",
+ strerror(errno));
+ if (qh->qh_qf.e2_file)
+ ext2fs_file_close(qh->qh_qf.e2_file);
+ }
+out:
+ ext2fs_free_mem(&qh);
+ return err;
+}
+
+/*
+ * Compares the measured quota in qctx->quota_dict with that in the quota inode
+ * on disk and updates the limits in qctx->quota_dict. 'usage_inconsistent' is
+ * set to 1 if the supplied and on-disk quota usage values are not identical.
+ */
+errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
+ int *usage_inconsistent)
+{
+ struct quota_handle qh;
+ struct scan_dquots_data scan_data;
+ struct dquot *dq;
+ dnode_t *n;
+ dict_t *dict = qctx->quota_dict[qtype];
+ errcode_t err = 0;
+
+ if (!dict)
+ goto out;
+
+ err = quota_file_open(qctx, &qh, 0, qtype, -1, 0);
+ if (err) {
+ log_debug("Open quota file failed");
+ goto out;
+ }
+
+ scan_data.quota_dict = qctx->quota_dict[qtype];
+ scan_data.update_limits = 1;
+ scan_data.update_usage = 0;
+ scan_data.check_consistency = 1;
+ scan_data.usage_is_inconsistent = 0;
+ err = qh.qh_ops->scan_dquots(&qh, scan_dquots_callback, &scan_data);
+ if (err) {
+ log_debug("Error scanning dquots");
+ *usage_inconsistent = 1;
+ goto out_close_qh;
+ }
+
+ for (n = dict_first(dict); n; n = dict_next(dict, n)) {
+ dq = dnode_get(n);
+ if (!dq)
+ continue;
+ if ((dq->dq_flags & DQF_SEEN) == 0) {
+ fprintf(stderr, "[QUOTA WARNING] "
+ "Missing quota entry ID %d\n", dq->dq_id);
+ scan_data.usage_is_inconsistent = 1;
+ }
+ }
+ *usage_inconsistent = scan_data.usage_is_inconsistent;
+
+out_close_qh:
+ err = quota_file_close(qctx, &qh);
+ if (err) {
+ log_debug("Cannot close quotafile: %s", error_message(errno));
+ if (qh.qh_qf.e2_file)
+ ext2fs_file_close(qh.qh_qf.e2_file);
+ }
+out:
+ return err;
+}
+
+int parse_quota_opts(const char *opts, int (*func)(char *))
+{
+ char *buf, *token, *next, *p;
+ int len;
+ int ret = 0;
+
+ len = strlen(opts);
+ buf = malloc(len + 1);
+ if (!buf) {
+ fprintf(stderr,
+ "Couldn't allocate memory to parse quota options!\n");
+ return -ENOMEM;
+ }
+ strcpy(buf, opts);
+ for (token = buf; token && *token; token = next) {
+ p = strchr(token, ',');
+ next = 0;
+ if (p) {
+ *p = 0;
+ next = p + 1;
+ }
+ ret = func(token);
+ if (ret)
+ break;
+ }
+ free(buf);
+ return ret;
+}
diff --git a/lib/support/nls-enable.h b/lib/support/nls-enable.h
new file mode 100644
index 0000000..2f62c01
--- /dev/null
+++ b/lib/support/nls-enable.h
@@ -0,0 +1,21 @@
+#if defined(ENABLE_NLS) && !defined(DEBUGFS)
+#include <libintl.h>
+#include <locale.h>
+#define _(a) (gettext (a))
+#ifdef gettext_noop
+#define N_(a) gettext_noop (a)
+#else
+#define N_(a) (a)
+#endif
+#define P_(singular, plural, n) (ngettext (singular, plural, n))
+#ifndef NLS_CAT_NAME
+#define NLS_CAT_NAME "e2fsprogs"
+#endif
+#ifndef LOCALEDIR
+#define LOCALEDIR "/usr/share/locale"
+#endif
+#else
+#define _(a) (a)
+#define N_(a) a
+#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
+#endif
diff --git a/lib/support/parse_qtype.c b/lib/support/parse_qtype.c
new file mode 100644
index 0000000..d8df07d
--- /dev/null
+++ b/lib/support/parse_qtype.c
@@ -0,0 +1,90 @@
+/*
+ * parse_qtype.c
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "quotaio.h"
+
+#define PARSE_DELIM ":,"
+
+int parse_quota_types(const char *in_str, unsigned int *qtype_bits,
+ char **err_token)
+{
+ char *buf, *token, *next, *tmp;
+ unsigned int qtype = *qtype_bits;
+ int len, ret = 0;
+
+ if (!in_str)
+ return 0;
+
+ len = strlen(in_str);
+ buf = malloc(len + 1);
+ if (!buf)
+ return ENOMEM;
+ strcpy(buf, in_str);
+
+ for (token = buf, next = strtok_r(buf, PARSE_DELIM, &tmp);
+ token && *token; token = next) {
+ int not = 0;
+ char *p = token;
+
+ if (*p == '^') {
+ not = 1;
+ p++;
+ }
+ if (!strcmp(p, "usr") || !strcmp(p, "usrquota")) {
+ if (not)
+ qtype &= ~QUOTA_USR_BIT;
+ else
+ qtype |= QUOTA_USR_BIT;
+ } else if (!strcmp(p, "grp") || !strcmp(p, "grpquota")) {
+ if (not)
+ qtype &= ~QUOTA_GRP_BIT;
+ else
+ qtype |= QUOTA_GRP_BIT;
+ } else if (!strcmp(p, "prj") || !strcmp(p, "prjquota")) {
+ if (not)
+ qtype &= ~QUOTA_PRJ_BIT;
+ else
+ qtype |= QUOTA_PRJ_BIT;
+ } else {
+ if (err_token) {
+ *err_token = malloc(strlen(token) + 1);
+ if (*err_token)
+ strcpy(*err_token, token);
+ }
+ ret = EINVAL;
+ goto errout;
+ }
+#ifdef DEBUG_PROGRAM
+ printf("word: %s\n", token);
+#endif
+ next = strtok_r(NULL, PARSE_DELIM, &tmp);
+ }
+ *qtype_bits = qtype;
+errout:
+ free(buf);
+ return ret;
+}
+
+#ifdef DEBUG_PROGRAM
+int main(int argc, char **argv)
+{
+ unsigned int qtype_bits = 0;
+ int ret;
+ char *err_token = 0;
+
+ ret = parse_quota_types(argv[1], &qtype_bits, &err_token);
+ printf("parse_quota_types returns %d, %d\n", ret, qtype_bits);
+ if (err_token)
+ printf("err_token is %s\n", err_token);
+ return 0;
+}
+#endif
diff --git a/lib/support/plausible.c b/lib/support/plausible.c
new file mode 100644
index 0000000..65a6b2e
--- /dev/null
+++ b/lib/support/plausible.c
@@ -0,0 +1,287 @@
+/*
+ * plausible.c --- Figure out if a pathname is ext* or something else.
+ *
+ * Copyright 2014, Oracle, Inc.
+ *
+ * Some parts are:
+ * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#include "config.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_MAGIC_H
+#include <magic.h>
+#endif
+#include "plausible.h"
+#include "ext2fs/ext2fs.h"
+#include "nls-enable.h"
+#include "blkid/blkid.h"
+
+#ifdef HAVE_MAGIC_H
+static magic_t (*dl_magic_open)(int);
+static const char *(*dl_magic_file)(magic_t, const char *);
+static int (*dl_magic_load)(magic_t, const char *);
+static void (*dl_magic_close)(magic_t);
+
+/*
+ * NO_CHECK functionality was only added in file 4.20.
+ * Older systems like RHEL 5.x still have file 4.17
+ */
+#ifndef MAGIC_NO_CHECK_COMPRESS
+#define MAGIC_NO_CHECK_COMPRESS 0x0001000
+#endif
+#ifndef MAGIC_NO_CHECK_ELF
+#define MAGIC_NO_CHECK_ELF 0x0010000
+#endif
+
+#ifdef HAVE_DLOPEN
+#include <dlfcn.h>
+
+static void *magic_handle;
+
+static int magic_library_available(void)
+{
+ if (!magic_handle) {
+ magic_handle = dlopen("libmagic.so.1", RTLD_NOW);
+ if (!magic_handle)
+ return 0;
+
+ dl_magic_open = (magic_t (*)(int))
+ dlsym(magic_handle, "magic_open");
+ dl_magic_file = (const char *(*)(magic_t, const char *))
+ dlsym(magic_handle, "magic_file");
+ dl_magic_load = (int (*)(magic_t, const char *))
+ dlsym(magic_handle, "magic_load");
+ dl_magic_close = (void (*)(magic_t))
+ dlsym(magic_handle, "magic_close");
+ }
+
+ if (!dl_magic_open || !dl_magic_file ||
+ !dl_magic_load || !dl_magic_close)
+ return 0;
+ return 1;
+}
+#else
+static int magic_library_available(void)
+{
+ dl_magic_open = magic_open;
+ dl_magic_file = magic_file;
+ dl_magic_load = magic_load;
+ dl_magic_close = magic_close;
+
+ return 1;
+}
+#endif
+#endif
+
+static void print_ext2_info(const char *device)
+
+{
+ struct ext2_super_block *sb;
+ ext2_filsys fs;
+ errcode_t retval;
+ time_t tm;
+
+ retval = ext2fs_open2(device, 0, EXT2_FLAG_64BITS, 0, 0,
+ default_io_manager, &fs);
+ if (retval)
+ return;
+ sb = fs->super;
+
+ if (sb->s_mtime) {
+ tm = sb->s_mtime;
+ if (sb->s_last_mounted[0])
+ printf(_("\tlast mounted on %.*s on %s"),
+ EXT2_LEN_STR(sb->s_last_mounted), ctime(&tm));
+ else
+ printf(_("\tlast mounted on %s"), ctime(&tm));
+ } else if (sb->s_mkfs_time) {
+ tm = sb->s_mkfs_time;
+ printf(_("\tcreated on %s"), ctime(&tm));
+ } else if (sb->s_wtime) {
+ tm = sb->s_wtime;
+ printf(_("\tlast modified on %s"), ctime(&tm));
+ }
+ ext2fs_close_free(&fs);
+}
+
+/*
+ * return 1 if there is no partition table, 0 if a partition table is
+ * detected, and -1 on an error.
+ */
+#ifdef HAVE_BLKID_PROBE_ENABLE_PARTITIONS
+static int check_partition_table(const char *device)
+{
+ blkid_probe pr;
+ const char *value;
+ int ret;
+
+ pr = blkid_new_probe_from_filename(device);
+ if (!pr)
+ return -1;
+
+ ret = blkid_probe_enable_partitions(pr, 1);
+ if (ret < 0)
+ goto errout;
+
+ ret = blkid_probe_enable_superblocks(pr, 0);
+ if (ret < 0)
+ goto errout;
+
+ ret = blkid_do_fullprobe(pr);
+ if (ret < 0)
+ goto errout;
+
+ ret = blkid_probe_lookup_value(pr, "PTTYPE", &value, NULL);
+ if (ret == 0)
+ fprintf(stderr, _("Found a %s partition table in %s\n"),
+ value, device);
+ else
+ ret = 1;
+
+errout:
+ blkid_free_probe(pr);
+ return ret;
+}
+#else
+static int check_partition_table(const char *device EXT2FS_ATTR((unused)))
+{
+ return -1;
+}
+#endif
+
+/*
+ * return 1 if the device looks plausible, creating the file if necessary
+ */
+int check_plausibility(const char *device, int flags, int *ret_is_dev)
+{
+ int fd, ret, is_dev = 0;
+ ext2fs_struct_stat s;
+ int fl = O_RDONLY;
+ blkid_cache cache = NULL;
+ char *fs_type = NULL;
+ char *fs_label = NULL;
+
+ fd = ext2fs_open_file(device, fl, 0666);
+ if ((fd < 0) && (errno == ENOENT) && (flags & NO_SIZE)) {
+ fprintf(stderr, _("The file %s does not exist and no "
+ "size was specified.\n"), device);
+ exit(1);
+ }
+ if ((fd < 0) && (errno == ENOENT) && (flags & CREATE_FILE)) {
+ fl |= O_CREAT;
+ fd = ext2fs_open_file(device, fl, 0666);
+ if (fd >= 0 && (flags & VERBOSE_CREATE))
+ printf(_("Creating regular file %s\n"), device);
+ }
+ if (fd < 0) {
+ fprintf(stderr, _("Could not open %s: %s\n"),
+ device, error_message(errno));
+ if (errno == ENOENT)
+ fputs(_("\nThe device apparently does not exist; "
+ "did you specify it correctly?\n"), stderr);
+ exit(1);
+ }
+
+ if (ext2fs_fstat(fd, &s) < 0) {
+ perror("stat");
+ exit(1);
+ }
+ close(fd);
+
+ if (S_ISBLK(s.st_mode))
+ is_dev = 1;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /* On FreeBSD, all disk devices are character specials */
+ if (S_ISCHR(s.st_mode))
+ is_dev = 1;
+#endif
+ if (ret_is_dev)
+ *ret_is_dev = is_dev;
+
+ if ((flags & CHECK_BLOCK_DEV) && !is_dev) {
+ printf(_("%s is not a block special device.\n"), device);
+ return 0;
+ }
+
+ /*
+ * Note: we use the older-style blkid API's here because we
+ * want as much functionality to be available when using the
+ * internal blkid library, when e2fsprogs is compiled for
+ * non-Linux systems that will probably not have the libraries
+ * from util-linux available. We only use the newer
+ * blkid-probe interfaces to access functionality not
+ * available in the original blkid library.
+ */
+ if ((flags & CHECK_FS_EXIST) && blkid_get_cache(&cache, NULL) >= 0) {
+ fs_type = blkid_get_tag_value(cache, "TYPE", device);
+ if (fs_type)
+ fs_label = blkid_get_tag_value(cache, "LABEL", device);
+ blkid_put_cache(cache);
+ }
+
+ if (fs_type) {
+ if (fs_label)
+ printf(_("%s contains a %s file system labelled '%s'\n"),
+ device, fs_type, fs_label);
+ else
+ printf(_("%s contains a %s file system\n"), device,
+ fs_type);
+ if (strncmp(fs_type, "ext", 3) == 0)
+ print_ext2_info(device);
+ free(fs_type);
+ free(fs_label);
+ return 0;
+ }
+
+#ifdef HAVE_MAGIC_H
+ if ((flags & CHECK_FS_EXIST) &&
+ !getenv("E2FSPROGS_LIBMAGIC_SUPPRESS") &&
+ magic_library_available()) {
+ const char *msg;
+ magic_t mag;
+ int has_magic = 0;
+
+ mag = dl_magic_open(MAGIC_RAW | MAGIC_SYMLINK | MAGIC_DEVICES |
+ MAGIC_ERROR | MAGIC_NO_CHECK_ELF |
+ MAGIC_NO_CHECK_COMPRESS);
+ dl_magic_load(mag, NULL);
+
+ msg = dl_magic_file(mag, device);
+ if (msg && strcmp(msg, "data") && strcmp(msg, "empty")) {
+ printf(_("%s contains `%s' data\n"), device, msg);
+ has_magic = 1;
+ }
+
+ dl_magic_close(mag);
+ return !has_magic;
+ }
+#endif
+ if (flags & CHECK_FS_EXIST) {
+ ret = check_partition_table(device);
+ if (ret >= 0)
+ return ret;
+ }
+ return 1;
+}
+
diff --git a/lib/support/plausible.h b/lib/support/plausible.h
new file mode 100644
index 0000000..b85150c
--- /dev/null
+++ b/lib/support/plausible.h
@@ -0,0 +1,29 @@
+/*
+ * plausible.h --- header file defining prototypes for helper functions
+ * used by tune2fs and mke2fs
+ *
+ * Copyright 2014 by Oracle, Inc.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef PLAUSIBLE_H_
+#define PLAUSIBLE_H_
+
+/*
+ * Flags for check_plausibility()
+ */
+#define CHECK_BLOCK_DEV 0x0001
+#define CREATE_FILE 0x0002
+#define CHECK_FS_EXIST 0x0004
+#define VERBOSE_CREATE 0x0008
+#define NO_SIZE 0x0010
+#define QUIET_CHECK 0x0020
+
+extern int check_plausibility(const char *device, int flags,
+ int *ret_is_dev);
+
+#endif /* PLAUSIBLE_H_ */
diff --git a/lib/support/print_fs_flags.c b/lib/support/print_fs_flags.c
new file mode 100644
index 0000000..f47cd66
--- /dev/null
+++ b/lib/support/print_fs_flags.c
@@ -0,0 +1,75 @@
+/*
+ * print_flags.c - Print ext2_filsys flags
+ *
+ * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
+ * 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%
+ */
+
+#include "config.h"
+#include <stdio.h>
+
+#include "ext2fs/ext2fs.h"
+
+struct flags_name {
+ unsigned long flag;
+ const char *name;
+};
+
+static const struct flags_name flags_array[] = {
+ { EXT2_FLAG_RW, "EXT2_FLAG_RW" },
+ { EXT2_FLAG_CHANGED, "EXT2_FLAG_CHANGED" },
+ { EXT2_FLAG_DIRTY, "EXT2_FLAG_DIRTY" },
+ { EXT2_FLAG_VALID, "EXT2_FLAG_VALID" },
+ { EXT2_FLAG_IB_DIRTY, "EXT2_FLAG_IB_DIRTY" },
+ { EXT2_FLAG_BB_DIRTY, "EXT2_FLAG_BB_DIRTY" },
+ { EXT2_FLAG_SWAP_BYTES, "EXT2_FLAG_SWAP_BYTES" },
+ { EXT2_FLAG_SWAP_BYTES_READ, "EXT2_FLAG_SWAP_BYTES_READ" },
+ { EXT2_FLAG_SWAP_BYTES_WRITE, "EXT2_FLAG_SWAP_BYTES_WRITE" },
+ { EXT2_FLAG_MASTER_SB_ONLY, "EXT2_FLAG_MASTER_SB_ONLY" },
+ { EXT2_FLAG_FORCE, "EXT2_FLAG_FORCE" },
+ { EXT2_FLAG_SUPER_ONLY, "EXT2_FLAG_SUPER_ONLY" },
+ { EXT2_FLAG_JOURNAL_DEV_OK, "EXT2_FLAG_JOURNAL_DEV_OK" },
+ { EXT2_FLAG_IMAGE_FILE, "EXT2_FLAG_IMAGE_FILE" },
+ { EXT2_FLAG_EXCLUSIVE, "EXT2_FLAG_EXCLUSIVE" },
+ { EXT2_FLAG_SOFTSUPP_FEATURES, "EXT2_FLAG_SOFTSUPP_FEATURES" },
+ { EXT2_FLAG_NOFREE_ON_ERROR, "EXT2_FLAG_NOFREE_ON_ERROR" },
+ { EXT2_FLAG_64BITS, "EXT2_FLAG_64BITS" },
+ { EXT2_FLAG_PRINT_PROGRESS, "EXT2_FLAG_PRINT_PROGRESS" },
+ { EXT2_FLAG_DIRECT_IO, "EXT2_FLAG_DIRECT_IO" },
+ { EXT2_FLAG_SKIP_MMP, "EXT2_FLAG_SKIP_MMP" },
+ { EXT2_FLAG_IGNORE_CSUM_ERRORS, "EXT2_FLAG_IGNORE_CSUM_ERRORS" },
+ { EXT2_FLAG_SHARE_DUP, "EXT2_FLAG_SHARE_DUP" },
+ { EXT2_FLAG_IGNORE_SB_ERRORS, "EXT2_FLAG_IGNORE_SB_ERRORS" },
+ { EXT2_FLAG_BBITMAP_TAIL_PROBLEM, "EXT2_FLAG_BBITMAP_TAIL_PROBLEM" },
+ { EXT2_FLAG_IBITMAP_TAIL_PROBLEM, "EXT2_FLAG_IBITMAP_TAIL_PROBLEM" },
+ { EXT2_FLAG_THREADS, "EXT2_FLAG_THREADS" },
+ { 0, NULL },
+};
+
+void print_fs_flags(FILE * f, unsigned long flags)
+{
+ const struct flags_name *fp;
+ int first = 1, pos = 16;
+
+ for (fp = flags_array; fp->flag != 0; fp++) {
+ if ((flags & fp->flag) == 0)
+ continue;
+ pos += strlen(fp->name) + 1;
+ if (pos > 72) {
+ fputs("\n\t", f);
+ pos = 9 + strlen(fp->name);
+ }
+ if (first)
+ first = 0;
+ else
+ fputc(' ', f);
+ fputs(fp->name, f);
+ }
+ fputc('\n', f);
+}
diff --git a/lib/support/print_fs_flags.h b/lib/support/print_fs_flags.h
new file mode 100644
index 0000000..bdd58a0
--- /dev/null
+++ b/lib/support/print_fs_flags.h
@@ -0,0 +1,5 @@
+/*
+ * print_flags.h -- header file for printing the fs flags
+ */
+
+void print_fs_flags(FILE * f, unsigned long flags);
diff --git a/lib/support/prof_err.et b/lib/support/prof_err.et
new file mode 100644
index 0000000..c9316c7
--- /dev/null
+++ b/lib/support/prof_err.et
@@ -0,0 +1,66 @@
+error_table prof
+
+error_code PROF_VERSION, "Profile version 0.0"
+
+#
+# generated by prof_tree.c
+#
+error_code PROF_MAGIC_NODE, "Bad magic value in profile_node"
+error_code PROF_NO_SECTION, "Profile section not found"
+error_code PROF_NO_RELATION, "Profile relation not found"
+error_code PROF_ADD_NOT_SECTION,
+ "Attempt to add a relation to node which is not a section"
+error_code PROF_SECTION_WITH_VALUE,
+ "A profile section header has a non-zero value"
+error_code PROF_BAD_LINK_LIST, "Bad linked list in profile structures"
+error_code PROF_BAD_GROUP_LVL, "Bad group level in profile structures"
+error_code PROF_BAD_PARENT_PTR,
+ "Bad parent pointer in profile structures"
+error_code PROF_MAGIC_ITERATOR, "Bad magic value in profile iterator"
+error_code PROF_SET_SECTION_VALUE, "Can't set value on section node"
+error_code PROF_EINVAL, "Invalid argument passed to profile library"
+error_code PROF_READ_ONLY, "Attempt to modify read-only profile"
+
+#
+# generated by prof_parse.c
+#
+
+error_code PROF_SECTION_NOTOP, "Profile section header not at top level"
+error_code PROF_SECTION_SYNTAX, "Syntax error in profile section header"
+error_code PROF_RELATION_SYNTAX, "Syntax error in profile relation"
+error_code PROF_EXTRA_CBRACE, "Extra closing brace in profile"
+error_code PROF_MISSING_OBRACE, "Missing open brace in profile"
+
+#
+# generated by prof_init.c
+#
+error_code PROF_MAGIC_PROFILE, "Bad magic value in profile_t"
+error_code PROF_MAGIC_SECTION, "Bad magic value in profile_section_t"
+error_code PROF_TOPSECTION_ITER_NOSUPP,
+ "Iteration through all top level section not supported"
+error_code PROF_INVALID_SECTION, "Invalid profile_section object"
+error_code PROF_END_OF_SECTIONS, "No more sections"
+error_code PROF_BAD_NAMESET, "Bad nameset passed to query routine"
+error_code PROF_NO_PROFILE, "No profile file open"
+
+#
+# generated by prof_file.c
+#
+error_code PROF_MAGIC_FILE, "Bad magic value in profile_file_t"
+error_code PROF_FAIL_OPEN, "Couldn't open profile file"
+
+#
+# generated by prof_set.c
+#
+error_code PROF_EXISTS, "Section already exists"
+
+#
+# generated by prof_get.c
+#
+error_code PROF_BAD_BOOLEAN, "Invalid boolean value"
+error_code PROF_BAD_INTEGER, "Invalid integer value"
+
+error_code PROF_MAGIC_FILE_DATA, "Bad magic value in profile_file_data_t"
+
+
+end
diff --git a/lib/support/profile.c b/lib/support/profile.c
new file mode 100644
index 0000000..bdb14b1
--- /dev/null
+++ b/lib/support/profile.c
@@ -0,0 +1,1910 @@
+/*
+ * profile.c -- A simple configuration file parsing "library in a file"
+ *
+ * The profile library was originally written by Theodore Ts'o in 1995
+ * for use in the MIT Kerberos v5 library. It has been
+ * modified/enhanced/bug-fixed over time by other members of the MIT
+ * Kerberos team. This version was originally taken from the Kerberos
+ * v5 distribution, version 1.4.2, and radically simplified for use in
+ * e2fsprogs. (Support for locking for multi-threaded operations,
+ * being able to modify and update the configuration file
+ * programmatically, and Mac/Windows portability have been removed.
+ * It has been folded into a single C source file to make it easier to
+ * fold into an application program.)
+ *
+ * Copyright (C) 2005, 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, 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, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original MIT software.
+ * M.I.T. makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include <et/com_err.h>
+#include "profile.h"
+#include "prof_err.h"
+
+#undef STAT_ONCE_PER_SECOND
+#undef HAVE_STAT
+
+/*
+ * prof_int.h
+ */
+
+typedef long prf_magic_t;
+
+/*
+ * This is the structure which stores the profile information for a
+ * particular configuration file.
+ */
+struct _prf_file_t {
+ prf_magic_t magic;
+ char *filespec;
+#ifdef STAT_ONCE_PER_SECOND
+ time_t last_stat;
+#endif
+ time_t timestamp; /* time tree was last updated from file */
+ int flags; /* r/w, dirty */
+ int upd_serial; /* incremented when data changes */
+ struct profile_node *root;
+ struct _prf_file_t *next;
+};
+
+typedef struct _prf_file_t *prf_file_t;
+
+/*
+ * The profile flags
+ */
+#define PROFILE_FILE_RW 0x0001
+#define PROFILE_FILE_DIRTY 0x0002
+#define PROFILE_FILE_NO_RELOAD 0x0004
+
+/*
+ * This structure defines the high-level, user visible profile_t
+ * object, which is used as a handle by users who need to query some
+ * configuration file(s)
+ */
+struct _profile_t {
+ prf_magic_t magic;
+ prf_file_t first_file;
+};
+
+/*
+ * Used by the profile iterator in prof_get.c
+ */
+#define PROFILE_ITER_LIST_SECTION 0x0001
+#define PROFILE_ITER_SECTIONS_ONLY 0x0002
+#define PROFILE_ITER_RELATIONS_ONLY 0x0004
+
+#define PROFILE_ITER_FINAL_SEEN 0x0100
+
+/*
+ * Check if a filespec is last in a list (NULL on UNIX, invalid FSSpec on MacOS
+ */
+
+#define PROFILE_LAST_FILESPEC(x) (((x) == NULL) || ((x)[0] == '\0'))
+
+struct profile_node {
+ errcode_t magic;
+ char *name;
+ char *value;
+ int group_level;
+ unsigned int final:1; /* Indicate don't search next file */
+ unsigned int deleted:1;
+ struct profile_node *first_child;
+ struct profile_node *parent;
+ struct profile_node *next, *prev;
+};
+
+#define CHECK_MAGIC(node) \
+ if ((node)->magic != PROF_MAGIC_NODE) \
+ return PROF_MAGIC_NODE;
+
+/* profile parser declarations */
+struct parse_state {
+ int state;
+ int group_level;
+ int line_num;
+ struct profile_node *root_section;
+ struct profile_node *current_section;
+};
+
+static const char *default_filename = "<default>";
+
+static profile_syntax_err_cb_t syntax_err_cb;
+
+static errcode_t parse_line(char *line, struct parse_state *state);
+
+#ifdef DEBUG_PROGRAM
+static errcode_t profile_write_tree_file
+ (struct profile_node *root, FILE *dstfile);
+
+static errcode_t profile_write_tree_to_buffer
+ (struct profile_node *root, char **buf);
+#endif
+
+
+static void profile_free_node
+ (struct profile_node *relation);
+
+static errcode_t profile_create_node
+ (const char *name, const char *value,
+ struct profile_node **ret_node);
+
+#ifdef DEBUG_PROGRAM
+static errcode_t profile_verify_node
+ (struct profile_node *node);
+#endif
+
+static errcode_t profile_add_node
+ (struct profile_node *section,
+ const char *name, const char *value,
+ struct profile_node **ret_node);
+
+static errcode_t profile_find_node
+ (struct profile_node *section,
+ const char *name, const char *value,
+ int section_flag, void **state,
+ struct profile_node **node);
+
+static errcode_t profile_node_iterator
+ (void **iter_p, struct profile_node **ret_node,
+ char **ret_name, char **ret_value);
+
+static errcode_t profile_open_file
+ (const char * file, prf_file_t *ret_prof);
+
+static errcode_t profile_update_file
+ (prf_file_t prf);
+
+static void profile_free_file
+ (prf_file_t profile);
+
+static errcode_t profile_get_value(profile_t profile, const char *name,
+ const char *subname, const char *subsubname,
+ const char **ret_value);
+
+
+/*
+ * prof_init.c --- routines that manipulate the user-visible profile_t
+ * object.
+ */
+
+static int compstr(const void *m1, const void *m2)
+{
+ const char *s1 = *((const char * const *) m1);
+ const char *s2 = *((const char * const *) m2);
+
+ return strcmp(s1, s2);
+}
+
+static void free_list(char **list)
+{
+ char **cp;
+
+ if (list == 0)
+ return;
+
+ for (cp = list; *cp; cp++)
+ free(*cp);
+ free(list);
+}
+
+static errcode_t get_dirlist(const char *dirname, char***ret_array)
+{
+ DIR *dir;
+ struct dirent *de;
+ struct stat st;
+ errcode_t retval;
+ char *fn, *cp;
+ char **array = 0, **new_array;
+ int max = 0, num = 0;
+
+ dir = opendir(dirname);
+ if (!dir)
+ return errno;
+
+ while ((de = readdir(dir)) != NULL) {
+ for (cp = de->d_name; *cp; cp++) {
+ if (!isalnum(*cp) &&
+ (*cp != '-') &&
+ (*cp != '_'))
+ break;
+ }
+ if (*cp)
+ continue;
+ fn = malloc(strlen(dirname) + strlen(de->d_name) + 2);
+ if (!fn) {
+ retval = ENOMEM;
+ goto errout;
+ }
+ sprintf(fn, "%s/%s", dirname, de->d_name);
+ if ((stat(fn, &st) < 0) || !S_ISREG(st.st_mode)) {
+ free(fn);
+ continue;
+ }
+ if (num >= max) {
+ max += 10;
+ new_array = realloc(array, sizeof(char *) * (max+1));
+ if (!new_array) {
+ retval = ENOMEM;
+ free(fn);
+ goto errout;
+ }
+ array = new_array;
+ }
+ array[num++] = fn;
+ }
+ if (array) {
+ qsort(array, num, sizeof(char *), compstr);
+ array[num++] = 0;
+ }
+ *ret_array = array;
+ closedir(dir);
+ return 0;
+errout:
+ if (array)
+ array[num] = 0;
+ closedir(dir);
+ free_list(array);
+ return retval;
+}
+
+errcode_t
+profile_init(const char * const *files, profile_t *ret_profile)
+{
+ const char * const *fs;
+ profile_t profile;
+ prf_file_t new_file, *last;
+ errcode_t retval = 0;
+ char **cpp, *cp, **array = 0;
+
+ profile = malloc(sizeof(struct _profile_t));
+ if (!profile)
+ return ENOMEM;
+ memset(profile, 0, sizeof(struct _profile_t));
+ profile->magic = PROF_MAGIC_PROFILE;
+ last = &profile->first_file;
+
+ /* if the filenames list is not specified return an empty profile */
+ if ( files ) {
+ for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
+ if (array)
+ free_list(array);
+ array = NULL;
+ retval = get_dirlist(*fs, &array);
+ if (retval == 0) {
+ if (!array)
+ continue;
+ for (cpp = array; (cp = *cpp); cpp++) {
+ retval = profile_open_file(cp, &new_file);
+ if (retval == EACCES)
+ continue;
+ if (retval)
+ goto errout;
+ *last = new_file;
+ last = &new_file->next;
+ }
+ } else if ((retval != ENOTDIR) &&
+ strcmp(*fs, default_filename))
+ goto errout;
+
+ retval = profile_open_file(*fs, &new_file);
+ /* if this file is missing, skip to the next */
+ if (retval == ENOENT || retval == EACCES) {
+ continue;
+ }
+ if (retval)
+ goto errout;
+ *last = new_file;
+ last = &new_file->next;
+ }
+ /*
+ * If all the files were not found, return the appropriate error.
+ */
+ if (!profile->first_file) {
+ retval = ENOENT;
+ goto errout;
+ }
+ }
+
+ free_list(array);
+ *ret_profile = profile;
+ return 0;
+errout:
+ free_list(array);
+ profile_release(profile);
+ return retval;
+}
+
+void
+profile_release(profile_t profile)
+{
+ prf_file_t p, next;
+
+ if (!profile || profile->magic != PROF_MAGIC_PROFILE)
+ return;
+
+ for (p = profile->first_file; p; p = next) {
+ next = p->next;
+ profile_free_file(p);
+ }
+ profile->magic = 0;
+ free(profile);
+}
+
+/*
+ * This function sets the value of the pseudo file "<default>". If
+ * the file "<default>" had previously been passed to profile_init(),
+ * then def_string parameter will be parsed and used as the profile
+ * information for the "<default>" file.
+ */
+errcode_t profile_set_default(profile_t profile, const char *def_string)
+{
+ struct parse_state state;
+ prf_file_t prf;
+ errcode_t retval;
+ const char *in;
+ char *line, *p, *end;
+ int line_size, len;
+
+ if (!def_string || !profile || profile->magic != PROF_MAGIC_PROFILE)
+ return PROF_MAGIC_PROFILE;
+
+ for (prf = profile->first_file; prf; prf = prf->next) {
+ if (strcmp(prf->filespec, default_filename) == 0)
+ break;
+ }
+ if (!prf)
+ return 0;
+
+ if (prf->root) {
+ profile_free_node(prf->root);
+ prf->root = 0;
+ }
+
+ memset(&state, 0, sizeof(struct parse_state));
+ retval = profile_create_node("(root)", 0, &state.root_section);
+ if (retval)
+ return retval;
+
+ line = 0;
+ line_size = 0;
+ in = def_string;
+ while (*in) {
+ end = strchr(in, '\n');
+ len = end ? (end - in) : (int) strlen(in);
+ if (len >= line_size) {
+ line_size = len+1;
+ p = realloc(line, line_size);
+ if (!p) {
+ retval = ENOMEM;
+ goto errout;
+ }
+ line = p;
+ }
+ memcpy(line, in, len);
+ line[len] = 0;
+ retval = parse_line(line, &state);
+ if (retval) {
+ errout:
+ if (syntax_err_cb)
+ (syntax_err_cb)(prf->filespec, retval,
+ state.line_num);
+ free(line);
+ if (prf->root)
+ profile_free_node(prf->root);
+ return retval;
+ }
+ if (!end)
+ break;
+ in = end+1;
+ }
+ prf->root = state.root_section;
+ free(line);
+
+ return 0;
+}
+
+/*
+ * prof_file.c ---- routines that manipulate an individual profile file.
+ */
+
+errcode_t profile_open_file(const char * filespec,
+ prf_file_t *ret_prof)
+{
+ prf_file_t prf;
+ errcode_t retval;
+ char *home_env = 0;
+ unsigned int len;
+ char *expanded_filename;
+
+ prf = malloc(sizeof(struct _prf_file_t));
+ if (!prf)
+ return ENOMEM;
+ memset(prf, 0, sizeof(struct _prf_file_t));
+ prf->magic = PROF_MAGIC_FILE;
+
+ len = strlen(filespec)+1;
+ if (filespec[0] == '~' && filespec[1] == '/') {
+ home_env = getenv("HOME");
+#ifdef HAVE_PWD_H
+ if (home_env == NULL) {
+#ifdef HAVE_GETWUID_R
+ struct passwd *pw, pwx;
+ uid_t uid;
+ char pwbuf[BUFSIZ];
+
+ uid = getuid();
+ if (!getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
+ && pw != NULL && pw->pw_dir[0] != 0)
+ home_env = pw->pw_dir;
+#else
+ struct passwd *pw;
+
+ pw = getpwuid(getuid());
+ home_env = pw->pw_dir;
+#endif
+ }
+#endif
+ if (home_env)
+ len += strlen(home_env);
+ }
+ expanded_filename = malloc(len);
+ if (expanded_filename == 0) {
+ profile_free_file(prf);
+ return errno;
+ }
+ if (home_env) {
+ strcpy(expanded_filename, home_env);
+ strcat(expanded_filename, filespec+1);
+ } else
+ memcpy(expanded_filename, filespec, len);
+
+ prf->filespec = expanded_filename;
+
+ if (strcmp(prf->filespec, default_filename) != 0) {
+ retval = profile_update_file(prf);
+ if (retval) {
+ profile_free_file(prf);
+ return retval;
+ }
+ }
+
+ *ret_prof = prf;
+ return 0;
+}
+
+errcode_t profile_update_file(prf_file_t prf)
+{
+ errcode_t retval;
+#ifdef HAVE_STAT
+ struct stat st;
+#ifdef STAT_ONCE_PER_SECOND
+ time_t now;
+#endif
+#endif
+ FILE *f;
+ char buf[2048];
+ struct parse_state state;
+
+ if (prf->flags & PROFILE_FILE_NO_RELOAD)
+ return 0;
+
+#ifdef HAVE_STAT
+#ifdef STAT_ONCE_PER_SECOND
+ now = time(0);
+ if (now == prf->last_stat && prf->root != NULL) {
+ return 0;
+ }
+#endif
+ if (stat(prf->filespec, &st)) {
+ retval = errno;
+ return retval;
+ }
+#ifdef STAT_ONCE_PER_SECOND
+ prf->last_stat = now;
+#endif
+ if (st.st_mtime == prf->timestamp && prf->root != NULL) {
+ return 0;
+ }
+ if (prf->root) {
+ profile_free_node(prf->root);
+ prf->root = 0;
+ }
+#else
+ /*
+ * If we don't have the stat() call, assume that our in-core
+ * memory image is correct. That is, we won't reread the
+ * profile file if it changes.
+ */
+ if (prf->root) {
+ return 0;
+ }
+#endif
+ memset(&state, 0, sizeof(struct parse_state));
+ retval = profile_create_node("(root)", 0, &state.root_section);
+ if (retval)
+ return retval;
+ errno = 0;
+ f = fopen(prf->filespec, "r");
+ if (f == NULL) {
+ retval = errno;
+ if (retval == 0)
+ retval = ENOENT;
+ return retval;
+ }
+ prf->upd_serial++;
+ while (!feof(f)) {
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ break;
+ retval = parse_line(buf, &state);
+ if (retval) {
+ if (syntax_err_cb)
+ (syntax_err_cb)(prf->filespec, retval,
+ state.line_num);
+ fclose(f);
+ return retval;
+ }
+ }
+ prf->root = state.root_section;
+
+ fclose(f);
+
+#ifdef HAVE_STAT
+ prf->timestamp = st.st_mtime;
+#endif
+ return 0;
+}
+
+void profile_free_file(prf_file_t prf)
+{
+ if (prf->root)
+ profile_free_node(prf->root);
+ free(prf->filespec);
+ free(prf);
+}
+
+/* Begin the profile parser */
+
+profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook)
+{
+ profile_syntax_err_cb_t old;
+
+ old = syntax_err_cb;
+ syntax_err_cb = hook;
+ return(old);
+}
+
+#define STATE_INIT_COMMENT 0
+#define STATE_STD_LINE 1
+#define STATE_GET_OBRACE 2
+
+static char *skip_over_blanks(char *cp)
+{
+ while (*cp && isspace((int) (*cp)))
+ cp++;
+ return cp;
+}
+
+static int end_or_comment(char ch)
+{
+ return (ch == 0 || ch == '#' || ch == ';');
+}
+
+static char *skip_over_nonblanks(char *cp)
+{
+ while (!end_or_comment(*cp) && !isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static void strip_line(char *line)
+{
+ char *p = line + strlen(line);
+ while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
+ *p-- = 0;
+}
+
+static void parse_quoted_string(char *str)
+{
+ char *to, *from;
+
+ to = from = str;
+
+ for (to = from = str; *from && *from != '"'; to++, from++) {
+ if (*from == '\\') {
+ from++;
+ switch (*from) {
+ case 'n':
+ *to = '\n';
+ break;
+ case 't':
+ *to = '\t';
+ break;
+ case 'b':
+ *to = '\b';
+ break;
+ default:
+ *to = *from;
+ }
+ continue;
+ }
+ *to = *from;
+ }
+ *to = '\0';
+}
+
+static errcode_t parse_line(char *line, struct parse_state *state)
+{
+ char *cp, ch, *tag, *value;
+ char *p;
+ errcode_t retval;
+ struct profile_node *node;
+ int do_subsection = 0;
+ void *iter = 0;
+
+ state->line_num++;
+ if (state->state == STATE_GET_OBRACE) {
+ cp = skip_over_blanks(line);
+ if (*cp != '{')
+ return PROF_MISSING_OBRACE;
+ state->state = STATE_STD_LINE;
+ return 0;
+ }
+ if (state->state == STATE_INIT_COMMENT) {
+ if (line[0] != '[')
+ return 0;
+ state->state = STATE_STD_LINE;
+ }
+
+ if (*line == 0)
+ return 0;
+ strip_line(line);
+ cp = skip_over_blanks(line);
+ ch = *cp;
+ if (end_or_comment(ch))
+ return 0;
+ if (ch == '[') {
+ if (state->group_level > 0)
+ return PROF_SECTION_NOTOP;
+ cp++;
+ cp = skip_over_blanks(cp);
+ p = strchr(cp, ']');
+ if (p == NULL)
+ return PROF_SECTION_SYNTAX;
+ if (*cp == '"') {
+ cp++;
+ parse_quoted_string(cp);
+ } else {
+ *p-- = '\0';
+ while (isspace(*p) && (p > cp))
+ *p-- = '\0';
+ if (*cp == 0)
+ return PROF_SECTION_SYNTAX;
+ }
+ retval = profile_find_node(state->root_section, cp, 0, 1,
+ &iter, &state->current_section);
+ if (retval == PROF_NO_SECTION) {
+ retval = profile_add_node(state->root_section,
+ cp, 0,
+ &state->current_section);
+ if (retval)
+ return retval;
+ } else if (retval)
+ return retval;
+
+ /*
+ * Finish off the rest of the line.
+ */
+ cp = p+1;
+ if (*cp == '*') {
+ state->current_section->final = 1;
+ cp++;
+ }
+ /*
+ * Spaces or comments after ']' should not be fatal
+ */
+ cp = skip_over_blanks(cp);
+ if (!end_or_comment(*cp))
+ return PROF_SECTION_SYNTAX;
+ return 0;
+ }
+ if (ch == '}') {
+ if (state->group_level == 0)
+ return PROF_EXTRA_CBRACE;
+ if (*(cp+1) == '*')
+ state->current_section->final = 1;
+ state->current_section = state->current_section->parent;
+ state->group_level--;
+ return 0;
+ }
+ /*
+ * Parse the relations
+ */
+ tag = cp;
+ cp = strchr(cp, '=');
+ if (!cp)
+ return PROF_RELATION_SYNTAX;
+ if (cp == tag)
+ return PROF_RELATION_SYNTAX;
+ *cp = '\0';
+ if (*tag == '"') {
+ tag++;
+ parse_quoted_string(tag);
+ } else {
+ /* Look for whitespace on left-hand side. */
+ p = skip_over_nonblanks(tag);
+ if (*p)
+ *p++ = 0;
+ p = skip_over_blanks(p);
+ /* If we have more non-whitespace, it's an error. */
+ if (*p)
+ return PROF_RELATION_SYNTAX;
+ }
+
+ cp = skip_over_blanks(cp+1);
+ value = cp;
+ ch = value[0];
+ if (ch == '"') {
+ value++;
+ parse_quoted_string(value);
+ } else if (end_or_comment(ch)) {
+ do_subsection++;
+ state->state = STATE_GET_OBRACE;
+ } else if (value[0] == '{') {
+ cp = skip_over_blanks(value+1);
+ ch = *cp;
+ if (end_or_comment(ch))
+ do_subsection++;
+ else
+ return PROF_RELATION_SYNTAX;
+ } else {
+ cp = skip_over_nonblanks(value);
+ p = skip_over_blanks(cp);
+ ch = *p;
+ *cp = 0;
+ if (!end_or_comment(ch))
+ return PROF_RELATION_SYNTAX;
+ }
+ if (do_subsection) {
+ p = strchr(tag, '*');
+ if (p)
+ *p = '\0';
+ retval = profile_add_node(state->current_section,
+ tag, 0, &state->current_section);
+ if (retval)
+ return retval;
+ if (p)
+ state->current_section->final = 1;
+ state->group_level++;
+ return 0;
+ }
+ p = strchr(tag, '*');
+ if (p)
+ *p = '\0';
+ profile_add_node(state->current_section, tag, value, &node);
+ if (p)
+ node->final = 1;
+ return 0;
+}
+
+#ifdef DEBUG_PROGRAM
+/*
+ * Return TRUE if the string begins or ends with whitespace
+ */
+static int need_double_quotes(char *str)
+{
+ if (!str || !*str)
+ return 0;
+ if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1))))
+ return 1;
+ if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b') ||
+ strchr(str, ' ') || strchr(str, '#') || strchr(str, ';'))
+ return 1;
+ return 0;
+}
+
+/*
+ * Output a string with double quotes, doing appropriate backquoting
+ * of characters as necessary.
+ */
+static void output_quoted_string(char *str, void (*cb)(const char *,void *),
+ void *data)
+{
+ char ch;
+ char buf[2];
+
+ cb("\"", data);
+ if (!str) {
+ cb("\"", data);
+ return;
+ }
+ buf[1] = 0;
+ while ((ch = *str++)) {
+ switch (ch) {
+ case '\\':
+ cb("\\\\", data);
+ break;
+ case '\n':
+ cb("\\n", data);
+ break;
+ case '\t':
+ cb("\\t", data);
+ break;
+ case '\b':
+ cb("\\b", data);
+ break;
+ default:
+ /* This would be a lot faster if we scanned
+ forward for the next "interesting"
+ character. */
+ buf[0] = ch;
+ cb(buf, data);
+ break;
+ }
+ }
+ cb("\"", data);
+}
+
+#ifndef EOL
+#define EOL "\n"
+#endif
+
+/* Errors should be returned, not ignored! */
+static void dump_profile(struct profile_node *root, int level,
+ void (*cb)(const char *, void *), void *data)
+{
+ int i;
+ struct profile_node *p;
+ void *iter;
+ long retval;
+
+ iter = 0;
+ do {
+ retval = profile_find_node(root, 0, 0, 0, &iter, &p);
+ if (retval)
+ break;
+ for (i=0; i < level; i++)
+ cb("\t", data);
+ if (need_double_quotes(p->name))
+ output_quoted_string(p->name, cb, data);
+ else
+ cb(p->name, data);
+ cb(" = ", data);
+ if (need_double_quotes(p->value))
+ output_quoted_string(p->value, cb, data);
+ else
+ cb(p->value, data);
+ cb(EOL, data);
+ } while (iter != 0);
+
+ iter = 0;
+ do {
+ retval = profile_find_node(root, 0, 0, 1, &iter, &p);
+ if (retval)
+ break;
+ if (level == 0) { /* [xxx] */
+ cb("[", data);
+ if (need_double_quotes(p->name))
+ output_quoted_string(p->name, cb, data);
+ else
+ cb(p->name, data);
+ cb("]", data);
+ cb(p->final ? "*" : "", data);
+ cb(EOL, data);
+ dump_profile(p, level+1, cb, data);
+ cb(EOL, data);
+ } else { /* xxx = { ... } */
+ for (i=0; i < level; i++)
+ cb("\t", data);
+ if (need_double_quotes(p->name))
+ output_quoted_string(p->name, cb, data);
+ else
+ cb(p->name, data);
+ cb(" = {", data);
+ cb(EOL, data);
+ dump_profile(p, level+1, cb, data);
+ for (i=0; i < level; i++)
+ cb("\t", data);
+ cb("}", data);
+ cb(p->final ? "*" : "", data);
+ cb(EOL, data);
+ }
+ } while (iter != 0);
+}
+
+static void dump_profile_to_file_cb(const char *str, void *data)
+{
+ fputs(str, data);
+}
+
+errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile)
+{
+ dump_profile(root, 0, dump_profile_to_file_cb, dstfile);
+ return 0;
+}
+
+struct prof_buf {
+ char *base;
+ size_t cur, max;
+ int err;
+};
+
+static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len)
+{
+ if (b->err)
+ return;
+ if (b->max - b->cur < len) {
+ size_t newsize;
+ char *newptr;
+
+ newsize = b->max + (b->max >> 1) + len + 1024;
+ newptr = realloc(b->base, newsize);
+ if (newptr == NULL) {
+ b->err = 1;
+ return;
+ }
+ b->base = newptr;
+ b->max = newsize;
+ }
+ memcpy(b->base + b->cur, d, len);
+ b->cur += len; /* ignore overflow */
+}
+
+static void dump_profile_to_buffer_cb(const char *str, void *data)
+{
+ add_data_to_buffer((struct prof_buf *)data, str, strlen(str));
+}
+
+errcode_t profile_write_tree_to_buffer(struct profile_node *root,
+ char **buf)
+{
+ struct prof_buf prof_buf = { 0, 0, 0, 0 };
+
+ dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf);
+ if (prof_buf.err) {
+ *buf = NULL;
+ return ENOMEM;
+ }
+ add_data_to_buffer(&prof_buf, "", 1); /* append nul */
+ if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) {
+ char *newptr = realloc(prof_buf.base, prof_buf.cur);
+ if (newptr)
+ prof_buf.base = newptr;
+ }
+ *buf = prof_buf.base;
+ return 0;
+}
+#endif
+
+/*
+ * prof_tree.c --- these routines maintain the parse tree of the
+ * config file.
+ *
+ * All of the details of how the tree is stored is abstracted away in
+ * this file; all of the other profile routines build, access, and
+ * modify the tree via the accessor functions found in this file.
+ *
+ * Each node may represent either a relation or a section header.
+ *
+ * A section header must have its value field set to 0, and may a one
+ * or more child nodes, pointed to by first_child.
+ *
+ * A relation has as its value a pointer to allocated memory
+ * containing a string. Its first_child pointer must be null.
+ *
+ */
+
+/*
+ * Free a node, and any children
+ */
+void profile_free_node(struct profile_node *node)
+{
+ struct profile_node *child, *next;
+
+ if (node->magic != PROF_MAGIC_NODE)
+ return;
+
+ free(node->name);
+ free(node->value);
+
+ for (child=node->first_child; child; child = next) {
+ next = child->next;
+ profile_free_node(child);
+ }
+ node->magic = 0;
+
+ free(node);
+}
+
+#ifndef HAVE_STRDUP
+#undef strdup
+#define strdup MYstrdup
+static char *MYstrdup (const char *s)
+{
+ size_t sz = strlen(s) + 1;
+ char *p = malloc(sz);
+ if (p != 0)
+ memcpy(p, s, sz);
+ return p;
+}
+#endif
+
+/*
+ * Create a node
+ */
+errcode_t profile_create_node(const char *name, const char *value,
+ struct profile_node **ret_node)
+{
+ struct profile_node *new;
+
+ new = malloc(sizeof(struct profile_node));
+ if (!new)
+ return ENOMEM;
+ memset(new, 0, sizeof(struct profile_node));
+ new->magic = PROF_MAGIC_NODE;
+ new->name = strdup(name);
+ if (new->name == 0) {
+ profile_free_node(new);
+ return ENOMEM;
+ }
+ if (value) {
+ new->value = strdup(value);
+ if (new->value == 0) {
+ profile_free_node(new);
+ return ENOMEM;
+ }
+ }
+
+ *ret_node = new;
+ return 0;
+}
+
+/*
+ * This function verifies that all of the representation invariants of
+ * the profile are true. If not, we have a programming bug somewhere,
+ * probably in this file.
+ */
+#ifdef DEBUG_PROGRAM
+errcode_t profile_verify_node(struct profile_node *node)
+{
+ struct profile_node *p, *last;
+ errcode_t retval;
+
+ CHECK_MAGIC(node);
+
+ if (node->value && node->first_child)
+ return PROF_SECTION_WITH_VALUE;
+
+ last = 0;
+ for (p = node->first_child; p; last = p, p = p->next) {
+ if (p->prev != last)
+ return PROF_BAD_LINK_LIST;
+ if (last && (last->next != p))
+ return PROF_BAD_LINK_LIST;
+ if (node->group_level+1 != p->group_level)
+ return PROF_BAD_GROUP_LVL;
+ if (p->parent != node)
+ return PROF_BAD_PARENT_PTR;
+ retval = profile_verify_node(p);
+ if (retval)
+ return retval;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * Add a node to a particular section
+ */
+errcode_t profile_add_node(struct profile_node *section, const char *name,
+ const char *value, struct profile_node **ret_node)
+{
+ errcode_t retval;
+ struct profile_node *p, *last, *new;
+
+ CHECK_MAGIC(section);
+
+ if (section->value)
+ return PROF_ADD_NOT_SECTION;
+
+ /*
+ * Find the place to insert the new node. We look for the
+ * place *after* the last match of the node name, since
+ * order matters.
+ */
+ for (p=section->first_child, last = 0; p; last = p, p = p->next) {
+ int cmp;
+ cmp = strcmp(p->name, name);
+ if (cmp > 0)
+ break;
+ }
+ retval = profile_create_node(name, value, &new);
+ if (retval)
+ return retval;
+ new->group_level = section->group_level+1;
+ new->deleted = 0;
+ new->parent = section;
+ new->prev = last;
+ new->next = p;
+ if (p)
+ p->prev = new;
+ if (last)
+ last->next = new;
+ else
+ section->first_child = new;
+ if (ret_node)
+ *ret_node = new;
+ return 0;
+}
+
+/*
+ * Iterate through the section, returning the nodes which match
+ * the given name. If name is NULL, then iterate through all the
+ * nodes in the section. If section_flag is non-zero, only return the
+ * section which matches the name; don't return relations. If value
+ * is non-NULL, then only return relations which match the requested
+ * value. (The value argument is ignored if section_flag is non-zero.)
+ *
+ * The first time this routine is called, the state pointer must be
+ * null. When this profile_find_node_relation() returns, if the state
+ * pointer is non-NULL, then this routine should be called again.
+ * (This won't happen if section_flag is non-zero, obviously.)
+ *
+ */
+errcode_t profile_find_node(struct profile_node *section, const char *name,
+ const char *value, int section_flag, void **state,
+ struct profile_node **node)
+{
+ struct profile_node *p;
+
+ CHECK_MAGIC(section);
+ p = *state;
+ if (p) {
+ CHECK_MAGIC(p);
+ } else
+ p = section->first_child;
+
+ for (; p; p = p->next) {
+ if (name && (strcmp(p->name, name)))
+ continue;
+ if (section_flag) {
+ if (p->value)
+ continue;
+ } else {
+ if (!p->value)
+ continue;
+ if (value && (strcmp(p->value, value)))
+ continue;
+ }
+ if (p->deleted)
+ continue;
+ /* A match! */
+ if (node)
+ *node = p;
+ break;
+ }
+ if (p == 0) {
+ *state = 0;
+ return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION;
+ }
+ /*
+ * OK, we've found one match; now let's try to find another
+ * one. This way, if we return a non-zero state pointer,
+ * there's guaranteed to be another match that's returned.
+ */
+ for (p = p->next; p; p = p->next) {
+ if (name && (strcmp(p->name, name)))
+ continue;
+ if (section_flag) {
+ if (p->value)
+ continue;
+ } else {
+ if (!p->value)
+ continue;
+ if (value && (strcmp(p->value, value)))
+ continue;
+ }
+ /* A match! */
+ break;
+ }
+ *state = p;
+ return 0;
+}
+
+/*
+ * This is a general-purpose iterator for returning all nodes that
+ * match the specified name array.
+ */
+struct profile_iterator {
+ prf_magic_t magic;
+ profile_t profile;
+ int flags;
+ const char *const *names;
+ const char *name;
+ prf_file_t file;
+ int file_serial;
+ int done_idx;
+ struct profile_node *node;
+ int num;
+};
+
+errcode_t
+profile_iterator_create(profile_t profile, const char *const *names, int flags,
+ void **ret_iter)
+{
+ struct profile_iterator *iter;
+ int done_idx = 0;
+
+ if (profile == 0)
+ return PROF_NO_PROFILE;
+ if (profile->magic != PROF_MAGIC_PROFILE)
+ return PROF_MAGIC_PROFILE;
+ if (!names)
+ return PROF_BAD_NAMESET;
+ if (!(flags & PROFILE_ITER_LIST_SECTION)) {
+ if (!names[0])
+ return PROF_BAD_NAMESET;
+ done_idx = 1;
+ }
+
+ if ((iter = malloc(sizeof(struct profile_iterator))) == NULL)
+ return ENOMEM;
+
+ iter->magic = PROF_MAGIC_ITERATOR;
+ iter->profile = profile;
+ iter->names = names;
+ iter->flags = flags;
+ iter->file = profile->first_file;
+ iter->done_idx = done_idx;
+ iter->node = 0;
+ iter->num = 0;
+ *ret_iter = iter;
+ return 0;
+}
+
+void profile_iterator_free(void **iter_p)
+{
+ struct profile_iterator *iter;
+
+ if (!iter_p)
+ return;
+ iter = *iter_p;
+ if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
+ return;
+ free(iter);
+ *iter_p = 0;
+}
+
+/*
+ * Note: the returned character strings in ret_name and ret_value
+ * points to the stored character string in the parse string. Before
+ * this string value is returned to a calling application
+ * (profile_node_iterator is not an exported interface), it should be
+ * strdup()'ed.
+ */
+errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node,
+ char **ret_name, char **ret_value)
+{
+ struct profile_iterator *iter = *iter_p;
+ struct profile_node *section, *p;
+ const char *const *cpp;
+ errcode_t retval;
+ int skip_num = 0;
+
+ if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
+ return PROF_MAGIC_ITERATOR;
+ if (iter->file && iter->file->magic != PROF_MAGIC_FILE)
+ return PROF_MAGIC_FILE;
+ /*
+ * If the file has changed, then the node pointer is invalid,
+ * so we'll have search the file again looking for it.
+ */
+ if (iter->node && (iter->file &&
+ iter->file->upd_serial != iter->file_serial)) {
+ iter->flags &= ~PROFILE_ITER_FINAL_SEEN;
+ skip_num = iter->num;
+ iter->node = 0;
+ }
+ if (iter->node && iter->node->magic != PROF_MAGIC_NODE) {
+ return PROF_MAGIC_NODE;
+ }
+get_new_file:
+ if (iter->node == 0) {
+ if (iter->file == NULL ||
+ (iter->flags & PROFILE_ITER_FINAL_SEEN)) {
+ profile_iterator_free(iter_p);
+ if (ret_node)
+ *ret_node = 0;
+ if (ret_name)
+ *ret_name = 0;
+ if (ret_value)
+ *ret_value =0;
+ return 0;
+ }
+ if ((retval = profile_update_file(iter->file))) {
+ if (retval == ENOENT || retval == EACCES) {
+ /* XXX memory leak? */
+ if (iter->file)
+ iter->file = iter->file->next;
+ skip_num = 0;
+ retval = 0;
+ goto get_new_file;
+ } else {
+ profile_iterator_free(iter_p);
+ return retval;
+ }
+ }
+ iter->file_serial = iter->file->upd_serial;
+ /*
+ * Find the section to list if we are a LIST_SECTION,
+ * or find the containing section if not.
+ */
+ section = iter->file->root;
+ for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {
+ for (p=section->first_child; p; p = p->next) {
+ if (!strcmp(p->name, *cpp) && !p->value)
+ break;
+ }
+ if (!p) {
+ section = 0;
+ break;
+ }
+ section = p;
+ if (p->final)
+ iter->flags |= PROFILE_ITER_FINAL_SEEN;
+ }
+ if (!section) {
+ if (iter->file)
+ iter->file = iter->file->next;
+ skip_num = 0;
+ goto get_new_file;
+ }
+ iter->name = *cpp;
+ iter->node = section->first_child;
+ }
+ /*
+ * OK, now we know iter->node is set up correctly. Let's do
+ * the search.
+ */
+ for (p = iter->node; p; p = p->next) {
+ if (iter->name && strcmp(p->name, iter->name))
+ continue;
+ if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&
+ p->value)
+ continue;
+ if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&
+ !p->value)
+ continue;
+ if (skip_num > 0) {
+ skip_num--;
+ continue;
+ }
+ if (p->deleted)
+ continue;
+ break;
+ }
+ iter->num++;
+ if (!p) {
+ if (iter->file)
+ iter->file = iter->file->next;
+ iter->node = 0;
+ skip_num = 0;
+ goto get_new_file;
+ }
+ if ((iter->node = p->next) == NULL)
+ if (iter->file)
+ iter->file = iter->file->next;
+ if (ret_node)
+ *ret_node = p;
+ if (ret_name)
+ *ret_name = p->name;
+ if (ret_value)
+ *ret_value = p->value;
+ return 0;
+}
+
+
+/*
+ * prof_get.c --- routines that expose the public interfaces for
+ * querying items from the profile.
+ *
+ */
+
+/*
+ * This function only gets the first value from the file; it is a
+ * helper function for profile_get_string, profile_get_integer, etc.
+ */
+errcode_t profile_get_value(profile_t profile, const char *name,
+ const char *subname, const char *subsubname,
+ const char **ret_value)
+{
+ errcode_t retval;
+ void *state;
+ char *value;
+ const char *names[4];
+
+ names[0] = name;
+ names[1] = subname;
+ names[2] = subsubname;
+ names[3] = 0;
+
+ if ((retval = profile_iterator_create(profile, names,
+ PROFILE_ITER_RELATIONS_ONLY,
+ &state)))
+ return retval;
+
+ if ((retval = profile_node_iterator(&state, 0, 0, &value)))
+ goto cleanup;
+
+ if (value)
+ *ret_value = value;
+ else
+ retval = PROF_NO_RELATION;
+
+cleanup:
+ profile_iterator_free(&state);
+ return retval;
+}
+
+errcode_t
+profile_get_string(profile_t profile, const char *name, const char *subname,
+ const char *subsubname, const char *def_val,
+ char **ret_string)
+{
+ const char *value;
+ errcode_t retval;
+
+ if (profile) {
+ retval = profile_get_value(profile, name, subname,
+ subsubname, &value);
+ if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION)
+ value = def_val;
+ else if (retval)
+ return retval;
+ } else
+ value = def_val;
+
+ if (value) {
+ *ret_string = malloc(strlen(value)+1);
+ if (*ret_string == 0)
+ return ENOMEM;
+ strcpy(*ret_string, value);
+ } else
+ *ret_string = 0;
+ return 0;
+}
+
+errcode_t
+profile_get_integer(profile_t profile, const char *name, const char *subname,
+ const char *subsubname, int def_val, int *ret_int)
+{
+ const char *value;
+ errcode_t retval;
+ char *end_value;
+ long ret_long;
+
+ *ret_int = def_val;
+ if (profile == 0)
+ return 0;
+
+ retval = profile_get_value(profile, name, subname, subsubname, &value);
+ if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+ *ret_int = def_val;
+ return 0;
+ } else if (retval)
+ return retval;
+
+ if (value[0] == 0)
+ /* Empty string is no good. */
+ return PROF_BAD_INTEGER;
+ errno = 0;
+ ret_long = strtol(value, &end_value, 0);
+
+ /* Overflow or underflow. */
+ if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0)
+ return PROF_BAD_INTEGER;
+ /* Value outside "int" range. */
+ if ((long) (int) ret_long != ret_long)
+ return PROF_BAD_INTEGER;
+ /* Garbage in string. */
+ if (end_value != value + strlen (value))
+ return PROF_BAD_INTEGER;
+
+
+ *ret_int = ret_long;
+ return 0;
+}
+
+errcode_t
+profile_get_uint(profile_t profile, const char *name, const char *subname,
+ const char *subsubname, unsigned int def_val,
+ unsigned int *ret_int)
+{
+ const char *value;
+ errcode_t retval;
+ char *end_value;
+ unsigned long ret_long;
+
+ *ret_int = def_val;
+ if (profile == 0)
+ return 0;
+
+ retval = profile_get_value(profile, name, subname, subsubname, &value);
+ if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+ *ret_int = def_val;
+ return 0;
+ } else if (retval)
+ return retval;
+
+ if (value[0] == 0)
+ /* Empty string is no good. */
+ return PROF_BAD_INTEGER;
+ errno = 0;
+ ret_long = strtoul(value, &end_value, 0);
+
+ /* Overflow or underflow. */
+ if ((ret_long == ULONG_MAX) && errno != 0)
+ return PROF_BAD_INTEGER;
+ /* Value outside "int" range. */
+ if ((unsigned long) (unsigned int) ret_long != ret_long)
+ return PROF_BAD_INTEGER;
+ /* Garbage in string. */
+ if (end_value != value + strlen (value))
+ return PROF_BAD_INTEGER;
+
+ *ret_int = ret_long;
+ return 0;
+}
+
+errcode_t
+profile_get_double(profile_t profile, const char *name, const char *subname,
+ const char *subsubname, double def_val, double *ret_double)
+{
+ const char *value;
+ errcode_t retval;
+ char *end_value;
+ double double_val;
+
+ *ret_double = def_val;
+ if (profile == 0)
+ return 0;
+
+ retval = profile_get_value(profile, name, subname, subsubname, &value);
+ if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+ *ret_double = def_val;
+ return 0;
+ } else if (retval)
+ return retval;
+
+ if (value[0] == 0)
+ /* Empty string is no good. */
+ return PROF_BAD_INTEGER;
+ errno = 0;
+ double_val = strtod(value, &end_value);
+
+ /* Overflow or underflow. */
+ if (errno != 0)
+ return PROF_BAD_INTEGER;
+ /* Garbage in string. */
+ if (end_value != value + strlen(value))
+ return PROF_BAD_INTEGER;
+
+ *ret_double = double_val;
+ return 0;
+}
+
+static const char *const conf_yes[] = {
+ "y", "yes", "true", "t", "1", "on",
+ 0,
+};
+
+static const char *const conf_no[] = {
+ "n", "no", "false", "nil", "0", "off",
+ 0,
+};
+
+static errcode_t
+profile_parse_boolean(const char *s, int *ret_boolean)
+{
+ const char *const *p;
+
+ if (ret_boolean == NULL)
+ return PROF_EINVAL;
+
+ for(p=conf_yes; *p; p++) {
+ if (!strcasecmp(*p,s)) {
+ *ret_boolean = 1;
+ return 0;
+ }
+ }
+
+ for(p=conf_no; *p; p++) {
+ if (!strcasecmp(*p,s)) {
+ *ret_boolean = 0;
+ return 0;
+ }
+ }
+
+ return PROF_BAD_BOOLEAN;
+}
+
+errcode_t
+profile_get_boolean(profile_t profile, const char *name, const char *subname,
+ const char *subsubname, int def_val, int *ret_boolean)
+{
+ const char *value;
+ errcode_t retval;
+
+ if (profile == 0) {
+ *ret_boolean = def_val;
+ return 0;
+ }
+
+ retval = profile_get_value(profile, name, subname, subsubname, &value);
+ if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) {
+ *ret_boolean = def_val;
+ return 0;
+ } else if (retval)
+ return retval;
+
+ return profile_parse_boolean (value, ret_boolean);
+}
+
+errcode_t
+profile_iterator(void **iter_p, char **ret_name, char **ret_value)
+{
+ char *name, *value;
+ errcode_t retval;
+
+ retval = profile_node_iterator(iter_p, 0, &name, &value);
+ if (retval)
+ return retval;
+
+ if (ret_name) {
+ if (name) {
+ *ret_name = malloc(strlen(name)+1);
+ if (!*ret_name)
+ return ENOMEM;
+ strcpy(*ret_name, name);
+ } else
+ *ret_name = 0;
+ }
+ if (ret_value) {
+ if (value) {
+ *ret_value = malloc(strlen(value)+1);
+ if (!*ret_value) {
+ if (ret_name) {
+ free(*ret_name);
+ *ret_name = 0;
+ }
+ return ENOMEM;
+ }
+ strcpy(*ret_value, value);
+ } else
+ *ret_value = 0;
+ }
+ return 0;
+}
+
+#ifdef DEBUG_PROGRAM
+
+/*
+ * test_profile.c --- testing program for the profile routine
+ */
+
+#include "argv_parse.h"
+#include "profile_helpers.h"
+
+const char *program_name = "test_profile";
+
+#define PRINT_VALUE 1
+#define PRINT_VALUES 2
+
+static void do_cmd(profile_t profile, char **argv)
+{
+ errcode_t retval;
+ const char **names, *value;
+ char **values, **cpp;
+ char *cmd;
+ int print_status;
+
+ cmd = *(argv);
+ names = (const char **) argv + 1;
+ print_status = 0;
+ retval = 0;
+ if (cmd == 0)
+ return;
+ if (!strcmp(cmd, "query")) {
+ retval = profile_get_values(profile, names, &values);
+ print_status = PRINT_VALUES;
+ } else if (!strcmp(cmd, "query1")) {
+ const char *name = 0;
+ const char *subname = 0;
+ const char *subsubname = 0;
+
+ name = names[0];
+ if (name)
+ subname = names[1];
+ if (subname)
+ subsubname = names[2];
+ if (subsubname && names[3]) {
+ fprintf(stderr,
+ "Only 3 levels are allowed with query1\n");
+ retval = EINVAL;
+ } else
+ retval = profile_get_value(profile, name, subname,
+ subsubname, &value);
+ print_status = PRINT_VALUE;
+ } else if (!strcmp(cmd, "list_sections")) {
+ retval = profile_get_subsection_names(profile, names,
+ &values);
+ print_status = PRINT_VALUES;
+ } else if (!strcmp(cmd, "list_relations")) {
+ retval = profile_get_relation_names(profile, names,
+ &values);
+ print_status = PRINT_VALUES;
+ } else if (!strcmp(cmd, "dump")) {
+ retval = profile_write_tree_file
+ (profile->first_file->root, stdout);
+#if 0
+ } else if (!strcmp(cmd, "clear")) {
+ retval = profile_clear_relation(profile, names);
+ } else if (!strcmp(cmd, "update")) {
+ retval = profile_update_relation(profile, names+2,
+ *names, *(names+1));
+#endif
+ } else if (!strcmp(cmd, "verify")) {
+ retval = profile_verify_node
+ (profile->first_file->root);
+#if 0
+ } else if (!strcmp(cmd, "rename_section")) {
+ retval = profile_rename_section(profile, names+1, *names);
+ } else if (!strcmp(cmd, "add")) {
+ value = *names;
+ if (strcmp(value, "NULL") == 0)
+ value = NULL;
+ retval = profile_add_relation(profile, names+1, value);
+ } else if (!strcmp(cmd, "flush")) {
+ retval = profile_flush(profile);
+#endif
+ } else {
+ printf("Invalid command.\n");
+ }
+ if (retval) {
+ com_err(cmd, retval, "");
+ print_status = 0;
+ }
+ switch (print_status) {
+ case PRINT_VALUE:
+ printf("%s\n", value);
+ break;
+ case PRINT_VALUES:
+ for (cpp = values; *cpp; cpp++)
+ printf("%s\n", *cpp);
+ profile_free_list(values);
+ break;
+ }
+}
+
+static void do_batchmode(profile_t profile)
+{
+ int argc, ret;
+ char **argv;
+ char buf[256];
+
+ while (!feof(stdin)) {
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ break;
+ printf(">%s", buf);
+ ret = argv_parse(buf, &argc, &argv);
+ if (ret != 0) {
+ printf("Argv_parse returned %d!\n", ret);
+ continue;
+ }
+ do_cmd(profile, argv);
+ printf("\n");
+ argv_free(argv);
+ }
+ profile_release(profile);
+ exit(0);
+
+}
+
+void syntax_err_report(const char *filename, long err, int line_num)
+{
+ fprintf(stderr, "Syntax error in %s, line number %d: %s\n",
+ filename, line_num, error_message(err));
+ exit(1);
+}
+
+const char *default_str = "[foo]\n\tbar=quux\n\tsub = {\n\t\twin = true\n}\n";
+
+int main(int argc, char **argv)
+{
+ profile_t profile;
+ long retval;
+ char *cmd;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name);
+ exit(1);
+ }
+
+ initialize_prof_error_table();
+
+ profile_set_syntax_err_cb(syntax_err_report);
+
+ retval = profile_init_path(argv[1], &profile);
+ if (retval) {
+ com_err(program_name, retval, "while initializing profile");
+ exit(1);
+ }
+ retval = profile_set_default(profile, default_str);
+ if (retval) {
+ com_err(program_name, retval, "while setting default");
+ exit(1);
+ }
+
+ cmd = *(argv+2);
+ if (!cmd || !strcmp(cmd, "batch"))
+ do_batchmode(profile);
+ else
+ do_cmd(profile, argv+2);
+ profile_release(profile);
+
+ return 0;
+}
+
+#endif
diff --git a/lib/support/profile.h b/lib/support/profile.h
new file mode 100644
index 0000000..aec870a
--- /dev/null
+++ b/lib/support/profile.h
@@ -0,0 +1,107 @@
+/*
+ * profile.h
+ *
+ * Copyright (C) 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, 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, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original MIT software.
+ * M.I.T. makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _PROFILE_H
+#define _PROFILE_H
+
+typedef struct _profile_t *profile_t;
+
+typedef void (*profile_syntax_err_cb_t)(const char *file, long err,
+ int line_num);
+
+/*
+ * Used by the profile iterator in prof_get.c
+ */
+#define PROFILE_ITER_LIST_SECTION 0x0001
+#define PROFILE_ITER_SECTIONS_ONLY 0x0002
+#define PROFILE_ITER_RELATIONS_ONLY 0x0004
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+long profile_init
+ (const char * const *files, profile_t *ret_profile);
+
+void profile_release
+ (profile_t profile);
+
+long profile_set_default
+ (profile_t profile, const char *def_string);
+
+long profile_get_string
+ (profile_t profile, const char *name, const char *subname,
+ const char *subsubname, const char *def_val,
+ char **ret_string);
+long profile_get_integer
+ (profile_t profile, const char *name, const char *subname,
+ const char *subsubname, int def_val,
+ int *ret_default);
+
+long profile_get_uint
+ (profile_t profile, const char *name, const char *subname,
+ const char *subsubname, unsigned int def_val,
+ unsigned int *ret_int);
+
+long profile_get_double
+ (profile_t profile, const char *name, const char *subname,
+ const char *subsubname, double def_val,
+ double *ret_float);
+
+long profile_get_boolean
+ (profile_t profile, const char *name, const char *subname,
+ const char *subsubname, int def_val,
+ int *ret_default);
+
+long profile_iterator_create
+ (profile_t profile, const char *const *names,
+ int flags, void **ret_iter);
+
+void profile_iterator_free
+ (void **iter_p);
+
+long profile_iterator
+ (void **iter_p, char **ret_name, char **ret_value);
+
+profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _KRB5_H */
diff --git a/lib/support/profile_helpers.c b/lib/support/profile_helpers.c
new file mode 100644
index 0000000..d7d6f15
--- /dev/null
+++ b/lib/support/profile_helpers.c
@@ -0,0 +1,317 @@
+/*
+ * profile_helpers.c -- Helper functions for the profile library
+ *
+ * These functions are not part of the "core" profile library, and do
+ * not require access to the internal functions and data structures of
+ * the profile library. They are mainly convenience functions for
+ * programs that want to do something unusual such as obtaining the
+ * list of sections or relations, or accessing multiple values from a
+ * relation that is listed more than once. This functionality can all
+ * be done using the profile_iterator abstraction, but it is less
+ * convenient.
+ *
+ * Copyright (C) 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <et/com_err.h>
+#include "profile.h"
+#include "profile_helpers.h"
+#include "prof_err.h"
+
+/*
+ * These functions --- init_list(), end_list(), and add_to_list() are
+ * internal functions used to build up a null-terminated char ** list
+ * of strings to be returned by functions like profile_get_values.
+ *
+ * The profile_string_list structure is used for internal booking
+ * purposes to build up the list, which is returned in *ret_list by
+ * the end_list() function.
+ *
+ * The publicly exported interface for freeing char** list is
+ * profile_free_list().
+ */
+
+struct profile_string_list {
+ char **list;
+ int num;
+ int max;
+};
+
+/*
+ * Initialize the string list abstraction.
+ */
+static errcode_t init_list(struct profile_string_list *list)
+{
+ list->num = 0;
+ list->max = 10;
+ list->list = malloc(list->max * sizeof(char *));
+ if (list->list == 0)
+ return ENOMEM;
+ list->list[0] = 0;
+ return 0;
+}
+
+/*
+ * Free any memory left over in the string abstraction, returning the
+ * built up list in *ret_list if it is non-null.
+ */
+static void end_list(struct profile_string_list *list, char ***ret_list)
+{
+ char **cp;
+
+ if (list == 0)
+ return;
+
+ if (ret_list) {
+ *ret_list = list->list;
+ return;
+ } else {
+ for (cp = list->list; *cp; cp++)
+ free(*cp);
+ free(list->list);
+ }
+ list->num = list->max = 0;
+ list->list = 0;
+}
+
+/*
+ * Add a string to the list.
+ */
+static errcode_t add_to_list(struct profile_string_list *list, char *str)
+{
+ char **newlist;
+ int newmax;
+
+ if (list->num+1 >= list->max) {
+ newmax = list->max + 10;
+ newlist = realloc(list->list, newmax * sizeof(char *));
+ if (newlist == 0)
+ return ENOMEM;
+ list->max = newmax;
+ list->list = newlist;
+ }
+
+ list->list[list->num++] = str;
+ list->list[list->num] = 0;
+ return 0;
+}
+
+/*
+ * Return TRUE if the string is already a member of the list.
+ */
+static int is_list_member(struct profile_string_list *list, const char *str)
+{
+ char **cpp;
+
+ if (!list->list)
+ return 0;
+
+ for (cpp = list->list; *cpp; cpp++) {
+ if (!strcmp(*cpp, str))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * This function frees a null-terminated list as returned by
+ * profile_get_values.
+ */
+void profile_free_list(char **list)
+{
+ char **cp;
+
+ if (list == 0)
+ return;
+
+ for (cp = list; *cp; cp++)
+ free(*cp);
+ free(list);
+}
+
+errcode_t
+profile_get_values(profile_t profile, const char *const *names,
+ char ***ret_values)
+{
+ errcode_t retval;
+ void *state;
+ char *value;
+ struct profile_string_list values;
+
+ if ((retval = profile_iterator_create(profile, names,
+ PROFILE_ITER_RELATIONS_ONLY,
+ &state)))
+ return retval;
+
+ if ((retval = init_list(&values)))
+ goto cleanup_iterator;
+
+ do {
+ if ((retval = profile_iterator(&state, 0, &value)))
+ goto cleanup;
+ if (value)
+ add_to_list(&values, value);
+ } while (state);
+
+ if (values.num == 0) {
+ retval = PROF_NO_RELATION;
+ goto cleanup;
+ }
+
+ end_list(&values, ret_values);
+ return 0;
+
+cleanup:
+ end_list(&values, 0);
+cleanup_iterator:
+ profile_iterator_free(&state);
+ return retval;
+}
+
+/*
+ * This function will return the list of the names of subsections in the
+ * under the specified section name.
+ */
+errcode_t
+profile_get_subsection_names(profile_t profile, const char **names,
+ char ***ret_names)
+{
+ errcode_t retval;
+ void *state;
+ char *name;
+ struct profile_string_list values;
+
+ if ((retval = profile_iterator_create(profile, names,
+ PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY,
+ &state)))
+ return retval;
+
+ if ((retval = init_list(&values)))
+ goto cleanup_iterator;
+
+ do {
+ if ((retval = profile_iterator(&state, &name, 0)))
+ goto cleanup;
+ if (name)
+ add_to_list(&values, name);
+ } while (state);
+
+ end_list(&values, ret_names);
+ return 0;
+
+cleanup:
+ end_list(&values, 0);
+cleanup_iterator:
+ profile_iterator_free(&state);
+ return retval;
+}
+
+/*
+ * This function will return the list of the names of relations in the
+ * under the specified section name.
+ */
+errcode_t
+profile_get_relation_names(profile_t profile, const char **names,
+ char ***ret_names)
+{
+ errcode_t retval;
+ void *state;
+ char *name;
+ struct profile_string_list values;
+
+ if ((retval = profile_iterator_create(profile, names,
+ PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY,
+ &state)))
+ return retval;
+
+ if ((retval = init_list(&values)))
+ goto cleanup_iterator;
+
+ do {
+ if ((retval = profile_iterator(&state, &name, 0)))
+ goto cleanup;
+ if (name) {
+ if (is_list_member(&values, name))
+ free(name);
+ else
+ add_to_list(&values, name);
+ }
+ } while (state);
+
+ end_list(&values, ret_names);
+ return 0;
+
+cleanup:
+ end_list(&values, 0);
+cleanup_iterator:
+ profile_iterator_free(&state);
+ return retval;
+}
+
+
+void
+profile_release_string(char *str)
+{
+ free(str);
+}
+
+errcode_t
+profile_init_path(const char * filepath,
+ profile_t *ret_profile)
+{
+ int n_entries, i;
+ unsigned int ent_len;
+ const char *s, *t;
+ char **filenames;
+ errcode_t retval;
+
+ /* count the distinct filename components */
+ for(s = filepath, n_entries = 1; *s; s++) {
+ if (*s == ':')
+ n_entries++;
+ }
+
+ /* the array is NULL terminated */
+ filenames = (char **) malloc((n_entries+1) * sizeof(char*));
+ if (filenames == 0)
+ return ENOMEM;
+
+ /* measure, copy, and skip each one */
+ for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) {
+ ent_len = t-s;
+ filenames[i] = (char*) malloc(ent_len + 1);
+ if (filenames[i] == 0) {
+ /* if malloc fails, free the ones that worked */
+ while(--i >= 0) free(filenames[i]);
+ free(filenames);
+ return ENOMEM;
+ }
+ strncpy(filenames[i], s, ent_len);
+ filenames[i][ent_len] = 0;
+ if (*t == 0) {
+ i++;
+ break;
+ }
+ }
+ /* cap the array */
+ filenames[i] = 0;
+
+ retval = profile_init((const char * const *) filenames,
+ ret_profile);
+
+ /* count back down and free the entries */
+ while(--i >= 0) free(filenames[i]);
+ free(filenames);
+
+ return retval;
+}
diff --git a/lib/support/profile_helpers.h b/lib/support/profile_helpers.h
new file mode 100644
index 0000000..af63ca5
--- /dev/null
+++ b/lib/support/profile_helpers.h
@@ -0,0 +1,28 @@
+/*
+ * profile_helpers.h -- Function prototypes for profile helper functions
+ *
+ * Copyright (C) 2006 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+long profile_get_values
+ (profile_t profile, const char *const *names, char ***ret_values);
+
+void profile_free_list
+ (char **list);
+
+long profile_get_relation_names
+ (profile_t profile, const char **names, char ***ret_names);
+
+long profile_get_subsection_names
+ (profile_t profile, const char **names, char ***ret_names);
+
+void profile_release_string (char *str);
+
+long profile_init_path
+ (const char * filelist, profile_t *ret_profile);
+
diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
new file mode 100644
index 0000000..b41bb74
--- /dev/null
+++ b/lib/support/quotaio.c
@@ -0,0 +1,412 @@
+/** quotaio.c
+ *
+ * Generic IO operations on quotafiles
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ * Aditya Kali <adityakali@google.com> - Ported to e2fsprogs
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <assert.h>
+
+#include "common.h"
+#include "quotaio.h"
+
+static const char * const extensions[MAXQUOTAS] = {
+ [USRQUOTA] = "user",
+ [GRPQUOTA] = "group",
+ [PRJQUOTA] = "project",
+};
+static const char * const basenames[] = {
+ "", /* undefined */
+ "quota", /* QFMT_VFS_OLD */
+ "aquota", /* QFMT_VFS_V0 */
+ "", /* QFMT_OCFS2 */
+ "aquota" /* QFMT_VFS_V1 */
+};
+
+/* Header in all newer quotafiles */
+struct disk_dqheader {
+ __le32 dqh_magic;
+ __le32 dqh_version;
+} __attribute__ ((packed));
+
+/**
+ * Convert type of quota to written representation
+ */
+const char *quota_type2name(enum quota_type qtype)
+{
+ if (qtype >= MAXQUOTAS)
+ return "unknown";
+ return extensions[qtype];
+}
+
+ext2_ino_t quota_type2inum(enum quota_type qtype,
+ struct ext2_super_block *sb)
+{
+ switch (qtype) {
+ case USRQUOTA:
+ return EXT4_USR_QUOTA_INO;
+ case GRPQUOTA:
+ return EXT4_GRP_QUOTA_INO;
+ case PRJQUOTA:
+ return sb->s_prj_quota_inum;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Creates a quota file name for given type and format.
+ */
+const char *quota_get_qf_name(enum quota_type type, int fmt, char *buf)
+{
+ if (!buf)
+ return NULL;
+ snprintf(buf, QUOTA_NAME_LEN, "%s.%s",
+ basenames[fmt], extensions[type]);
+
+ return buf;
+}
+
+/*
+ * Set grace time if needed
+ */
+void update_grace_times(struct dquot *q)
+{
+ time_t now;
+
+ time(&now);
+ if (q->dq_dqb.dqb_bsoftlimit && toqb(q->dq_dqb.dqb_curspace) >
+ q->dq_dqb.dqb_bsoftlimit) {
+ if (!q->dq_dqb.dqb_btime)
+ q->dq_dqb.dqb_btime =
+ now + q->dq_h->qh_info.dqi_bgrace;
+ } else {
+ q->dq_dqb.dqb_btime = 0;
+ }
+
+ if (q->dq_dqb.dqb_isoftlimit && q->dq_dqb.dqb_curinodes >
+ q->dq_dqb.dqb_isoftlimit) {
+ if (!q->dq_dqb.dqb_itime)
+ q->dq_dqb.dqb_itime =
+ now + q->dq_h->qh_info.dqi_igrace;
+ } else {
+ q->dq_dqb.dqb_itime = 0;
+ }
+}
+
+errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ errcode_t err;
+ enum quota_type qtype;
+
+ if ((err = ext2fs_read_inode(fs, ino, &inode)))
+ return err;
+
+ for (qtype = 0; qtype < MAXQUOTAS; qtype++)
+ if (ino == quota_type2inum(qtype, fs->super))
+ break;
+
+ if (qtype != MAXQUOTAS) {
+ inode.i_dtime = fs->now ? fs->now : time(0);
+ if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+ return 0;
+ err = ext2fs_punch(fs, ino, &inode, NULL, 0, ~0ULL);
+ if (err)
+ return err;
+ fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ } else {
+ inode.i_flags &= ~EXT2_IMMUTABLE_FL;
+ }
+ err = ext2fs_write_inode(fs, ino, &inode);
+ return err;
+}
+
+/* Functions to read/write quota file. */
+static unsigned int quota_write_nomount(struct quota_file *qf,
+ ext2_loff_t offset,
+ void *buf, unsigned int size)
+{
+ ext2_file_t e2_file = qf->e2_file;
+ unsigned int bytes_written = 0;
+ errcode_t err;
+
+ err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
+ if (err) {
+ log_err("ext2fs_file_llseek failed: %ld", err);
+ return 0;
+ }
+
+ err = ext2fs_file_write(e2_file, buf, size, &bytes_written);
+ if (err) {
+ log_err("ext2fs_file_write failed: %ld", err);
+ return 0;
+ }
+
+ /* Correct inode.i_size is set in end_io. */
+ return bytes_written;
+}
+
+static unsigned int quota_read_nomount(struct quota_file *qf,
+ ext2_loff_t offset,
+ void *buf, unsigned int size)
+{
+ ext2_file_t e2_file = qf->e2_file;
+ unsigned int bytes_read = 0;
+ errcode_t err;
+
+ err = ext2fs_file_llseek(e2_file, offset, EXT2_SEEK_SET, NULL);
+ if (err) {
+ log_err("ext2fs_file_llseek failed: %ld", err);
+ return 0;
+ }
+
+ err = ext2fs_file_read(e2_file, buf, size, &bytes_read);
+ if (err) {
+ log_err("ext2fs_file_read failed: %ld", err);
+ return 0;
+ }
+
+ return bytes_read;
+}
+
+/*
+ * Detect quota format and initialize quota IO
+ */
+errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
+ ext2_ino_t qf_ino, enum quota_type qtype,
+ int fmt, int flags)
+{
+ ext2_filsys fs = qctx->fs;
+ ext2_file_t e2_file;
+ errcode_t err;
+ int allocated_handle = 0;
+
+ if (qtype >= MAXQUOTAS)
+ return EINVAL;
+
+ if (fmt == -1)
+ fmt = QFMT_VFS_V1;
+
+ err = ext2fs_read_bitmaps(fs);
+ if (err)
+ return err;
+
+ if (qf_ino == 0)
+ qf_ino = *quota_sb_inump(fs->super, qtype);
+
+ log_debug("Opening quota ino=%u, type=%d", qf_ino, qtype);
+ err = ext2fs_file_open(fs, qf_ino, flags, &e2_file);
+ if (err) {
+ log_err("ext2fs_file_open failed: %s", error_message(err));
+ return err;
+ }
+
+ if (!h) {
+ if (qctx->quota_file[qtype]) {
+ h = qctx->quota_file[qtype];
+ if (((flags & EXT2_FILE_WRITE) == 0) ||
+ (h->qh_file_flags & EXT2_FILE_WRITE)) {
+ ext2fs_file_close(e2_file);
+ return 0;
+ }
+ (void) quota_file_close(qctx, h);
+ }
+ err = ext2fs_get_mem(sizeof(struct quota_handle), &h);
+ if (err) {
+ log_err("Unable to allocate quota handle");
+ ext2fs_file_close(e2_file);
+ return err;
+ }
+ allocated_handle = 1;
+ }
+
+ h->qh_qf.e2_file = e2_file;
+ h->qh_qf.fs = fs;
+ h->qh_qf.ino = qf_ino;
+ h->e2fs_write = quota_write_nomount;
+ h->e2fs_read = quota_read_nomount;
+ h->qh_file_flags = flags;
+ h->qh_io_flags = 0;
+ h->qh_type = qtype;
+ h->qh_fmt = fmt;
+ memset(&h->qh_info, 0, sizeof(h->qh_info));
+ h->qh_ops = &quotafile_ops_2;
+
+ if (h->qh_ops->check_file &&
+ (h->qh_ops->check_file(h, qtype, fmt) == 0)) {
+ log_err("qh_ops->check_file failed");
+ err = EIO;
+ goto errout;
+ }
+
+ if (h->qh_ops->init_io && (h->qh_ops->init_io(h) < 0)) {
+ log_err("qh_ops->init_io failed");
+ err = EIO;
+ goto errout;
+ }
+ if (allocated_handle)
+ qctx->quota_file[qtype] = h;
+
+ return 0;
+errout:
+ ext2fs_file_close(e2_file);
+ if (allocated_handle)
+ ext2fs_free_mem(&h);
+ return err;
+}
+
+static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ errcode_t err = 0;
+
+ err = ext2fs_read_inode(fs, ino, &inode);
+ if (err) {
+ log_err("ex2fs_read_inode failed");
+ return err;
+ }
+
+ if (EXT2_I_SIZE(&inode)) {
+ err = quota_inode_truncate(fs, ino);
+ if (err)
+ return err;
+ }
+
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ ext2fs_iblk_set(fs, &inode, 0);
+ inode.i_atime = inode.i_mtime =
+ inode.i_ctime = fs->now ? fs->now : time(0);
+ inode.i_links_count = 1;
+ inode.i_mode = LINUX_S_IFREG | 0600;
+ inode.i_flags |= EXT2_IMMUTABLE_FL;
+ if (ext2fs_has_feature_extents(fs->super))
+ inode.i_flags |= EXT4_EXTENTS_FL;
+
+ err = ext2fs_write_new_inode(fs, ino, &inode);
+ if (err) {
+ log_err("ext2fs_write_new_inode failed: %ld", err);
+ return err;
+ }
+ return err;
+}
+
+/*
+ * Create new quotafile of specified format on given filesystem
+ */
+errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
+ enum quota_type qtype, int fmt)
+{
+ ext2_file_t e2_file;
+ errcode_t err;
+ ext2_ino_t qf_inum = 0;
+
+ if (fmt == -1)
+ fmt = QFMT_VFS_V1;
+
+ h->qh_qf.fs = fs;
+ qf_inum = quota_type2inum(qtype, fs->super);
+ if (qf_inum == 0 && qtype == PRJQUOTA) {
+ err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
+ 0, &qf_inum);
+ if (err)
+ return err;
+ ext2fs_inode_alloc_stats2(fs, qf_inum, +1, 0);
+ ext2fs_mark_ib_dirty(fs);
+ } else if (qf_inum == 0) {
+ return EXT2_ET_BAD_INODE_NUM;
+ }
+
+ err = ext2fs_read_bitmaps(fs);
+ if (err)
+ goto out_err;
+
+ err = quota_inode_init_new(fs, qf_inum);
+ if (err) {
+ log_err("init_new_quota_inode failed");
+ goto out_err;
+ }
+ h->qh_qf.ino = qf_inum;
+ h->qh_file_flags = EXT2_FILE_WRITE | EXT2_FILE_CREATE;
+ h->e2fs_write = quota_write_nomount;
+ h->e2fs_read = quota_read_nomount;
+
+ log_debug("Creating quota ino=%u, type=%d", qf_inum, qtype);
+ err = ext2fs_file_open(fs, qf_inum, h->qh_file_flags, &e2_file);
+ if (err) {
+ log_err("ext2fs_file_open failed: %ld", err);
+ goto out_err;
+ }
+ h->qh_qf.e2_file = e2_file;
+
+ h->qh_io_flags = 0;
+ h->qh_type = qtype;
+ h->qh_fmt = fmt;
+ memset(&h->qh_info, 0, sizeof(h->qh_info));
+ h->qh_ops = &quotafile_ops_2;
+
+ if (h->qh_ops->new_io && (h->qh_ops->new_io(h) < 0)) {
+ log_err("qh_ops->new_io failed");
+ err = EIO;
+ goto out_err1;
+ }
+
+ return 0;
+
+out_err1:
+ ext2fs_file_close(e2_file);
+out_err:
+
+ if (qf_inum)
+ quota_inode_truncate(fs, qf_inum);
+
+ return err;
+}
+
+/*
+ * Close quotafile and release handle
+ */
+errcode_t quota_file_close(quota_ctx_t qctx, struct quota_handle *h)
+{
+ if (h->qh_io_flags & IOFL_INFODIRTY) {
+ if (h->qh_ops->write_info && h->qh_ops->write_info(h) < 0)
+ return EIO;
+ h->qh_io_flags &= ~IOFL_INFODIRTY;
+ }
+
+ if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0)
+ return EIO;
+ if (h->qh_qf.e2_file)
+ ext2fs_file_close(h->qh_qf.e2_file);
+ if (qctx->quota_file[h->qh_type] == h)
+ ext2fs_free_mem(&qctx->quota_file[h->qh_type]);
+ return 0;
+}
+
+/*
+ * Create empty quota structure
+ */
+struct dquot *get_empty_dquot(void)
+{
+ struct dquot *dquot;
+
+ if (ext2fs_get_memzero(sizeof(struct dquot), &dquot)) {
+ log_err("Failed to allocate dquot");
+ return NULL;
+ }
+
+ dquot->dq_id = -1;
+ return dquot;
+}
diff --git a/lib/support/quotaio.h b/lib/support/quotaio.h
new file mode 100644
index 0000000..84fac35
--- /dev/null
+++ b/lib/support/quotaio.h
@@ -0,0 +1,268 @@
+/** quotaio.h
+ *
+ * Interface to the quota library.
+ *
+ * The quota library provides interface for creating and updating the quota
+ * files and the ext4 superblock fields. It supports the new VFS_V1 quota
+ * format. The quota library also provides support for keeping track of quotas
+ * in memory.
+ * The typical way to use the quota library is as follows:
+ * {
+ * quota_ctx_t qctx;
+ *
+ * quota_init_context(&qctx, fs, QUOTA_ALL_BIT);
+ * {
+ * quota_compute_usage(qctx);
+ * AND/OR
+ * quota_data_add/quota_data_sub/quota_data_inodes();
+ * }
+ * quota_write_inode(qctx, USRQUOTA);
+ * quota_write_inode(qctx, GRPQUOTA);
+ * quota_release_context(&qctx);
+ * }
+ *
+ * This initial version does not support reading the quota files. This support
+ * will be added in near future.
+ *
+ * Aditya Kali <adityakali@google.com>
+ * Header of IO operations for quota utilities
+ *
+ * Jan Kara <jack@suse.cz>
+ */
+
+#ifndef GUARD_QUOTAIO_H
+#define GUARD_QUOTAIO_H
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "dqblk_v2.h"
+
+typedef int64_t qsize_t; /* Type in which we store size limitations */
+
+enum quota_type {
+ USRQUOTA = 0,
+ GRPQUOTA = 1,
+ PRJQUOTA = 2,
+ MAXQUOTAS = 3,
+};
+
+#if MAXQUOTAS > 32
+#error "cannot have more than 32 quota types to fit in qtype_bits"
+#endif
+
+#define QUOTA_USR_BIT (1 << USRQUOTA)
+#define QUOTA_GRP_BIT (1 << GRPQUOTA)
+#define QUOTA_PRJ_BIT (1 << PRJQUOTA)
+#define QUOTA_ALL_BIT (QUOTA_USR_BIT | QUOTA_GRP_BIT | QUOTA_PRJ_BIT)
+
+typedef struct quota_ctx *quota_ctx_t;
+struct dict_t;
+
+struct quota_ctx {
+ ext2_filsys fs;
+ struct dict_t *quota_dict[MAXQUOTAS];
+ struct quota_handle *quota_file[MAXQUOTAS];
+};
+
+/*
+ * Definitions of magics and versions of current quota files
+ */
+#define INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927, /* GRPQUOTA */\
+ 0xd9c03f14 /* PRJQUOTA */\
+}
+
+/* Size of blocks in which are counted size limits in generic utility parts */
+#define QUOTABLOCK_BITS 10
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+
+/* Quota format type IDs */
+#define QFMT_VFS_OLD 1
+#define QFMT_VFS_V0 2
+#define QFMT_VFS_V1 4
+
+/*
+ * The following constants define the default amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
+
+#define IOFL_INFODIRTY 0x01 /* Did info change? */
+
+struct quotafile_ops;
+
+/* Generic information about quotafile */
+struct util_dqinfo {
+ time_t dqi_bgrace; /* Block grace time for given quotafile */
+ time_t dqi_igrace; /* Inode grace time for given quotafile */
+ union {
+ struct v2_mem_dqinfo v2_mdqi;
+ } u; /* Format specific info about quotafile */
+};
+
+struct quota_file {
+ ext2_filsys fs;
+ ext2_ino_t ino;
+ ext2_file_t e2_file;
+};
+
+/* Structure for one opened quota file */
+struct quota_handle {
+ enum quota_type qh_type; /* Type of quotafile */
+ int qh_fmt; /* Quotafile format */
+ int qh_file_flags;
+ int qh_io_flags; /* IO flags for file */
+ struct quota_file qh_qf;
+ unsigned int (*e2fs_read)(struct quota_file *qf, ext2_loff_t offset,
+ void *buf, unsigned int size);
+ unsigned int (*e2fs_write)(struct quota_file *qf, ext2_loff_t offset,
+ void *buf, unsigned int size);
+ struct quotafile_ops *qh_ops; /* Operations on quotafile */
+ struct util_dqinfo qh_info; /* Generic quotafile info */
+};
+
+/* Utility quota block */
+struct util_dqblk {
+ qsize_t dqb_ihardlimit;
+ qsize_t dqb_isoftlimit;
+ qsize_t dqb_curinodes;
+ qsize_t dqb_bhardlimit;
+ qsize_t dqb_bsoftlimit;
+ qsize_t dqb_curspace;
+ time_t dqb_btime;
+ time_t dqb_itime;
+ union {
+ struct v2_mem_dqblk v2_mdqb;
+ } u; /* Format specific dquot information */
+};
+
+/* Structure for one loaded quota */
+struct dquot {
+ struct dquot *dq_next; /* Pointer to next dquot in the list */
+ qid_t dq_id; /* ID dquot belongs to */
+ int dq_flags; /* Some flags for utils */
+ struct quota_handle *dq_h; /* Handle of quotafile for this dquot */
+ struct util_dqblk dq_dqb; /* Parsed data of dquot */
+};
+
+#define DQF_SEEN 0x0001
+
+/* Structure of quotafile operations */
+struct quotafile_ops {
+ /* Check whether quotafile is in our format */
+ int (*check_file) (struct quota_handle *h, int type, int fmt);
+ /* Open quotafile */
+ int (*init_io) (struct quota_handle *h);
+ /* Create new quotafile */
+ int (*new_io) (struct quota_handle *h);
+ /* Write all changes and close quotafile */
+ int (*end_io) (struct quota_handle *h);
+ /* Write info about quotafile */
+ int (*write_info) (struct quota_handle *h);
+ /* Read dquot into memory */
+ struct dquot *(*read_dquot) (struct quota_handle *h, qid_t id);
+ /* Write given dquot to disk */
+ int (*commit_dquot) (struct dquot *dquot);
+ /* Scan quotafile and call callback on every structure */
+ int (*scan_dquots) (struct quota_handle *h,
+ int (*process_dquot) (struct dquot *dquot,
+ void *data),
+ void *data);
+ /* Function to print format specific file information */
+ int (*report) (struct quota_handle *h, int verbose);
+};
+
+/* This might go into a special header file but that sounds a bit silly... */
+extern struct quotafile_ops quotafile_ops_meta;
+
+/* Open existing quotafile of given type (and verify its format) on given
+ * filesystem. */
+errcode_t quota_file_open(quota_ctx_t qctx, struct quota_handle *h,
+ ext2_ino_t qf_ino, enum quota_type type,
+ int fmt, int flags);
+
+
+/* Create new quotafile of specified format on given filesystem */
+errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs,
+ enum quota_type qtype, int fmt);
+
+/* Close quotafile */
+errcode_t quota_file_close(quota_ctx_t qctx, struct quota_handle *h);
+
+/* Get empty quota structure */
+struct dquot *get_empty_dquot(void);
+
+errcode_t quota_inode_truncate(ext2_filsys fs, ext2_ino_t ino);
+
+const char *quota_type2name(enum quota_type qtype);
+ext2_ino_t quota_type2inum(enum quota_type qtype, struct ext2_super_block *);
+
+void update_grace_times(struct dquot *q);
+
+/* size for the buffer returned by quota_get_qf_name(); must be greater
+ than maxlen of extensions[] and fmtnames[] (plus 2) found in quotaio.c */
+#define QUOTA_NAME_LEN 16
+
+const char *quota_get_qf_name(enum quota_type, int fmt, char *buf);
+
+/* In mkquota.c */
+errcode_t quota_init_context(quota_ctx_t *qctx, ext2_filsys fs,
+ unsigned int qtype_bits);
+void quota_data_inodes(quota_ctx_t qctx, struct ext2_inode_large *inode,
+ ext2_ino_t ino, int adjust);
+void quota_data_add(quota_ctx_t qctx, struct ext2_inode_large *inode,
+ ext2_ino_t ino, qsize_t space);
+void quota_data_sub(quota_ctx_t qctx, struct ext2_inode_large *inode,
+ ext2_ino_t ino, qsize_t space);
+errcode_t quota_write_inode(quota_ctx_t qctx, enum quota_type qtype);
+/* Flags for quota_read_all_dquots() */
+#define QREAD_USAGE 0x01
+#define QREAD_LIMITS 0x02
+errcode_t quota_read_all_dquots(quota_ctx_t qctx, ext2_ino_t qf_ino,
+ enum quota_type type, unsigned int flags);
+errcode_t quota_compute_usage(quota_ctx_t qctx);
+void quota_release_context(quota_ctx_t *qctx);
+errcode_t quota_remove_inode(ext2_filsys fs, enum quota_type qtype);
+int quota_file_exists(ext2_filsys fs, enum quota_type qtype);
+void quota_set_sb_inum(ext2_filsys fs, ext2_ino_t ino, enum quota_type qtype);
+errcode_t quota_compare_and_update(quota_ctx_t qctx, enum quota_type qtype,
+ int *usage_inconsistent);
+int parse_quota_opts(const char *opts, int (*func)(char *));
+
+/* parse_qtype.c */
+int parse_quota_types(const char *in_str, unsigned int *qtype_bits,
+ char **err_token);
+
+/*
+ * Return pointer to reserved inode field in superblock for given quota type.
+ *
+ * This allows the caller to get or set the quota inode by type without the
+ * need for the quota array to be contiguous in the superblock.
+ */
+static inline ext2_ino_t *quota_sb_inump(struct ext2_super_block *sb,
+ enum quota_type qtype)
+{
+ switch (qtype) {
+ case USRQUOTA:
+ return &sb->s_usr_quota_inum;
+ case GRPQUOTA:
+ return &sb->s_grp_quota_inum;
+ case PRJQUOTA:
+ return &sb->s_prj_quota_inum;
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#endif /* GUARD_QUOTAIO_H */
diff --git a/lib/support/quotaio_tree.c b/lib/support/quotaio_tree.c
new file mode 100644
index 0000000..5910e63
--- /dev/null
+++ b/lib/support/quotaio_tree.c
@@ -0,0 +1,687 @@
+/*
+ * Implementation of new quotafile format
+ *
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "quotaio_tree.h"
+#include "quotaio.h"
+
+typedef char *dqbuf_t;
+
+#define freedqbuf(buf) ext2fs_free_mem(&buf)
+
+static inline dqbuf_t getdqbuf(void)
+{
+ dqbuf_t buf;
+ if (ext2fs_get_memzero(QT_BLKSIZE, &buf)) {
+ log_err("Failed to allocate dqbuf");
+ return NULL;
+ }
+
+ return buf;
+}
+
+/* Is given dquot empty? */
+int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk)
+{
+ unsigned int i;
+
+ for (i = 0; i < info->dqi_entry_size; i++)
+ if (disk[i])
+ return 0;
+ return 1;
+}
+
+int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info)
+{
+ return (QT_BLKSIZE - sizeof(struct qt_disk_dqdbheader)) /
+ info->dqi_entry_size;
+}
+
+static int get_index(qid_t id, int depth)
+{
+ return (id >> ((QT_TREEDEPTH - depth - 1) * 8)) & 0xff;
+}
+
+static inline void mark_quotafile_info_dirty(struct quota_handle *h)
+{
+ h->qh_io_flags |= IOFL_INFODIRTY;
+}
+
+/* Read given block */
+static void read_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
+{
+ int err;
+
+ err = h->e2fs_read(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
+ QT_BLKSIZE);
+ if (err < 0)
+ log_err("Cannot read block %u: %s", blk, strerror(errno));
+ else if (err != QT_BLKSIZE)
+ memset(buf + err, 0, QT_BLKSIZE - err);
+}
+
+/* Write block */
+static int write_blk(struct quota_handle *h, unsigned int blk, dqbuf_t buf)
+{
+ int err;
+
+ err = h->e2fs_write(&h->qh_qf, blk << QT_BLKSIZE_BITS, buf,
+ QT_BLKSIZE);
+ if (err < 0 && errno != ENOSPC)
+ log_err("Cannot write block (%u): %s", blk, strerror(errno));
+ if (err != QT_BLKSIZE)
+ return -ENOSPC;
+ return 0;
+}
+
+/* Get free block in file (either from free list or create new one) */
+static int get_free_dqblk(struct quota_handle *h)
+{
+ dqbuf_t buf = getdqbuf();
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ int blk;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (info->dqi_free_blk) {
+ blk = info->dqi_free_blk;
+ read_blk(h, blk, buf);
+ info->dqi_free_blk = ext2fs_le32_to_cpu(dh->dqdh_next_free);
+ } else {
+ memset(buf, 0, QT_BLKSIZE);
+ /* Assure block allocation... */
+ if (write_blk(h, info->dqi_blocks, buf) < 0) {
+ freedqbuf(buf);
+ log_err("Cannot allocate new quota block "
+ "(out of disk space).");
+ return -ENOSPC;
+ }
+ blk = info->dqi_blocks++;
+ }
+ mark_quotafile_info_dirty(h);
+ freedqbuf(buf);
+ return blk;
+}
+
+/* Put given block to free list */
+static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
+{
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_blk);
+ dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
+ dh->dqdh_entries = ext2fs_cpu_to_le16(0);
+ info->dqi_free_blk = blk;
+ mark_quotafile_info_dirty(h);
+ write_blk(h, blk, buf);
+}
+
+/* Remove given block from the list of blocks with free entries */
+static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ unsigned int nextblk = ext2fs_le32_to_cpu(dh->dqdh_next_free), prevblk =
+
+ ext2fs_le32_to_cpu(dh->dqdh_prev_free);
+
+ if (!tmpbuf)
+ return;
+
+ if (nextblk) {
+ read_blk(h, nextblk, tmpbuf);
+ ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
+ dh->dqdh_prev_free;
+ write_blk(h, nextblk, tmpbuf);
+ }
+ if (prevblk) {
+ read_blk(h, prevblk, tmpbuf);
+ ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_next_free =
+ dh->dqdh_next_free;
+ write_blk(h, prevblk, tmpbuf);
+ } else {
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = nextblk;
+ mark_quotafile_info_dirty(h);
+ }
+ freedqbuf(tmpbuf);
+ dh->dqdh_next_free = dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
+ write_blk(h, blk, buf); /* No matter whether write succeeds
+ * block is out of list */
+}
+
+/* Insert given block to the beginning of list with free entries */
+static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf,
+ unsigned int blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ if (!tmpbuf)
+ return;
+
+ dh->dqdh_next_free = ext2fs_cpu_to_le32(info->dqi_free_entry);
+ dh->dqdh_prev_free = ext2fs_cpu_to_le32(0);
+ write_blk(h, blk, buf);
+ if (info->dqi_free_entry) {
+ read_blk(h, info->dqi_free_entry, tmpbuf);
+ ((struct qt_disk_dqdbheader *)tmpbuf)->dqdh_prev_free =
+ ext2fs_cpu_to_le32(blk);
+ write_blk(h, info->dqi_free_entry, tmpbuf);
+ }
+ freedqbuf(tmpbuf);
+ info->dqi_free_entry = blk;
+ mark_quotafile_info_dirty(h);
+}
+
+/* Find space for dquot */
+static unsigned int find_free_dqentry(struct quota_handle *h,
+ struct dquot *dquot, int *err)
+{
+ int blk, i;
+ struct qt_disk_dqdbheader *dh;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ char *ddquot;
+ dqbuf_t buf;
+
+ *err = 0;
+ buf = getdqbuf();
+ if (!buf) {
+ *err = -ENOMEM;
+ return 0;
+ }
+
+ dh = (struct qt_disk_dqdbheader *)buf;
+ if (info->dqi_free_entry) {
+ blk = info->dqi_free_entry;
+ read_blk(h, blk, buf);
+ } else {
+ blk = get_free_dqblk(h);
+ if (blk < 0) {
+ freedqbuf(buf);
+ *err = blk;
+ return 0;
+ }
+ memset(buf, 0, QT_BLKSIZE);
+ info->dqi_free_entry = blk;
+ mark_quotafile_info_dirty(h);
+ }
+
+ /* Block will be full? */
+ if (ext2fs_le16_to_cpu(dh->dqdh_entries) + 1 >=
+ qtree_dqstr_in_blk(info))
+ remove_free_dqentry(h, buf, blk);
+
+ dh->dqdh_entries =
+ ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) + 1);
+ /* Find free structure in block */
+ ddquot = buf + sizeof(struct qt_disk_dqdbheader);
+ for (i = 0;
+ i < qtree_dqstr_in_blk(info) && !qtree_entry_unused(info, ddquot);
+ i++)
+ ddquot += info->dqi_entry_size;
+
+ if (i == qtree_dqstr_in_blk(info))
+ log_err("find_free_dqentry(): Data block full unexpectedly.");
+
+ write_blk(h, blk, buf);
+ dquot->dq_dqb.u.v2_mdqb.dqb_off =
+ (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
+ i * info->dqi_entry_size;
+ freedqbuf(buf);
+ return blk;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct quota_handle *h, struct dquot *dquot,
+ unsigned int * treeblk, int depth)
+{
+ dqbuf_t buf;
+ int newson = 0, newact = 0;
+ __le32 *ref;
+ unsigned int newblk;
+ int ret = 0;
+
+ log_debug("inserting in tree: treeblk=%u, depth=%d", *treeblk, depth);
+ buf = getdqbuf();
+ if (!buf)
+ return -ENOMEM;
+
+ if (!*treeblk) {
+ ret = get_free_dqblk(h);
+ if (ret < 0)
+ goto out_buf;
+ *treeblk = ret;
+ memset(buf, 0, QT_BLKSIZE);
+ newact = 1;
+ } else {
+ read_blk(h, *treeblk, buf);
+ }
+
+ ref = (__le32 *) buf;
+ newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
+ if (!newblk)
+ newson = 1;
+ if (depth == QT_TREEDEPTH - 1) {
+ if (newblk)
+ log_err("Inserting already present quota entry "
+ "(block %u).",
+ ref[get_index(dquot->dq_id, depth)]);
+ newblk = find_free_dqentry(h, dquot, &ret);
+ } else {
+ ret = do_insert_tree(h, dquot, &newblk, depth + 1);
+ }
+
+ if (newson && ret >= 0) {
+ ref[get_index(dquot->dq_id, depth)] =
+ ext2fs_cpu_to_le32(newblk);
+ write_blk(h, *treeblk, buf);
+ } else if (newact && ret < 0) {
+ put_free_dqblk(h, buf, *treeblk);
+ }
+
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static void dq_insert_tree(struct quota_handle *h, struct dquot *dquot)
+{
+ unsigned int tmp = QT_TREEOFF;
+
+ if (do_insert_tree(h, dquot, &tmp, 0) < 0)
+ log_err("Cannot write quota (id %u): %s",
+ (unsigned int) dquot->dq_id, strerror(errno));
+}
+
+/* Write dquot to file */
+void qtree_write_dquot(struct dquot *dquot)
+{
+ errcode_t retval;
+ unsigned int ret;
+ char *ddquot;
+ struct quota_handle *h = dquot->dq_h;
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+ log_debug("writing ddquot 1: off=%llu, info->dqi_entry_size=%u",
+ dquot->dq_dqb.u.v2_mdqb.dqb_off,
+ info->dqi_entry_size);
+ retval = ext2fs_get_mem(info->dqi_entry_size, &ddquot);
+ if (retval) {
+ errno = ENOMEM;
+ log_err("Quota write failed (id %u): %s",
+ (unsigned int)dquot->dq_id, strerror(errno));
+ return;
+ }
+ memset(ddquot, 0, info->dqi_entry_size);
+
+ if (!dquot->dq_dqb.u.v2_mdqb.dqb_off)
+ dq_insert_tree(dquot->dq_h, dquot);
+ info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
+ log_debug("writing ddquot 2: off=%llu, info->dqi_entry_size=%u",
+ dquot->dq_dqb.u.v2_mdqb.dqb_off,
+ info->dqi_entry_size);
+ ret = h->e2fs_write(&h->qh_qf, dquot->dq_dqb.u.v2_mdqb.dqb_off, ddquot,
+ info->dqi_entry_size);
+
+ if (ret != info->dqi_entry_size) {
+ if (ret > 0)
+ errno = ENOSPC;
+ log_err("Quota write failed (id %u): %s",
+ (unsigned int)dquot->dq_id, strerror(errno));
+ }
+ ext2fs_free_mem(&ddquot);
+}
+
+/* Free dquot entry in data block */
+static void free_dqentry(struct quota_handle *h, struct dquot *dquot,
+ unsigned int blk)
+{
+ struct qt_disk_dqdbheader *dh;
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ dqbuf_t buf = getdqbuf();
+
+ if (!buf)
+ return;
+
+ if (dquot->dq_dqb.u.v2_mdqb.dqb_off >> QT_BLKSIZE_BITS != blk)
+ log_err("Quota structure has offset to other block (%u) "
+ "than it should (%u).", blk,
+ (unsigned int) (dquot->dq_dqb.u.v2_mdqb.dqb_off >>
+ QT_BLKSIZE_BITS));
+
+ read_blk(h, blk, buf);
+ dh = (struct qt_disk_dqdbheader *)buf;
+ dh->dqdh_entries =
+ ext2fs_cpu_to_le16(ext2fs_le16_to_cpu(dh->dqdh_entries) - 1);
+
+ if (!ext2fs_le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
+ remove_free_dqentry(h, buf, blk);
+ put_free_dqblk(h, buf, blk);
+ } else {
+ memset(buf + (dquot->dq_dqb.u.v2_mdqb.dqb_off &
+ ((1 << QT_BLKSIZE_BITS) - 1)),
+ 0, info->dqi_entry_size);
+
+ /* First free entry? */
+ if (ext2fs_le16_to_cpu(dh->dqdh_entries) ==
+ qtree_dqstr_in_blk(info) - 1)
+ /* This will also write data block */
+ insert_free_dqentry(h, buf, blk);
+ else
+ write_blk(h, blk, buf);
+ }
+ dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
+ freedqbuf(buf);
+}
+
+/* Remove reference to dquot from tree */
+static void remove_tree(struct quota_handle *h, struct dquot *dquot,
+ unsigned int * blk, int depth)
+{
+ dqbuf_t buf = getdqbuf();
+ unsigned int newblk;
+ __le32 *ref = (__le32 *) buf;
+
+ if (!buf)
+ return;
+
+ read_blk(h, *blk, buf);
+ newblk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
+ if (depth == QT_TREEDEPTH - 1) {
+ free_dqentry(h, dquot, newblk);
+ newblk = 0;
+ } else {
+ remove_tree(h, dquot, &newblk, depth + 1);
+ }
+
+ if (!newblk) {
+ int i;
+
+ ref[get_index(dquot->dq_id, depth)] = ext2fs_cpu_to_le32(0);
+
+ /* Block got empty? */
+ for (i = 0; i < QT_BLKSIZE && !buf[i]; i++);
+
+ /* Don't put the root block into the free block list */
+ if (i == QT_BLKSIZE && *blk != QT_TREEOFF) {
+ put_free_dqblk(h, buf, *blk);
+ *blk = 0;
+ } else {
+ write_blk(h, *blk, buf);
+ }
+ }
+ freedqbuf(buf);
+}
+
+/* Delete dquot from tree */
+void qtree_delete_dquot(struct dquot *dquot)
+{
+ unsigned int tmp = QT_TREEOFF;
+
+ if (!dquot->dq_dqb.u.v2_mdqb.dqb_off) /* Even not allocated? */
+ return;
+ remove_tree(dquot->dq_h, dquot, &tmp, 0);
+}
+
+/* Find entry in block */
+static ext2_loff_t find_block_dqentry(struct quota_handle *h,
+ struct dquot *dquot, unsigned int blk)
+{
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ dqbuf_t buf = getdqbuf();
+ int i;
+ char *ddquot = buf + sizeof(struct qt_disk_dqdbheader);
+
+ if (!buf)
+ return -ENOMEM;
+
+ read_blk(h, blk, buf);
+ for (i = 0;
+ i < qtree_dqstr_in_blk(info) && !info->dqi_ops->is_id(ddquot, dquot);
+ i++)
+ ddquot += info->dqi_entry_size;
+
+ if (i == qtree_dqstr_in_blk(info))
+ log_err("Quota for id %u referenced but not present.",
+ dquot->dq_id);
+ freedqbuf(buf);
+ return (blk << QT_BLKSIZE_BITS) + sizeof(struct qt_disk_dqdbheader) +
+ i * info->dqi_entry_size;
+}
+
+/* Find entry for given id in the tree */
+static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
+ struct dquot *dquot,
+ unsigned int blk, int depth)
+{
+ dqbuf_t buf = getdqbuf();
+ ext2_loff_t ret = 0;
+ __le32 *ref = (__le32 *) buf;
+
+ if (!buf)
+ return -ENOMEM;
+
+ read_blk(h, blk, buf);
+ ret = 0;
+ blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
+ if (!blk) /* No reference? */
+ goto out_buf;
+ if (depth < QT_TREEDEPTH - 1)
+ ret = find_tree_dqentry(h, dquot, blk, depth + 1);
+ else
+ ret = find_block_dqentry(h, dquot, blk);
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline ext2_loff_t find_dqentry(struct quota_handle *h,
+ struct dquot *dquot)
+{
+ return find_tree_dqentry(h, dquot, QT_TREEOFF, 0);
+}
+
+/*
+ * Read dquot from disk.
+ */
+struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id)
+{
+ struct qtree_mem_dqinfo *info = &h->qh_info.u.v2_mdqi.dqi_qtree;
+ ext2_loff_t offset;
+ unsigned int ret;
+ char *ddquot;
+ struct dquot *dquot = get_empty_dquot();
+
+ if (!dquot)
+ return NULL;
+ if (ext2fs_get_mem(info->dqi_entry_size, &ddquot)) {
+ ext2fs_free_mem(&dquot);
+ return NULL;
+ }
+
+ dquot->dq_id = id;
+ dquot->dq_h = h;
+ dquot->dq_dqb.u.v2_mdqb.dqb_off = 0;
+ memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
+
+ offset = find_dqentry(h, dquot);
+ if (offset > 0) {
+ dquot->dq_dqb.u.v2_mdqb.dqb_off = offset;
+ ret = h->e2fs_read(&h->qh_qf, offset, ddquot,
+ info->dqi_entry_size);
+ if (ret != info->dqi_entry_size) {
+ if (ret > 0)
+ errno = EIO;
+ log_err("Cannot read quota structure for id %u: %s",
+ dquot->dq_id, strerror(errno));
+ }
+ info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
+ }
+ ext2fs_free_mem(&ddquot);
+ return dquot;
+}
+
+static int check_reference(struct quota_handle *h, unsigned int blk)
+{
+ if (blk >= h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks) {
+ log_err("Illegal reference (%u >= %u) in %s quota file",
+ blk, h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks,
+ quota_type2name(h->qh_type));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Scan all dquots in file and call callback on each
+ */
+#define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
+#define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
+
+static int report_block(struct dquot *dquot, unsigned int blk, char *bitmap,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+ dqbuf_t buf = getdqbuf();
+ struct qt_disk_dqdbheader *dh;
+ char *ddata;
+ int entries, i;
+
+ if (!buf)
+ return -1;
+
+ set_bit(bitmap, blk);
+ read_blk(dquot->dq_h, blk, buf);
+ dh = (struct qt_disk_dqdbheader *)buf;
+ ddata = buf + sizeof(struct qt_disk_dqdbheader);
+ entries = ext2fs_le16_to_cpu(dh->dqdh_entries);
+ for (i = 0; i < qtree_dqstr_in_blk(info);
+ i++, ddata += info->dqi_entry_size)
+ if (!qtree_entry_unused(info, ddata)) {
+ dquot->dq_dqb.u.v2_mdqb.dqb_off =
+ (blk << QT_BLKSIZE_BITS) +
+ sizeof(struct qt_disk_dqdbheader) +
+ i * info->dqi_entry_size;
+ info->dqi_ops->disk2mem_dqblk(dquot, ddata);
+ if (process_dquot(dquot, data) < 0)
+ break;
+ }
+ freedqbuf(buf);
+ return entries;
+}
+
+static int report_tree(struct dquot *dquot, unsigned int blk, int depth,
+ char *bitmap,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ int entries = 0, ret, i;
+ dqbuf_t buf = getdqbuf();
+ __le32 *ref = (__le32 *) buf;
+
+ if (!buf)
+ return -1;
+
+ read_blk(dquot->dq_h, blk, buf);
+ if (depth == QT_TREEDEPTH - 1) {
+ for (i = 0; i < QT_BLKSIZE >> 2; i++) {
+ blk = ext2fs_le32_to_cpu(ref[i]);
+ if (check_reference(dquot->dq_h, blk)) {
+ entries = -1;
+ goto errout;
+ }
+ if (blk && !get_bit(bitmap, blk)) {
+ ret = report_block(dquot, blk, bitmap,
+ process_dquot, data);
+ if (ret < 0) {
+ entries = ret;
+ goto errout;
+ }
+ entries += ret;
+ }
+ }
+ } else {
+ for (i = 0; i < QT_BLKSIZE >> 2; i++) {
+ blk = ext2fs_le32_to_cpu(ref[i]);
+ if (blk) {
+ if (check_reference(dquot->dq_h, blk)) {
+ entries = -1;
+ goto errout;
+ }
+ ret = report_tree(dquot, blk, depth + 1,
+ bitmap, process_dquot,
+ data);
+ if (ret < 0) {
+ entries = ret;
+ goto errout;
+ }
+ entries += ret;
+ }
+ }
+ }
+errout:
+ freedqbuf(buf);
+ return entries;
+}
+
+static unsigned int find_set_bits(char *bmp, int blocks)
+{
+ unsigned int used = 0;
+ int i;
+
+ for (i = 0; i < blocks; i++)
+ if (get_bit(bmp, i))
+ used++;
+ return used;
+}
+
+int qtree_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ int ret;
+ char *bitmap;
+ struct v2_mem_dqinfo *v2info = &h->qh_info.u.v2_mdqi;
+ struct qtree_mem_dqinfo *info = &v2info->dqi_qtree;
+ struct dquot *dquot = get_empty_dquot();
+
+ if (!dquot)
+ return -1;
+
+ dquot->dq_h = h;
+ if (ext2fs_get_memzero((info->dqi_blocks + 7) >> 3, &bitmap)) {
+ ext2fs_free_mem(&dquot);
+ return -1;
+ }
+ ret = report_tree(dquot, QT_TREEOFF, 0, bitmap, process_dquot, data);
+ if (ret < 0)
+ goto errout;
+ v2info->dqi_used_entries = ret;
+ v2info->dqi_data_blocks = find_set_bits(bitmap, info->dqi_blocks);
+ ret = 0;
+errout:
+ ext2fs_free_mem(&bitmap);
+ ext2fs_free_mem(&dquot);
+ return ret;
+}
diff --git a/lib/support/quotaio_tree.h b/lib/support/quotaio_tree.h
new file mode 100644
index 0000000..b0b7257
--- /dev/null
+++ b/lib/support/quotaio_tree.h
@@ -0,0 +1,64 @@
+/*
+ * Definitions of structures for vfsv0 quota format
+ */
+
+#ifndef _LINUX_QUOTA_TREE_H
+#define _LINUX_QUOTA_TREE_H
+
+#include <sys/types.h>
+
+typedef __u32 qid_t; /* Type in which we store ids in memory */
+
+#define QT_TREEOFF 1 /* Offset of tree in file in blocks */
+#define QT_TREEDEPTH 4 /* Depth of quota tree */
+#define QT_BLKSIZE_BITS 10
+#define QT_BLKSIZE (1 << QT_BLKSIZE_BITS) /* Size of block with quota
+ * structures */
+
+/*
+ * Structure of header of block with quota structures. It is padded to 16 bytes
+ * so there will be space for exactly 21 quota-entries in a block
+ */
+struct qt_disk_dqdbheader {
+ __le32 dqdh_next_free; /* Number of next block with free
+ * entry */
+ __le32 dqdh_prev_free; /* Number of previous block with free
+ * entry */
+ __le16 dqdh_entries; /* Number of valid entries in block */
+ __le16 dqdh_pad1;
+ __le32 dqdh_pad2;
+} __attribute__ ((packed));
+
+struct dquot;
+struct quota_handle;
+
+/* Operations */
+struct qtree_fmt_operations {
+ /* Convert given entry from in memory format to disk one */
+ void (*mem2disk_dqblk)(void *disk, struct dquot *dquot);
+ /* Convert given entry from disk format to in memory one */
+ void (*disk2mem_dqblk)(struct dquot *dquot, void *disk);
+ /* Is this structure for given id? */
+ int (*is_id)(void *disk, struct dquot *dquot);
+};
+
+/* In-memory copy of version specific information */
+struct qtree_mem_dqinfo {
+ unsigned int dqi_blocks; /* # of blocks in quota file */
+ unsigned int dqi_free_blk; /* First block in list of free blocks */
+ unsigned int dqi_free_entry; /* First block with free entry */
+ unsigned int dqi_entry_size; /* Size of quota entry in quota file */
+ struct qtree_fmt_operations *dqi_ops; /* Operations for entry
+ * manipulation */
+};
+
+void qtree_write_dquot(struct dquot *dquot);
+struct dquot *qtree_read_dquot(struct quota_handle *h, qid_t id);
+void qtree_delete_dquot(struct dquot *dquot);
+int qtree_entry_unused(struct qtree_mem_dqinfo *info, char *disk);
+int qtree_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *, void *), void *data);
+
+int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info);
+
+#endif /* _LINUX_QUOTAIO_TREE_H */
diff --git a/lib/support/quotaio_v2.c b/lib/support/quotaio_v2.c
new file mode 100644
index 0000000..d09294b
--- /dev/null
+++ b/lib/support/quotaio_v2.c
@@ -0,0 +1,389 @@
+/*
+ * Implementation of new quotafile format
+ *
+ * Jan Kara <jack@suse.cz> - sponsored by SuSE CR
+ */
+
+#include "config.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "quotaio_v2.h"
+#include "dqblk_v2.h"
+#include "quotaio.h"
+#include "quotaio_tree.h"
+
+static int v2_check_file(struct quota_handle *h, int type, int fmt);
+static int v2_init_io(struct quota_handle *h);
+static int v2_new_io(struct quota_handle *h);
+static int v2_write_info(struct quota_handle *h);
+static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id);
+static int v2_commit_dquot(struct dquot *dquot);
+static int v2_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *dquot,
+ void *data),
+ void *data);
+static int v2_report(struct quota_handle *h, int verbose);
+
+struct quotafile_ops quotafile_ops_2 = {
+ .check_file = v2_check_file,
+ .init_io = v2_init_io,
+ .new_io = v2_new_io,
+ .write_info = v2_write_info,
+ .read_dquot = v2_read_dquot,
+ .commit_dquot = v2_commit_dquot,
+ .scan_dquots = v2_scan_dquots,
+ .report = v2_report,
+};
+
+/*
+ * Copy dquot from disk to memory
+ */
+static void v2r0_disk2memdqblk(struct dquot *dquot, void *dp)
+{
+ struct util_dqblk *m = &dquot->dq_dqb;
+ struct v2r0_disk_dqblk *d = dp, empty;
+
+ dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id);
+ m->dqb_ihardlimit = ext2fs_le32_to_cpu(d->dqb_ihardlimit);
+ m->dqb_isoftlimit = ext2fs_le32_to_cpu(d->dqb_isoftlimit);
+ m->dqb_bhardlimit = ext2fs_le32_to_cpu(d->dqb_bhardlimit);
+ m->dqb_bsoftlimit = ext2fs_le32_to_cpu(d->dqb_bsoftlimit);
+ m->dqb_curinodes = ext2fs_le32_to_cpu(d->dqb_curinodes);
+ m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace);
+ m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime);
+ m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime);
+
+ memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
+ empty.dqb_itime = ext2fs_cpu_to_le64(1);
+ if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
+ m->dqb_itime = 0;
+}
+
+/*
+ * Copy dquot from memory to disk
+ */
+static void v2r0_mem2diskdqblk(void *dp, struct dquot *dquot)
+{
+ struct util_dqblk *m = &dquot->dq_dqb;
+ struct v2r0_disk_dqblk *d = dp;
+
+ d->dqb_ihardlimit = ext2fs_cpu_to_le32(m->dqb_ihardlimit);
+ d->dqb_isoftlimit = ext2fs_cpu_to_le32(m->dqb_isoftlimit);
+ d->dqb_bhardlimit = ext2fs_cpu_to_le32(m->dqb_bhardlimit);
+ d->dqb_bsoftlimit = ext2fs_cpu_to_le32(m->dqb_bsoftlimit);
+ d->dqb_curinodes = ext2fs_cpu_to_le32(m->dqb_curinodes);
+ d->dqb_curspace = ext2fs_cpu_to_le64(m->dqb_curspace);
+ d->dqb_itime = ext2fs_cpu_to_le64(m->dqb_itime);
+ d->dqb_btime = ext2fs_cpu_to_le64(m->dqb_btime);
+ d->dqb_id = ext2fs_cpu_to_le32(dquot->dq_id);
+ if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
+ d->dqb_itime = ext2fs_cpu_to_le64(1);
+}
+
+static int v2r0_is_id(void *dp, struct dquot *dquot)
+{
+ struct v2r0_disk_dqblk *d = dp;
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ if (qtree_entry_unused(info, dp))
+ return 0;
+ return ext2fs_le32_to_cpu(d->dqb_id) == dquot->dq_id;
+}
+
+static struct qtree_fmt_operations v2r0_fmt_ops = {
+ .mem2disk_dqblk = v2r0_mem2diskdqblk,
+ .disk2mem_dqblk = v2r0_disk2memdqblk,
+ .is_id = v2r0_is_id,
+};
+
+/*
+ * Copy dquot from disk to memory
+ */
+static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
+{
+ struct util_dqblk *m = &dquot->dq_dqb;
+ struct v2r1_disk_dqblk *d = dp, empty;
+
+ dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id);
+ m->dqb_ihardlimit = ext2fs_le64_to_cpu(d->dqb_ihardlimit);
+ m->dqb_isoftlimit = ext2fs_le64_to_cpu(d->dqb_isoftlimit);
+ m->dqb_bhardlimit = ext2fs_le64_to_cpu(d->dqb_bhardlimit);
+ m->dqb_bsoftlimit = ext2fs_le64_to_cpu(d->dqb_bsoftlimit);
+ m->dqb_curinodes = ext2fs_le64_to_cpu(d->dqb_curinodes);
+ m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace);
+ m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime);
+ m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime);
+
+ memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
+ empty.dqb_itime = ext2fs_cpu_to_le64(1);
+ if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
+ m->dqb_itime = 0;
+}
+
+/*
+ * Copy dquot from memory to disk
+ */
+static void v2r1_mem2diskdqblk(void *dp, struct dquot *dquot)
+{
+ struct util_dqblk *m = &dquot->dq_dqb;
+ struct v2r1_disk_dqblk *d = dp;
+
+ d->dqb_ihardlimit = ext2fs_cpu_to_le64(m->dqb_ihardlimit);
+ d->dqb_isoftlimit = ext2fs_cpu_to_le64(m->dqb_isoftlimit);
+ d->dqb_bhardlimit = ext2fs_cpu_to_le64(m->dqb_bhardlimit);
+ d->dqb_bsoftlimit = ext2fs_cpu_to_le64(m->dqb_bsoftlimit);
+ d->dqb_curinodes = ext2fs_cpu_to_le64(m->dqb_curinodes);
+ d->dqb_curspace = ext2fs_cpu_to_le64(m->dqb_curspace);
+ d->dqb_itime = ext2fs_cpu_to_le64(m->dqb_itime);
+ d->dqb_btime = ext2fs_cpu_to_le64(m->dqb_btime);
+ d->dqb_id = ext2fs_cpu_to_le32(dquot->dq_id);
+ if (qtree_entry_unused(&dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree, dp))
+ d->dqb_itime = ext2fs_cpu_to_le64(1);
+}
+
+static int v2r1_is_id(void *dp, struct dquot *dquot)
+{
+ struct v2r1_disk_dqblk *d = dp;
+ struct qtree_mem_dqinfo *info =
+ &dquot->dq_h->qh_info.u.v2_mdqi.dqi_qtree;
+
+ if (qtree_entry_unused(info, dp))
+ return 0;
+ return ext2fs_le32_to_cpu(d->dqb_id) == dquot->dq_id;
+}
+
+static struct qtree_fmt_operations v2r1_fmt_ops = {
+ .mem2disk_dqblk = v2r1_mem2diskdqblk,
+ .disk2mem_dqblk = v2r1_disk2memdqblk,
+ .is_id = v2r1_is_id,
+};
+
+/*
+ * Copy dqinfo from disk to memory
+ */
+static inline void v2_disk2memdqinfo(struct util_dqinfo *m,
+ struct v2_disk_dqinfo *d)
+{
+ m->dqi_bgrace = ext2fs_le32_to_cpu(d->dqi_bgrace);
+ m->dqi_igrace = ext2fs_le32_to_cpu(d->dqi_igrace);
+ m->u.v2_mdqi.dqi_flags = ext2fs_le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
+ m->u.v2_mdqi.dqi_qtree.dqi_blocks = ext2fs_le32_to_cpu(d->dqi_blocks);
+ m->u.v2_mdqi.dqi_qtree.dqi_free_blk =
+ ext2fs_le32_to_cpu(d->dqi_free_blk);
+ m->u.v2_mdqi.dqi_qtree.dqi_free_entry =
+ ext2fs_le32_to_cpu(d->dqi_free_entry);
+}
+
+/*
+ * Copy dqinfo from memory to disk
+ */
+static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d,
+ struct util_dqinfo *m)
+{
+ d->dqi_bgrace = ext2fs_cpu_to_le32(m->dqi_bgrace);
+ d->dqi_igrace = ext2fs_cpu_to_le32(m->dqi_igrace);
+ d->dqi_flags = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
+ d->dqi_blocks = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
+ d->dqi_free_blk =
+ ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
+ d->dqi_free_entry =
+ ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
+}
+
+static int v2_read_header(struct quota_handle *h, struct v2_disk_dqheader *dqh)
+{
+ if (h->e2fs_read(&h->qh_qf, 0, dqh, sizeof(struct v2_disk_dqheader)) !=
+ sizeof(struct v2_disk_dqheader))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Check whether given quota file is in our format
+ */
+static int v2_check_file(struct quota_handle *h, int type, int fmt)
+{
+ struct v2_disk_dqheader dqh;
+ int file_magics[] = INITQMAGICS;
+ int be_magic;
+
+ if (fmt != QFMT_VFS_V1)
+ return 0;
+
+ if (!v2_read_header(h, &dqh))
+ return 0;
+
+ be_magic = ext2fs_be32_to_cpu((__force __be32)dqh.dqh_magic);
+ if (be_magic == file_magics[type]) {
+ log_err("Your quota file is stored in wrong endianness");
+ return 0;
+ }
+ if (V2_VERSION_R0 != ext2fs_le32_to_cpu(dqh.dqh_version) &&
+ V2_VERSION_R1 != ext2fs_le32_to_cpu(dqh.dqh_version))
+ return 0;
+ return 1;
+}
+
+/*
+ * Open quotafile
+ */
+static int v2_init_io(struct quota_handle *h)
+{
+ struct v2_disk_dqheader dqh;
+ struct v2_disk_dqinfo ddqinfo;
+ struct v2_mem_dqinfo *info;
+ __u64 filesize;
+ int version;
+
+ if (!v2_read_header(h, &dqh))
+ return -1;
+ version = ext2fs_le32_to_cpu(dqh.dqh_version);
+
+ if (version == V2_VERSION_R0) {
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
+ sizeof(struct v2r0_disk_dqblk);
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r0_fmt_ops;
+ } else {
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
+ sizeof(struct v2r1_disk_dqblk);
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
+ }
+
+ /* Read information about quotafile */
+ if (h->e2fs_read(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
+ sizeof(ddqinfo)) != sizeof(ddqinfo))
+ return -1;
+ v2_disk2memdqinfo(&h->qh_info, &ddqinfo);
+
+ /* Check to make sure quota file info is sane */
+ info = &h->qh_info.u.v2_mdqi;
+ if (ext2fs_file_get_lsize(h->qh_qf.e2_file, &filesize))
+ return -1;
+ if ((filesize > (1U << 31)) ||
+ (info->dqi_qtree.dqi_blocks >
+ (filesize + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS)) {
+ log_err("Quota inode %u corrupted: file size %llu; "
+ "dqi_blocks %u", h->qh_qf.ino,
+ (unsigned long long) filesize,
+ info->dqi_qtree.dqi_blocks);
+ return -1;
+ }
+ if (info->dqi_qtree.dqi_free_blk >= info->dqi_qtree.dqi_blocks) {
+ log_err("Quota inode %u corrupted: free_blk %u; dqi_blocks %u",
+ h->qh_qf.ino, info->dqi_qtree.dqi_free_blk,
+ info->dqi_qtree.dqi_blocks);
+ return -1;
+ }
+ if (info->dqi_qtree.dqi_free_entry >= info->dqi_qtree.dqi_blocks) {
+ log_err("Quota inode %u corrupted: free_entry %u; "
+ "dqi_blocks %u", h->qh_qf.ino,
+ info->dqi_qtree.dqi_free_entry,
+ info->dqi_qtree.dqi_blocks);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Initialize new quotafile
+ */
+static int v2_new_io(struct quota_handle *h)
+{
+ int file_magics[] = INITQMAGICS;
+ struct v2_disk_dqheader ddqheader;
+ struct v2_disk_dqinfo ddqinfo;
+
+ if (h->qh_fmt != QFMT_VFS_V1)
+ return -1;
+
+ /* Write basic quota header */
+ ddqheader.dqh_magic = ext2fs_cpu_to_le32(file_magics[h->qh_type]);
+ ddqheader.dqh_version = ext2fs_cpu_to_le32(V2_VERSION_R1);
+ if (h->e2fs_write(&h->qh_qf, 0, &ddqheader, sizeof(ddqheader)) !=
+ sizeof(ddqheader))
+ return -1;
+
+ /* Write information about quotafile */
+ h->qh_info.dqi_bgrace = MAX_DQ_TIME;
+ h->qh_info.dqi_igrace = MAX_IQ_TIME;
+ h->qh_info.u.v2_mdqi.dqi_flags = 0;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_blocks = QT_TREEOFF + 1;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_blk = 0;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_free_entry = 0;
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_entry_size =
+ sizeof(struct v2r1_disk_dqblk);
+ h->qh_info.u.v2_mdqi.dqi_qtree.dqi_ops = &v2r1_fmt_ops;
+ v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
+ if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo,
+ sizeof(ddqinfo)) !=
+ sizeof(ddqinfo))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Write information (grace times to file)
+ */
+static int v2_write_info(struct quota_handle *h)
+{
+ struct v2_disk_dqinfo ddqinfo;
+
+ v2_mem2diskdqinfo(&ddqinfo, &h->qh_info);
+ if (h->e2fs_write(&h->qh_qf, V2_DQINFOOFF, &ddqinfo, sizeof(ddqinfo)) !=
+ sizeof(ddqinfo))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Read dquot from disk
+ */
+static struct dquot *v2_read_dquot(struct quota_handle *h, qid_t id)
+{
+ return qtree_read_dquot(h, id);
+}
+
+/*
+ * Commit changes of dquot to disk - it might also mean deleting it when quota
+ * became fake one and user has no blocks.
+ * User can process use 'errno' to detect errstr.
+ */
+static int v2_commit_dquot(struct dquot *dquot)
+{
+ struct util_dqblk *b = &dquot->dq_dqb;
+
+ if (!b->dqb_curspace && !b->dqb_curinodes && !b->dqb_bsoftlimit &&
+ !b->dqb_isoftlimit && !b->dqb_bhardlimit && !b->dqb_ihardlimit)
+ qtree_delete_dquot(dquot);
+ else
+ qtree_write_dquot(dquot);
+ return 0;
+}
+
+static int v2_scan_dquots(struct quota_handle *h,
+ int (*process_dquot) (struct dquot *, void *),
+ void *data)
+{
+ return qtree_scan_dquots(h, process_dquot, data);
+}
+
+/* Report information about quotafile.
+ * TODO: Not used right now, but we should be able to use this when we add
+ * support to debugfs to read quota files.
+ */
+static int v2_report(struct quota_handle *h EXT2FS_ATTR((unused)),
+ int verbose EXT2FS_ATTR((unused)))
+{
+ log_err("Not Implemented.");
+ return -1;
+}
diff --git a/lib/support/quotaio_v2.h b/lib/support/quotaio_v2.h
new file mode 100644
index 0000000..35054ca
--- /dev/null
+++ b/lib/support/quotaio_v2.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * Header file for disk format of new quotafile format
+ *
+ */
+
+#ifndef GUARD_QUOTAIO_V2_H
+#define GUARD_QUOTAIO_V2_H
+
+#include <sys/types.h>
+#include "quotaio.h"
+
+/* Offset of info header in file */
+#define V2_DQINFOOFF sizeof(struct v2_disk_dqheader)
+/* Supported version of quota-tree format */
+#define V2_VERSION_R1 1
+#define V2_VERSION_R0 0
+
+struct v2_disk_dqheader {
+ __le32 dqh_magic; /* Magic number identifying file */
+ __le32 dqh_version; /* File version */
+} __attribute__ ((packed));
+
+/* Flags for version specific files */
+#define V2_DQF_MASK 0x0000 /* Mask for all valid ondisk flags */
+
+/* Header with type and version specific information */
+struct v2_disk_dqinfo {
+ __le32 dqi_bgrace; /* Time before block soft limit becomes
+ * hard limit */
+ __le32 dqi_igrace; /* Time before inode soft limit becomes
+ * hard limit */
+ __le32 dqi_flags; /* Flags for quotafile (DQF_*) */
+ __le32 dqi_blocks; /* Number of blocks in file */
+ __le32 dqi_free_blk; /* Number of first free block in the list */
+ __le32 dqi_free_entry; /* Number of block with at least one
+ * free entry */
+} __attribute__ ((packed));
+
+struct v2r0_disk_dqblk {
+ __le32 dqb_id; /* id this quota applies to */
+ __le32 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __le32 dqb_isoftlimit; /* preferred inode limit */
+ __le32 dqb_curinodes; /* current # allocated inodes */
+ __le32 dqb_bhardlimit; /* absolute limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le32 dqb_bsoftlimit; /* preferred limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_curspace; /* current space occupied (in bytes) */
+ __le64 dqb_btime; /* time limit for excessive disk use */
+ __le64 dqb_itime; /* time limit for excessive inode use */
+} __attribute__ ((packed));
+
+struct v2r1_disk_dqblk {
+ __le32 dqb_id; /* id this quota applies to */
+ __le32 dqb_pad;
+ __le64 dqb_ihardlimit; /* absolute limit on allocated inodes */
+ __le64 dqb_isoftlimit; /* preferred inode limit */
+ __le64 dqb_curinodes; /* current # allocated inodes */
+ __le64 dqb_bhardlimit; /* absolute limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_bsoftlimit; /* preferred limit on disk space
+ * (in QUOTABLOCK_SIZE) */
+ __le64 dqb_curspace; /* current space occupied (in bytes) */
+ __le64 dqb_btime; /* time limit for excessive disk use */
+ __le64 dqb_itime; /* time limit for excessive inode use */
+} __attribute__ ((packed));
+
+#endif
diff --git a/lib/support/sort_r.h b/lib/support/sort_r.h
new file mode 100644
index 0000000..8473ca8
--- /dev/null
+++ b/lib/support/sort_r.h
@@ -0,0 +1,325 @@
+/* Isaac Turner 29 April 2014 Public Domain */
+#ifndef SORT_R_H_
+#define SORT_R_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+
+sort_r function to be exported.
+
+Parameters:
+ base is the array to be sorted
+ nel is the number of elements in the array
+ width is the size in bytes of each element of the array
+ compar is the comparison function
+ arg is a pointer to be passed to the comparison function
+
+void sort_r(void *base, size_t nel, size_t width,
+ int (*compar)(const void *_a, const void *_b, void *_arg),
+ void *arg);
+
+*/
+
+#define _SORT_R_INLINE inline
+
+#if (defined HAVE_GNU_QSORT_R)
+# define _SORT_R_GNU
+#elif (defined HAVE_BSD_QSORT_R)
+# define _SORT_R_BSD
+#elif (defined __gnu_hurd__ || defined __GNU__ || \
+ defined __MINGW32__ || defined __GLIBC__)
+# define _SORT_R_GNU
+#elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
+ defined __FreeBSD__ || defined __DragonFly__)
+# define _SORT_R_BSD
+#elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)
+# define _SORT_R_WINDOWS
+# undef _SORT_R_INLINE
+# define _SORT_R_INLINE __inline
+#else
+ /* Using our own recursive quicksort sort_r_simple() */
+#endif
+
+#if (defined NESTED_QSORT && NESTED_QSORT == 0)
+# undef NESTED_QSORT
+#endif
+
+#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
+
+/* swap a and b */
+/* a and b must not be equal! */
+static _SORT_R_INLINE void sort_r_swap(char *__restrict a, char *__restrict b,
+ size_t w)
+{
+ char tmp, *end = a+w;
+ for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); }
+}
+
+/* swap a, b iff a>b */
+/* a and b must not be equal! */
+/* __restrict is same as restrict but better support on old machines */
+static _SORT_R_INLINE int sort_r_cmpswap(char *__restrict a,
+ char *__restrict b, size_t w,
+ int (*compar)(const void *_a,
+ const void *_b,
+ void *_arg),
+ void *arg)
+{
+ if(compar(a, b, arg) > 0) {
+ sort_r_swap(a, b, w);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr,
+with the smallest swap so that the blocks are in the opposite order. Blocks may
+be internally re-ordered e.g.
+
+ 12345ab -> ab34512
+ 123abc -> abc123
+ 12abcde -> deabc12
+*/
+static _SORT_R_INLINE void sort_r_swap_blocks(char *ptr, size_t na, size_t nb)
+{
+ if(na > 0 && nb > 0) {
+ if(na > nb) { sort_r_swap(ptr, ptr+na, nb); }
+ else { sort_r_swap(ptr, ptr+nb, na); }
+ }
+}
+
+/* Implement recursive quicksort ourselves */
+/* Note: quicksort is not stable, equivalent values may be swapped */
+static _SORT_R_INLINE void sort_r_simple(void *base, size_t nel, size_t w,
+ int (*compar)(const void *_a,
+ const void *_b,
+ void *_arg),
+ void *arg)
+{
+ char *b = (char *)base, *end = b + nel*w;
+
+ /* for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
+ printf("\n"); */
+
+ if(nel < 10) {
+ /* Insertion sort for arbitrarily small inputs */
+ char *pi, *pj;
+ for(pi = b+w; pi < end; pi += w) {
+ for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {}
+ }
+ }
+ else
+ {
+ /* nel > 6; Quicksort */
+
+ int cmp;
+ char *pl, *ple, *pr, *pre, *pivot;
+ char *last = b+w*(nel-1), *tmp;
+
+ /*
+ Use median of second, middle and second-last items as pivot.
+ First and last may have been swapped with pivot and therefore be extreme
+ */
+ char *l[3];
+ l[0] = b + w;
+ l[1] = b+w*(nel/2);
+ l[2] = last - w;
+
+ /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */
+
+ if(compar(l[0],l[1],arg) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
+ if(compar(l[1],l[2],arg) > 0) {
+ SORT_R_SWAP(l[1], l[2], tmp);
+ if(compar(l[0],l[1],arg) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
+ }
+
+ /* swap mid value (l[1]), and last element to put pivot as last element */
+ if(l[1] != last) { sort_r_swap(l[1], last, w); }
+
+ /*
+ pl is the next item on the left to be compared to the pivot
+ pr is the last item on the right that was compared to the pivot
+ ple is the left position to put the next item that equals the pivot
+ ple is the last right position where we put an item that equals the pivot
+
+ v- end (beyond the array)
+ EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE.
+ ^- b ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is)
+
+ Pivot comparison key:
+ E = equal, L = less than, u = unknown, G = greater than, E = equal
+ */
+ pivot = last;
+ ple = pl = b;
+ pre = pr = last;
+
+ /*
+ Strategy:
+ Loop into the list from the left and right at the same time to find:
+ - an item on the left that is greater than the pivot
+ - an item on the right that is less than the pivot
+ Once found, they are swapped and the loop continues.
+ Meanwhile items that are equal to the pivot are moved to the edges of the
+ array.
+ */
+ while(pl < pr) {
+ /* Move left hand items which are equal to the pivot to the far left.
+ break when we find an item that is greater than the pivot */
+ for(; pl < pr; pl += w) {
+ cmp = compar(pl, pivot, arg);
+ if(cmp > 0) { break; }
+ else if(cmp == 0) {
+ if(ple < pl) { sort_r_swap(ple, pl, w); }
+ ple += w;
+ }
+ }
+ /* break if last batch of left hand items were equal to pivot */
+ if(pl >= pr) { break; }
+ /* Move right hand items which are equal to the pivot to the far right.
+ break when we find an item that is less than the pivot */
+ for(; pl < pr; ) {
+ pr -= w; /* Move right pointer onto an unprocessed item */
+ cmp = compar(pr, pivot, arg);
+ if(cmp == 0) {
+ pre -= w;
+ if(pr < pre) { sort_r_swap(pr, pre, w); }
+ }
+ else if(cmp < 0) {
+ if(pl < pr) { sort_r_swap(pl, pr, w); }
+ pl += w;
+ break;
+ }
+ }
+ }
+
+ pl = pr; /* pr may have gone below pl */
+
+ /*
+ Now we need to go from: EEELLLGGGGEEEE
+ to: LLLEEEEEEEGGGG
+
+ Pivot comparison key:
+ E = equal, L = less than, u = unknown, G = greater than, E = equal
+ */
+ sort_r_swap_blocks(b, ple-b, pl-ple);
+ sort_r_swap_blocks(pr, pre-pr, end-pre);
+
+ /*for(size_t i=0; i<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));}
+ printf("\n");*/
+
+ sort_r_simple(b, (pl-ple)/w, w, compar, arg);
+ sort_r_simple(end-(pre-pr), (pre-pr)/w, w, compar, arg);
+ }
+}
+
+
+#if defined NESTED_QSORT
+
+ static _SORT_R_INLINE void sort_r(void *base, size_t nel, size_t width,
+ int (*compar)(const void *_a,
+ const void *_b,
+ void *aarg),
+ void *arg)
+ {
+ int nested_cmp(const void *a, const void *b)
+ {
+ return compar(a, b, arg);
+ }
+
+ qsort(base, nel, width, nested_cmp);
+ }
+
+#else /* !NESTED_QSORT */
+
+ /* Declare structs and functions */
+
+ #if defined _SORT_R_BSD
+
+ /* Ensure qsort_r is defined */
+ extern void qsort_r(void *base, size_t nel, size_t width, void *thunk,
+ int (*compar)(void *_thunk,
+ const void *_a, const void *_b));
+
+ #endif
+
+ #if defined _SORT_R_BSD || defined _SORT_R_WINDOWS
+
+ /* BSD (qsort_r), Windows (qsort_s) require argument swap */
+
+ struct sort_r_data
+ {
+ void *arg;
+ int (*compar)(const void *_a, const void *_b, void *_arg);
+ };
+
+ static _SORT_R_INLINE int sort_r_arg_swap(void *s,
+ const void *a, const void *b)
+ {
+ struct sort_r_data *ss = (struct sort_r_data*)s;
+ return (ss->compar)(a, b, ss->arg);
+ }
+
+ #endif
+
+ #if defined _SORT_R_GNU
+
+ typedef int(* __compar_d_fn_t)(const void *, const void *, void *);
+ extern void qsort_r(void *base, size_t nel, size_t width,
+ __compar_d_fn_t __compar, void *arg)
+ __attribute__((nonnull (1, 4)));
+
+ #endif
+
+ /* implementation */
+
+ static _SORT_R_INLINE void sort_r(void *base, size_t nel, size_t width,
+ int (*compar)(const void *_a,
+ const void *_b, void *_arg),
+ void *arg)
+ {
+ #if defined _SORT_R_GNU
+
+ #if defined __GLIBC__ && ((__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8))
+
+ /* no qsort_r in glibc before 2.8, need to use nested qsort */
+ sort_r_simple(base, nel, width, compar, arg);
+
+ #else
+
+ qsort_r(base, nel, width, compar, arg);
+
+ #endif
+
+ #elif defined _SORT_R_BSD
+
+ struct sort_r_data tmp;
+ tmp.arg = arg;
+ tmp.compar = compar;
+ qsort_r(base, nel, width, &tmp, sort_r_arg_swap);
+
+ #elif defined _SORT_R_WINDOWS
+
+ struct sort_r_data tmp;
+ tmp.arg = arg;
+ tmp.compar = compar;
+ qsort_s(base, nel, width, sort_r_arg_swap, &tmp);
+
+ #else
+
+ /* Fall back to our own quicksort implementation */
+ sort_r_simple(base, nel, width, compar, arg);
+
+ #endif
+ }
+
+#endif /* !NESTED_QSORT */
+
+#undef _SORT_R_INLINE
+#undef _SORT_R_WINDOWS
+#undef _SORT_R_GNU
+#undef _SORT_R_BSD
+
+#endif /* SORT_R_H_ */
diff --git a/lib/uuid/Android.bp b/lib/uuid/Android.bp
new file mode 100644
index 0000000..3e6048d
--- /dev/null
+++ b/lib/uuid/Android.bp
@@ -0,0 +1,54 @@
+// Copyright 2017 The Android Open Source Project
+
+package {
+ default_applicable_licenses: ["external_e2fsprogs_lib_uuid_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "external_e2fsprogs_lib_uuid_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-BSD",
+ ],
+ license_text: [
+ "COPYING",
+ ],
+}
+
+cc_library {
+ name: "libext2_uuid",
+ host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ recovery_available: true,
+ vendor_available: true,
+ product_available: true,
+ unique_host_soname: true,
+ defaults: ["e2fsprogs-defaults"],
+ srcs: [
+ "clear.c",
+ "compare.c",
+ "copy.c",
+ "gen_uuid.c",
+ "isnull.c",
+ "pack.c",
+ "parse.c",
+ "unpack.c",
+ "unparse.c",
+ "uuid_time.c",
+ ],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ header_libs: ["libext2-headers"],
+ export_include_dirs: ["."],
+ export_header_lib_headers: ["libext2-headers"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
diff --git a/lib/uuid/COPYING b/lib/uuid/COPYING
new file mode 100644
index 0000000..2f17068
--- /dev/null
+++ b/lib/uuid/COPYING
@@ -0,0 +1,25 @@
+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, and the entire permission notice in its entirety,
+ including the disclaimer of warranties.
+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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/lib/uuid/Makefile.in b/lib/uuid/Makefile.in
new file mode 100644
index 0000000..5f69779
--- /dev/null
+++ b/lib/uuid/Makefile.in
@@ -0,0 +1,216 @@
+# Makefile for the UUID library
+#
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/uuid
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+
+@MCONFIG@
+
+all::
+
+SMANPAGES= uuid.3 uuid_clear.3 uuid_compare.3 uuid_copy.3 \
+ uuid_generate.3 uuid_is_null.3 uuid_parse.3 \
+ uuid_time.3 uuid_unparse.3
+
+OBJS= clear.o \
+ compare.o \
+ copy.o \
+ gen_uuid.o \
+ isnull.o \
+ pack.o \
+ parse.o \
+ unpack.o \
+ unparse.o \
+ uuid_time.o
+
+SRCS= $(srcdir)/clear.c \
+ $(srcdir)/compare.c \
+ $(srcdir)/copy.c \
+ $(srcdir)/gen_uuid.c \
+ $(srcdir)/isnull.c \
+ $(srcdir)/pack.c \
+ $(srcdir)/parse.c \
+ $(srcdir)/unpack.c \
+ $(srcdir)/unparse.c \
+ $(srcdir)/uuid_time.c
+
+LIBRARY= libuuid
+LIBDIR= uuid
+
+ELF_VERSION = 1.2
+ELF_SO_VERSION = 1
+ELF_IMAGE = libuuid
+ELF_MYDIR = uuid
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS =
+
+BSDLIB_VERSION = 1.1
+BSDLIB_IMAGE = libuuid
+BSDLIB_MYDIR = uuid
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+
+.c.o:
+ $(E) " CC $<"
+ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -c $< -o $@
+ $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $<
+@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS_STLIB) -g -pg -o profiled/$*.o -c $<
+@ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) -fPIC -shared -o elfshared/$*.o -c $<
+@BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS_SHLIB) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $<
+
+all:: tst_uuid uuid_time $(SMANPAGES) uuid.pc
+
+uuid.h: $(srcdir)/uuid.h.in
+ $(E) " CP $@"
+ $(Q) cp $(srcdir)/uuid.h.in uuid.h
+
+$(top_builddir)/lib/uuid/uuid_types.h: $(srcdir)/uuid_types.h.in $(top_builddir)/config.status
+ cd $(top_builddir); CONFIG_FILES=$(my_dir)/uuid_types.h ./config.status
+
+tst_uuid.o: $(srcdir)/tst_uuid.c uuid.h
+ $(E) " CC $@"
+ $(Q) $(CC) $(ALL_CFLAGS) -c $(srcdir)/tst_uuid.c -o tst_uuid.o
+
+tst_uuid: tst_uuid.o $(DEPSTATIC_LIBUUID)
+ $(E) " LD $@"
+ $(Q) $(CC) $(ALL_LDFLAGS) -o tst_uuid tst_uuid.o $(STATIC_LIBUUID)
+
+uuid_time: $(srcdir)/uuid_time.c $(DEPLIBUUID)
+ $(E) " LD $@"
+ $(Q) $(CC) $(ALL_CFLAGS) $(LDFLAGS) -DDEBUG -o uuid_time \
+ $(srcdir)/uuid_time.c $(LIBUUID)
+
+uuid.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid.3.in uuid.3
+
+uuid_clear.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_clear.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_clear.3.in uuid_clear.3
+
+uuid_compare.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_compare.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_compare.3.in uuid_compare.3
+
+uuid_copy.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_copy.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_copy.3.in uuid_copy.3
+
+uuid_generate.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_generate.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_generate.3.in uuid_generate.3
+
+uuid_is_null.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_is_null.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_is_null.3.in uuid_is_null.3
+
+uuid_parse.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_parse.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_parse.3.in uuid_parse.3
+
+uuid_time.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_time.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_time.3.in uuid_time.3
+
+uuid_unparse.3: $(DEP_SUBSTITUTE) $(srcdir)/uuid_unparse.3.in
+ $(E) " SUBST $@"
+ $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/uuid_unparse.3.in uuid_unparse.3
+
+uuid.pc: $(srcdir)/uuid.pc.in $(top_builddir)/config.status
+ $(E) " CONFIG.STATUS $@"
+ $(Q) cd $(top_builddir); CONFIG_FILES=lib/uuid/uuid.pc ./config.status
+
+installdirs::
+ $(E) " MKDIR_P $(libdir) $(includedir)/uuid $(man3dir)"
+ $(Q) $(MKDIR_P) $(DESTDIR)$(libdir) \
+ $(DESTDIR)$(includedir)/uuid $(DESTDIR)$(man3dir) \
+ $(DESTDIR)$(pkgconfigdir)
+
+install:: all installdirs
+ $(E) " INSTALL_DATA $(libdir)/libuuid.a"
+ $(Q) $(INSTALL_DATA) libuuid.a $(DESTDIR)$(libdir)/libuuid.a
+ -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libuuid.a
+ $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libuuid.a
+ $(E) " INSTALL_DATA $(includedir)/uuid/uuid.h"
+ $(Q) $(INSTALL_DATA) uuid.h $(DESTDIR)$(includedir)/uuid/uuid.h
+ $(Q) for i in $(SMANPAGES); do \
+ $(RM) -f $(DESTDIR)$(man3dir)/$$i.gz; \
+ echo " INSTALL_DATA $(man3dir)/$$i"; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(man3dir)/$$i; \
+ done
+ $(Q) $(RM) -f $(DESTDIR)$(man3dir)/uuid_generate_random.3.gz \
+ $(DESTDIR)$(man3dir)/uuid_generate_time.3.gz
+ $(E) " LINK $(man3dir)/uuid_generate_random.3"
+ $(Q) (cd $(DESTDIR)$(man3dir); \
+ $(LN) $(LINK_INSTALL_FLAGS) uuid_generate.3 uuid_generate_random.3)
+ $(E) " LINK $(man3dir)/uuid_generate_time.3"
+ $(Q) (cd $(DESTDIR)$(man3dir); \
+ $(LN) $(LINK_INSTALL_FLAGS) uuid_generate.3 uuid_generate_time.3)
+ $(E) " INSTALL_DATA $(pkgconfigdir)/uuid.pc"
+ $(Q) $(INSTALL_DATA) uuid.pc $(DESTDIR)$(pkgconfigdir)/uuid.pc
+
+uninstall::
+ $(RM) -f $(DESTDIR)$(libdir)/libuuid.a \
+ $(DESTDIR)$(pkgconfigdir)/uuid.pc
+ for i in $(SMANPAGES); do \
+ $(RM) -f $(DESTDIR)$(man3dir)/$$i; \
+ done
+ $(RM) -f $(DESTDIR)$(man3dir)/uuid_generate_random.3 $(DESTDIR)$(man3dir)/uuid_generate_time.3
+
+clean::
+ $(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/* uuid.h \
+ ../libuuid.a ../libuuid_p.a tst_uuid uuid_time \
+ uuid.pc uuid_types.h $(SMANPAGES)
+
+fullcheck check:: tst_uuid
+ LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_uuid
+
+mostlyclean:: clean
+distclean:: clean
+ $(RM) -f .depend Makefile uuid.pc \
+ $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+#
+# Hack to parallel makes recognize dependencies correctly.
+#
+../../lib/libuuid.a: libuuid.a
+../../lib/libuuid.so: image
+../../lib/libuuid.dylib: image
+
+$(OBJS): subdirs uuid.h
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+clear.o: $(srcdir)/clear.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+compare.o: $(srcdir)/compare.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+copy.o: $(srcdir)/copy.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+gen_uuid.o: $(srcdir)/gen_uuid.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h $(srcdir)/uuidd.h
+isnull.o: $(srcdir)/isnull.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+pack.o: $(srcdir)/pack.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+parse.o: $(srcdir)/parse.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+unpack.o: $(srcdir)/unpack.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+unparse.o: $(srcdir)/unparse.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
+uuid_time.o: $(srcdir)/uuid_time.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/uuidP.h
diff --git a/lib/uuid/clear.c b/lib/uuid/clear.c
new file mode 100644
index 0000000..436f8f7
--- /dev/null
+++ b/lib/uuid/clear.c
@@ -0,0 +1,44 @@
+/*
+ * clear.c -- Clear a UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "string.h"
+
+#include "uuidP.h"
+
+void uuid_clear(uuid_t uu)
+{
+ memset(uu, 0, 16);
+}
+
diff --git a/lib/uuid/compare.c b/lib/uuid/compare.c
new file mode 100644
index 0000000..d5fc503
--- /dev/null
+++ b/lib/uuid/compare.c
@@ -0,0 +1,56 @@
+/*
+ * compare.c --- compare whether or not two UUID's are the same
+ *
+ * Returns 0 if the two UUID's are different, and 1 if they are the same.
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "uuidP.h"
+#include <string.h>
+
+#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1);
+
+int uuid_compare(const uuid_t uu1, const uuid_t uu2)
+{
+ struct uuid uuid1, uuid2;
+
+ uuid_unpack(uu1, &uuid1);
+ uuid_unpack(uu2, &uuid2);
+
+ UUCMP(uuid1.time_low, uuid2.time_low);
+ UUCMP(uuid1.time_mid, uuid2.time_mid);
+ UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
+ UUCMP(uuid1.clock_seq, uuid2.clock_seq);
+ return memcmp(uuid1.node, uuid2.node, 6);
+}
+
diff --git a/lib/uuid/configure.in b/lib/uuid/configure.in
new file mode 100644
index 0000000..bf9509f
--- /dev/null
+++ b/lib/uuid/configure.in
@@ -0,0 +1,10 @@
+dnl
+dnl Not used now, for the future when uuid is separated out into its
+dnl own package.
+dnl
+AC_INIT(gen_uuid.c)
+AC_PREREQ(2.12)
+
+AC_CHECK_HEADERS(stdlib.h unistd.h sys/sockio.h net/if.h netinet/in.h)
+AC_CHECK_FUNCS(srandom)
+AC_OUTPUT(Makefile)
diff --git a/lib/uuid/copy.c b/lib/uuid/copy.c
new file mode 100644
index 0000000..32e6f50
--- /dev/null
+++ b/lib/uuid/copy.c
@@ -0,0 +1,46 @@
+/*
+ * copy.c --- copy UUIDs
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "uuidP.h"
+
+void uuid_copy(uuid_t dst, const uuid_t src)
+{
+ unsigned char *cp1;
+ const unsigned char *cp2;
+ int i;
+
+ for (i=0, cp1 = dst, cp2 = src; i < 16; i++)
+ *cp1++ = *cp2++;
+}
diff --git a/lib/uuid/gen_uuid.c b/lib/uuid/gen_uuid.c
new file mode 100644
index 0000000..2f02886
--- /dev/null
+++ b/lib/uuid/gen_uuid.c
@@ -0,0 +1,682 @@
+/*
+ * gen_uuid.c --- generate a DCE-compatible uuid
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+/*
+ * Force inclusion of SVID stuff since we need it if we're compiling in
+ * gcc-wall wall mode
+ */
+#define _SVID_SOURCE
+#define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
+#include <sys/syscall.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include "uuidP.h"
+#include "uuidd.h"
+
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+#ifdef TLS
+#define THREAD_LOCAL static TLS
+#else
+#define THREAD_LOCAL static
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
+#define DO_JRAND_MIX
+THREAD_LOCAL unsigned short jrand_seed[3];
+#endif
+
+static int get_random_fd(void)
+{
+ struct timeval tv;
+ static int fd = -2;
+ int i;
+
+ if (fd == -2) {
+ gettimeofday(&tv, 0);
+#ifndef _WIN32
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (fd >= 0) {
+ i = fcntl(fd, F_GETFD);
+ if (i >= 0)
+ fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+ }
+#endif
+ srand(((unsigned)getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+#ifdef DO_JRAND_MIX
+ jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+ jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+ jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+#endif
+ }
+ /* Crank the random number generator a few times */
+ gettimeofday(&tv, 0);
+ for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+ rand();
+ return fd;
+}
+
+
+/*
+ * Generate a series of random bytes. Use /dev/urandom if possible,
+ * and if not, use srandom/random.
+ */
+static void get_random_bytes(void *buf, int nbytes)
+{
+ int i, n = nbytes, fd;
+ int lose_counter = 0;
+ unsigned char *cp = buf;
+
+#ifdef HAVE_GETRANDOM
+ i = getrandom(buf, nbytes, 0);
+ if (i == nbytes)
+ return;
+#endif
+#ifdef HAVE_GETENTROPY
+ if (getentropy(buf, nbytes) == 0)
+ return;
+#endif
+
+ fd = get_random_fd();
+ if (fd >= 0) {
+ while (n > 0) {
+ i = read(fd, cp, n);
+ if (i <= 0) {
+ if (lose_counter++ > 16)
+ break;
+ continue;
+ }
+ n -= i;
+ cp += i;
+ lose_counter = 0;
+ }
+ }
+
+ /*
+ * We do this all the time, but this is the only source of
+ * randomness if /dev/random/urandom is out to lunch.
+ */
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (rand() >> 7) & 0xFF;
+#ifdef DO_JRAND_MIX
+ {
+ unsigned short tmp_seed[3];
+
+ memcpy(tmp_seed, jrand_seed, sizeof(tmp_seed));
+ jrand_seed[2] = jrand_seed[2] ^ syscall(__NR_gettid);
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+ memcpy(jrand_seed, tmp_seed,
+ sizeof(jrand_seed) - sizeof(unsigned short));
+ }
+#endif
+
+ return;
+}
+
+/*
+ * Get the ethernet hardware address, if we can find it...
+ *
+ * XXX for a windows version, probably should use GetAdaptersInfo:
+ * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451
+ * commenting out get_node_id just to get gen_uuid to compile under windows
+ * is not the right way to go!
+ */
+static int get_node_id(unsigned char *node_id)
+{
+#ifdef HAVE_NET_IF_H
+ int sd;
+ struct ifreq ifr, *ifrp;
+ struct ifconf ifc;
+ char buf[1024];
+ int n, i;
+ unsigned char *a;
+#ifdef HAVE_NET_IF_DL_H
+ struct sockaddr_dl *sdlp;
+#endif
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+ sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
+ close(sd);
+ return -1;
+ }
+ n = ifc.ifc_len;
+ for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
+ ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
+ strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
+#ifdef SIOCGIFHWADDR
+ if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+ if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) ifr.ifr_enaddr;
+#else
+#ifdef HAVE_NET_IF_DL_H
+ sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
+ if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
+ continue;
+ a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
+#else
+ /*
+ * XXX we don't have a way of getting the hardware
+ * address
+ */
+ close(sd);
+ return 0;
+#endif /* HAVE_NET_IF_DL_H */
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+ continue;
+ if (node_id) {
+ memcpy(node_id, a, 6);
+ close(sd);
+ return 1;
+ }
+ }
+ close(sd);
+#endif
+ return 0;
+}
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
+ uint16_t *ret_clock_seq, int *num)
+{
+ THREAD_LOCAL int adjustment = 0;
+ THREAD_LOCAL struct timeval last = {0, 0};
+ THREAD_LOCAL int state_fd = -2;
+ THREAD_LOCAL FILE *state_f;
+ THREAD_LOCAL uint16_t clock_seq;
+ struct timeval tv;
+#ifndef _WIN32
+ struct flock fl;
+#endif
+ uint64_t clock_reg;
+ mode_t save_umask;
+ int len;
+
+ if (state_fd == -2) {
+ save_umask = umask(0);
+ state_fd = open("/var/lib/libuuid/clock.txt",
+ O_RDWR|O_CREAT, 0660);
+ (void) umask(save_umask);
+ if (state_fd >= 0) {
+ state_f = fdopen(state_fd, "r+");
+ if (!state_f) {
+ close(state_fd);
+ state_fd = -1;
+ }
+ }
+ }
+#ifndef _WIN32
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = 0;
+ if (state_fd >= 0) {
+ rewind(state_f);
+ while (fcntl(state_fd, F_SETLKW, &fl) < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ fclose(state_f);
+ state_fd = -1;
+ break;
+ }
+ }
+#endif
+ if (state_fd >= 0) {
+ unsigned int cl;
+ unsigned long tv1, tv2;
+ int a;
+
+ if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
+ &cl, &tv1, &tv2, &a) == 4) {
+ clock_seq = cl & 0x3FFF;
+ last.tv_sec = tv1;
+ last.tv_usec = tv2;
+ adjustment = a;
+ }
+ }
+
+ if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+ get_random_bytes(&clock_seq, sizeof(clock_seq));
+ clock_seq &= 0x3FFF;
+ gettimeofday(&last, 0);
+ last.tv_sec--;
+ }
+
+try_again:
+ gettimeofday(&tv, 0);
+ if ((tv.tv_sec < last.tv_sec) ||
+ ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec < last.tv_usec))) {
+ clock_seq = (clock_seq+1) & 0x3FFF;
+ adjustment = 0;
+ last = tv;
+ } else if ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec == last.tv_usec)) {
+ if (adjustment >= MAX_ADJUSTMENT)
+ goto try_again;
+ adjustment++;
+ } else {
+ adjustment = 0;
+ last = tv;
+ }
+
+ clock_reg = tv.tv_usec*10 + adjustment;
+ clock_reg += ((uint64_t) tv.tv_sec)*10000000;
+ clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+
+ if (num && (*num > 1)) {
+ adjustment += *num - 1;
+ last.tv_usec += adjustment / 10;
+ adjustment = adjustment % 10;
+ last.tv_sec += last.tv_usec / 1000000;
+ last.tv_usec = last.tv_usec % 1000000;
+ }
+
+ if (state_fd > 0) {
+ rewind(state_f);
+ len = fprintf(state_f,
+ "clock: %04x tv: %016lu %08lu adj: %08d\n",
+ clock_seq, (unsigned long)last.tv_sec,
+ (unsigned long)last.tv_usec, adjustment);
+ fflush(state_f);
+ if (ftruncate(state_fd, len) < 0) {
+ fprintf(state_f, " \n");
+ fflush(state_f);
+ }
+ rewind(state_f);
+#ifndef _WIN32
+ fl.l_type = F_UNLCK;
+ if (fcntl(state_fd, F_SETLK, &fl) < 0) {
+ fclose(state_f);
+ state_fd = -1;
+ }
+#endif
+ }
+
+ *clock_high = clock_reg >> 32;
+ *clock_low = clock_reg;
+ *ret_clock_seq = clock_seq;
+ return 0;
+}
+
+#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H)
+static ssize_t read_all(int fd, char *buf, size_t count)
+{
+ ssize_t ret;
+ ssize_t c = 0;
+ int tries = 0;
+
+ memset(buf, 0, count);
+ while (count > 0) {
+ ret = read(fd, buf, count);
+ if (ret <= 0) {
+ if ((errno == EAGAIN || errno == EINTR || ret == 0) &&
+ (tries++ < 5))
+ continue;
+ return c ? c : -1;
+ }
+ if (ret > 0)
+ tries = 0;
+ count -= ret;
+ buf += ret;
+ c += ret;
+ }
+ return c;
+}
+
+/*
+ * Close all file descriptors
+ */
+static void close_all_fds(void)
+{
+ int i, max;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+ max = sysconf(_SC_OPEN_MAX);
+#elif defined(HAVE_GETDTABLESIZE)
+ max = getdtablesize();
+#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
+ struct rlimit rl;
+
+ getrlimit(RLIMIT_NOFILE, &rl);
+ max = rl.rlim_cur;
+#else
+ max = OPEN_MAX;
+#endif
+
+ for (i=0; i < max; i++) {
+ close(i);
+ if (i <= 2)
+ open("/dev/null", O_RDWR);
+ }
+}
+#endif /* defined(USE_UUIDD) && defined(HAVE_SYS_UN_H) */
+
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic push
+#if !defined(USE_UUIDD) || !defined(HAVE_SYS_UN_H)
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#endif
+/*
+ * Try using the uuidd daemon to generate the UUID
+ *
+ * Returns 0 on success, non-zero on failure.
+ */
+static int get_uuid_via_daemon(int op, uuid_t out, int *num)
+{
+#if defined(USE_UUIDD) && defined(HAVE_SYS_UN_H)
+ char op_buf[64];
+ int op_len;
+ int s;
+ ssize_t ret;
+ int32_t reply_len = 0, expected = 16;
+ struct sockaddr_un srv_addr;
+ struct stat st;
+ pid_t pid;
+ static const char *uuidd_path = UUIDD_PATH;
+ static int access_ret = -2;
+ static int start_attempts = 0;
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+
+ srv_addr.sun_family = AF_UNIX;
+ strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH);
+
+ if (connect(s, (const struct sockaddr *) &srv_addr,
+ sizeof(struct sockaddr_un)) < 0) {
+ if (access_ret == -2)
+ access_ret = access(uuidd_path, X_OK);
+ if (access_ret == 0)
+ access_ret = stat(uuidd_path, &st);
+ if (access_ret == 0 && (st.st_mode & (S_ISUID | S_ISGID)) == 0)
+ access_ret = access(UUIDD_DIR, W_OK);
+ if (access_ret == 0 && start_attempts++ < 5) {
+ if ((pid = fork()) == 0) {
+ close_all_fds();
+ execl(uuidd_path, "uuidd", "-qT", "300",
+ (char *) NULL);
+ exit(1);
+ }
+ (void) waitpid(pid, 0, 0);
+ if (connect(s, (const struct sockaddr *) &srv_addr,
+ sizeof(struct sockaddr_un)) < 0)
+ goto fail;
+ } else
+ goto fail;
+ }
+ op_buf[0] = op;
+ op_len = 1;
+ if (op == UUIDD_OP_BULK_TIME_UUID) {
+ memcpy(op_buf+1, num, sizeof(*num));
+ op_len += sizeof(*num);
+ expected += sizeof(*num);
+ }
+
+ ret = write(s, op_buf, op_len);
+ if (ret < 1)
+ goto fail;
+
+ ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
+ if (ret < 0)
+ goto fail;
+
+ if (reply_len != expected)
+ goto fail;
+
+ ret = read_all(s, op_buf, reply_len);
+
+ if (op == UUIDD_OP_BULK_TIME_UUID)
+ memcpy(op_buf+16, num, sizeof(int));
+
+ memcpy(out, op_buf, 16);
+
+ close(s);
+ return ((ret == expected) ? 0 : -1);
+
+fail:
+ close(s);
+#endif
+ return -1;
+}
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic pop
+#endif
+
+void uuid__generate_time(uuid_t out, int *num)
+{
+ static unsigned char node_id[6];
+ static int has_init = 0;
+ struct uuid uu;
+ uint32_t clock_mid;
+
+ if (!has_init) {
+ if (get_node_id(node_id) <= 0) {
+ get_random_bytes(node_id, 6);
+ /*
+ * Set multicast bit, to prevent conflicts
+ * with IEEE 802 addresses obtained from
+ * network cards
+ */
+ node_id[0] |= 0x01;
+ }
+ has_init = 1;
+ }
+ get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+ uu.clock_seq |= 0x8000;
+ uu.time_mid = (uint16_t) clock_mid;
+ uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
+ memcpy(uu.node, node_id, 6);
+ uuid_pack(&uu, out);
+}
+
+void uuid_generate_time(uuid_t out)
+{
+#ifdef TLS
+ THREAD_LOCAL int num = 0;
+ THREAD_LOCAL struct uuid uu;
+ THREAD_LOCAL time_t last_time = 0;
+ time_t now;
+
+ if (num > 0) {
+ now = time(0);
+ if (now > last_time+1)
+ num = 0;
+ }
+ if (num <= 0) {
+ num = 1000;
+ if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID,
+ out, &num) == 0) {
+ last_time = time(0);
+ uuid_unpack(out, &uu);
+ num--;
+ return;
+ }
+ num = 0;
+ }
+ if (num > 0) {
+ uu.time_low++;
+ if (uu.time_low == 0) {
+ uu.time_mid++;
+ if (uu.time_mid == 0)
+ uu.time_hi_and_version++;
+ }
+ num--;
+ uuid_pack(&uu, out);
+ return;
+ }
+#else
+ if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
+ return;
+#endif
+
+ uuid__generate_time(out, 0);
+}
+
+
+void uuid__generate_random(uuid_t out, int *num)
+{
+ uuid_t buf;
+ struct uuid uu;
+ int i, n;
+
+ if (!num || !*num)
+ n = 1;
+ else
+ n = *num;
+
+ for (i = 0; i < n; i++) {
+ get_random_bytes(buf, sizeof(buf));
+ uuid_unpack(buf, &uu);
+
+ uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+ uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF)
+ | 0x4000;
+ uuid_pack(&uu, out);
+ out += sizeof(uuid_t);
+ }
+}
+
+void uuid_generate_random(uuid_t out)
+{
+ int num = 1;
+ /* No real reason to use the daemon for random uuid's -- yet */
+
+ uuid__generate_random(out, &num);
+}
+
+
+/*
+ * This is the generic front-end to uuid_generate_random and
+ * uuid_generate_time. It uses uuid_generate_random only if
+ * /dev/urandom is available, since otherwise we won't have
+ * high-quality randomness.
+ */
+void uuid_generate(uuid_t out)
+{
+ if (get_random_fd() >= 0)
+ uuid_generate_random(out);
+ else
+ uuid_generate_time(out);
+}
diff --git a/lib/uuid/gen_uuid_nt.c b/lib/uuid/gen_uuid_nt.c
new file mode 100644
index 0000000..9a9a977
--- /dev/null
+++ b/lib/uuid/gen_uuid_nt.c
@@ -0,0 +1,93 @@
+/*
+ * gen_uuid_nt.c -- Use NT api to generate uuid
+ *
+ * Written by Andrey Shedel (andreys@ns.cr.cyco.com)
+ */
+
+
+#include "config.h"
+#include "uuidP.h"
+
+#pragma warning(push,4)
+
+#pragma comment(lib, "ntdll.lib")
+
+//
+// Here is a nice example why it's not a good idea
+// to use native API in ordinary applications.
+// Number of parameters in function below was changed from 3 to 4
+// for NT5.
+//
+//
+// NTSYSAPI
+// NTSTATUS
+// NTAPI
+// NtAllocateUuids(
+// OUT PULONG p1,
+// OUT PULONG p2,
+// OUT PULONG p3,
+// OUT PUCHAR Seed // 6 bytes
+// );
+//
+//
+
+unsigned long
+__stdcall
+NtAllocateUuids(
+ void* p1, // 8 bytes
+ void* p2, // 4 bytes
+ void* p3 // 4 bytes
+ );
+
+typedef
+unsigned long
+(__stdcall*
+NtAllocateUuids_2000)(
+ void* p1, // 8 bytes
+ void* p2, // 4 bytes
+ void* p3, // 4 bytes
+ void* seed // 6 bytes
+ );
+
+
+
+//
+// Nice, but instead of including ntddk.h ot winnt.h
+// I should define it here because they MISSED __stdcall in those headers.
+//
+
+__declspec(dllimport)
+struct _TEB*
+__stdcall
+NtCurrentTeb(void);
+
+
+//
+// The only way to get version information from the system is to examine
+// one stored in PEB. But it's pretty dangerous because this value could
+// be altered in image header.
+//
+
+static
+int
+Nt5(void)
+{
+ //return NtCuttentTeb()->Peb->OSMajorVersion >= 5;
+ return (int)*(int*)((char*)(int)(*(int*)((char*)NtCurrentTeb() + 0x30)) + 0xA4) >= 5;
+}
+
+
+
+
+void uuid_generate(uuid_t out)
+{
+ if(Nt5())
+ {
+ unsigned char seed[6];
+ ((NtAllocateUuids_2000)NtAllocateUuids)(out, ((char*)out)+8, ((char*)out)+12, &seed[0] );
+ }
+ else
+ {
+ NtAllocateUuids(out, ((char*)out)+8, ((char*)out)+12);
+ }
+}
diff --git a/lib/uuid/isnull.c b/lib/uuid/isnull.c
new file mode 100644
index 0000000..170c9a2
--- /dev/null
+++ b/lib/uuid/isnull.c
@@ -0,0 +1,49 @@
+/*
+ * isnull.c --- Check whether or not the UUID is null
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include "uuidP.h"
+
+/* Returns 1 if the uuid is the NULL uuid */
+int uuid_is_null(const uuid_t uu)
+{
+ const unsigned char *cp;
+ int i;
+
+ for (i=0, cp = uu; i < 16; i++)
+ if (*cp++)
+ return 0;
+ return 1;
+}
+
diff --git a/lib/uuid/pack.c b/lib/uuid/pack.c
new file mode 100644
index 0000000..3db21ae
--- /dev/null
+++ b/lib/uuid/pack.c
@@ -0,0 +1,70 @@
+/*
+ * Internal routine for packing UUID's
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+ uint32_t tmp;
+ unsigned char *out = ptr;
+
+ tmp = uu->time_low;
+ out[3] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[2] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[1] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[0] = (unsigned char) tmp;
+
+ tmp = uu->time_mid;
+ out[5] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[4] = (unsigned char) tmp;
+
+ tmp = uu->time_hi_and_version;
+ out[7] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[6] = (unsigned char) tmp;
+
+ tmp = uu->clock_seq;
+ out[9] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[8] = (unsigned char) tmp;
+
+ memcpy(out+10, uu->node, 6);
+}
+
diff --git a/lib/uuid/parse.c b/lib/uuid/parse.c
new file mode 100644
index 0000000..4c0857d
--- /dev/null
+++ b/lib/uuid/parse.c
@@ -0,0 +1,80 @@
+/*
+ * parse.c --- UUID parsing
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "uuidP.h"
+
+int uuid_parse(const char *in, uuid_t uu)
+{
+ struct uuid uuid;
+ int i;
+ const char *cp;
+ char buf[3];
+
+ if (strlen(in) != 36)
+ return -1;
+ for (i=0, cp = in; i <= 36; i++,cp++) {
+ if ((i == 8) || (i == 13) || (i == 18) ||
+ (i == 23)) {
+ if (*cp == '-')
+ continue;
+ else
+ return -1;
+ }
+ if (i== 36)
+ if (*cp == 0)
+ continue;
+ if (!isxdigit(*cp))
+ return -1;
+ }
+ uuid.time_low = strtoul(in, NULL, 16);
+ uuid.time_mid = strtoul(in+9, NULL, 16);
+ uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
+ uuid.clock_seq = strtoul(in+19, NULL, 16);
+ cp = in+24;
+ buf[2] = 0;
+ for (i=0; i < 6; i++) {
+ buf[0] = *cp++;
+ buf[1] = *cp++;
+ uuid.node[i] = strtoul(buf, NULL, 16);
+ }
+
+ uuid_pack(&uuid, uu);
+ return 0;
+}
diff --git a/lib/uuid/tst_uuid.c b/lib/uuid/tst_uuid.c
new file mode 100644
index 0000000..c1c2901
--- /dev/null
+++ b/lib/uuid/tst_uuid.c
@@ -0,0 +1,194 @@
+/*
+ * tst_uuid.c --- test program from the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <uuid/uuid.h>
+
+static int test_uuid(const char * uuid, int isValid)
+{
+ static const char * validStr[2] = {"invalid", "valid"};
+ uuid_t uuidBits;
+ int parsedOk;
+
+ parsedOk = uuid_parse(uuid, uuidBits) == 0;
+
+ printf("%s is %s", uuid, validStr[isValid]);
+ if (parsedOk != isValid) {
+ printf(" but uuid_parse says %s\n", validStr[parsedOk]);
+ return 1;
+ }
+ printf(", OK\n");
+ return 0;
+}
+
+#ifdef __GNUC__
+#define ATTR(x) __attribute__(x)
+#else
+#define ATTR(x)
+#endif
+
+int
+main(int argc ATTR((unused)) , char **argv ATTR((unused)))
+{
+ uuid_t buf, tst;
+ char str[100];
+ struct timeval tv;
+ time_t time_reg, time_gen;
+ unsigned char *cp;
+ int i;
+ int failed = 0;
+ int type, variant;
+
+ uuid_generate(buf);
+ uuid_unparse(buf, str);
+ printf("UUID generate = %s\n", str);
+ printf("UUID: ");
+ for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+ printf("%02x", *cp++);
+ }
+ printf("\n");
+ type = uuid_type(buf); variant = uuid_variant(buf);
+ printf("UUID type = %d, UUID variant = %d\n", type, variant);
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Incorrect UUID Variant; was expecting DCE!\n");
+ failed++;
+ }
+ printf("\n");
+
+ uuid_generate_random(buf);
+ uuid_unparse(buf, str);
+ printf("UUID random string = %s\n", str);
+ printf("UUID: ");
+ for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+ printf("%02x", *cp++);
+ }
+ printf("\n");
+ type = uuid_type(buf);
+ variant = uuid_variant(buf);
+ printf("UUID type = %d, UUID variant = %d\n", type, variant);
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Incorrect UUID Variant; was expecting DCE!\n");
+ failed++;
+ }
+ if (type != 4) {
+ printf("Incorrect UUID type; was expecting "
+ "4 (random type)!\n");
+ failed++;
+ }
+ printf("\n");
+
+ time_gen = time(0);
+ uuid_generate_time(buf);
+ uuid_unparse(buf, str);
+ printf("UUID string = %s\n", str);
+ printf("UUID time: ");
+ for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+ printf("%02x", *cp++);
+ }
+ printf("\n");
+ type = uuid_type(buf);
+ variant = uuid_variant(buf);
+ printf("UUID type = %d, UUID variant = %d\n", type, variant);
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Incorrect UUID Variant; was expecting DCE!\n");
+ failed++;
+ }
+ if (type != 1) {
+ printf("Incorrect UUID type; was expecting "
+ "1 (time-based type)!\\n");
+ failed++;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ time_reg = uuid_time(buf, &tv);
+ printf("UUID generated at %lu reports %lu (%ld.%ld)\n",
+ (unsigned long)time_gen, (unsigned long)time_reg,
+ (long)tv.tv_sec, (long)tv.tv_usec);
+ /* allow 1s margin in case of rollover between sampling
+ * the current time and when the UUID is generated. */
+ if (time_reg > time_gen + 1) {
+ printf("UUID time comparison failed!\n");
+ failed++;
+ } else {
+ printf("UUID time comparison succeeded.\n");
+ }
+
+ if (uuid_parse(str, tst) < 0) {
+ printf("UUID parse failed\n");
+ failed++;
+ }
+ if (!uuid_compare(buf, tst)) {
+ printf("UUID parse and compare succeeded.\n");
+ } else {
+ printf("UUID parse and compare failed!\n");
+ failed++;
+ }
+ uuid_clear(tst);
+ if (uuid_is_null(tst))
+ printf("UUID clear and is null succeeded.\n");
+ else {
+ printf("UUID clear and is null failed!\n");
+ failed++;
+ }
+ uuid_copy(buf, tst);
+ if (!uuid_compare(buf, tst))
+ printf("UUID copy and compare succeeded.\n");
+ else {
+ printf("UUID copy and compare failed!\n");
+ failed++;
+ }
+
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981b", 1);
+ failed += test_uuid("84949CC5-4701-4A84-895B-354C584A981B", 1);
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981bc", 0);
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981", 0);
+ failed += test_uuid("84949cc5x4701-4a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc504701-4a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-470104a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-4701-4a840895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-4701-4a84-895b0354c584a981b", 0);
+ failed += test_uuid("g4949cc5-4701-4a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981g", 0);
+
+ if (failed) {
+ printf("%d failures.\n", failed);
+ exit(1);
+ }
+ return 0;
+}
diff --git a/lib/uuid/unpack.c b/lib/uuid/unpack.c
new file mode 100644
index 0000000..2c8043d
--- /dev/null
+++ b/lib/uuid/unpack.c
@@ -0,0 +1,64 @@
+/*
+ * Internal routine for unpacking UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+ const uint8_t *ptr = in;
+ uint32_t 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);
+}
+
diff --git a/lib/uuid/unparse.c b/lib/uuid/unparse.c
new file mode 100644
index 0000000..c556ae6
--- /dev/null
+++ b/lib/uuid/unparse.c
@@ -0,0 +1,77 @@
+/*
+ * unparse.c -- convert a UUID to string
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+
+#include "uuidP.h"
+
+static const char *fmt_lower =
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+
+static const char *fmt_upper =
+ "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X";
+
+#ifdef UUID_UNPARSE_DEFAULT_UPPER
+#define FMT_DEFAULT fmt_upper
+#else
+#define FMT_DEFAULT fmt_lower
+#endif
+
+static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ sprintf(out, fmt,
+ 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]);
+}
+
+void uuid_unparse_lower(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, fmt_lower);
+}
+
+void uuid_unparse_upper(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, fmt_upper);
+}
+
+void uuid_unparse(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, FMT_DEFAULT);
+}
diff --git a/lib/uuid/uuid.3.in b/lib/uuid/uuid.3.in
new file mode 100644
index 0000000..1c51bd0
--- /dev/null
+++ b/lib/uuid/uuid.3.in
@@ -0,0 +1,66 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid \- DCE compatible Universally Unique Identifier library
+.SH SYNOPSIS
+.B #include <uuid/uuid.h>
+.SH DESCRIPTION
+The UUID library is used to generate unique identifiers for objects
+that may be accessible beyond the local system. This library
+generates UUIDs compatible with those created by the Open Software
+Foundation (OSF) Distributed Computing Environment (DCE) utility
+.BR uuidgen .
+.sp
+The UUIDs generated by this library can be reasonably expected to be
+unique within a system, and unique across all systems. They could
+be used, for instance, to generate unique HTTP cookies across multiple
+web servers without communication between the servers, and without fear
+of a name clash.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+The UUID library is part of the e2fsprogs package and is available from
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid.h.in b/lib/uuid/uuid.h.in
new file mode 100644
index 0000000..ca846da
--- /dev/null
+++ b/lib/uuid/uuid.h.in
@@ -0,0 +1,103 @@
+/*
+ * Public include file for the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUID_H
+#define _UUID_UUID_H
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+typedef unsigned char uuid_t[16];
+
+/* UUID Variant definitions */
+#define UUID_VARIANT_NCS 0
+#define UUID_VARIANT_DCE 1
+#define UUID_VARIANT_MICROSOFT 2
+#define UUID_VARIANT_OTHER 3
+
+/* UUID Type definitions */
+#define UUID_TYPE_DCE_TIME 1
+#define UUID_TYPE_DCE_RANDOM 4
+
+/* Allow UUID constants to be defined */
+#ifdef __GNUC__
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+ static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#else
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+ static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* clear.c */
+void uuid_clear(uuid_t uu);
+
+/* compare.c */
+int uuid_compare(const uuid_t uu1, const uuid_t uu2);
+
+/* copy.c */
+void uuid_copy(uuid_t dst, const uuid_t src);
+
+/* gen_uuid.c */
+void uuid_generate(uuid_t out);
+void uuid_generate_random(uuid_t out);
+void uuid_generate_time(uuid_t out);
+
+/* isnull.c */
+int uuid_is_null(const uuid_t uu);
+
+/* parse.c */
+int uuid_parse(const char *in, uuid_t uu);
+
+/* unparse.c */
+void uuid_unparse(const uuid_t uu, char *out);
+void uuid_unparse_lower(const uuid_t uu, char *out);
+void uuid_unparse_upper(const uuid_t uu, char *out);
+
+/* uuid_time.c */
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
+int uuid_type(const uuid_t uu);
+int uuid_variant(const uuid_t uu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UUID_UUID_H */
diff --git a/lib/uuid/uuid.pc.in b/lib/uuid/uuid.pc.in
new file mode 100644
index 0000000..eaa4e4d
--- /dev/null
+++ b/lib/uuid/uuid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: uuid
+Description: Universally unique id library
+Version: @E2FSPROGS_VERSION@
+Requires:
+Cflags: -I${includedir}/uuid -I${includedir}
+Libs: -L${libdir} -luuid
diff --git a/lib/uuid/uuidP.h b/lib/uuid/uuidP.h
new file mode 100644
index 0000000..6c0fd80
--- /dev/null
+++ b/lib/uuid/uuidP.h
@@ -0,0 +1,71 @@
+/*
+ * uuid.h -- private header file for uuids
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <uuid/uuid_types.h>
+#endif
+#include <sys/types.h>
+
+#include <uuid/uuid.h>
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW 0x13814000
+
+struct uuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint16_t clock_seq;
+ uint8_t node[6];
+};
+
+#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
+
+/*
+ * prototypes
+ */
+void uuid_pack(const struct uuid *uu, uuid_t ptr);
+void uuid_unpack(const uuid_t in, struct uuid *uu);
diff --git a/lib/uuid/uuid_clear.3.in b/lib/uuid/uuid_clear.3.in
new file mode 100644
index 0000000..92d2bf2
--- /dev/null
+++ b/lib/uuid/uuid_clear.3.in
@@ -0,0 +1,60 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_CLEAR 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_clear \- reset value of UUID variable to the NULL value
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "void uuid_clear(uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_clear
+function sets the value of the supplied uuid variable
+.I uu
+to the NULL value.
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_compare.3.in b/lib/uuid/uuid_compare.3.in
new file mode 100644
index 0000000..67bc9b9
--- /dev/null
+++ b/lib/uuid/uuid_compare.3.in
@@ -0,0 +1,66 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_COMPARE 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_compare \- compare whether two UUIDs are the same
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "int uuid_compare(uuid_t " uu1 ", uuid_t " uu2)
+.fi
+.SH DESCRIPTION
+The
+.B uuid_compare
+function compares the two supplied uuid variables
+.IR uu1 " and " uu2
+to each other.
+.SH RETURN VALUE
+Returns an integer less than, equal to, or greater than zero if
+.I uu1
+is found, respectively, to be lexicographical less than, equal, or
+greater than
+.IR uu2 .
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_copy.3.in b/lib/uuid/uuid_copy.3.in
new file mode 100644
index 0000000..5f5e8c6
--- /dev/null
+++ b/lib/uuid/uuid_copy.3.in
@@ -0,0 +1,62 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_COPY 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_copy \- copy a UUID value
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "void uuid_copy(uuid_t " dst ", uuid_t " src);
+.fi
+.SH DESCRIPTION
+The
+.B uuid_copy
+function copies the UUID variable
+.IR src " to " dst .
+.SH RETURN VALUE
+The copied UUID is returned in the location pointed to by
+.IR dst .
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_generate.3.in b/lib/uuid/uuid_generate.3.in
new file mode 100644
index 0000000..e6be968
--- /dev/null
+++ b/lib/uuid/uuid_generate.3.in
@@ -0,0 +1,104 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_GENERATE 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_generate, uuid_generate_random, uuid_generate_time \- create a new unique UUID value
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "void uuid_generate(uuid_t " out );
+.BI "void uuid_generate_random(uuid_t " out );
+.BI "void uuid_generate_time(uuid_t " out );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_generate
+function creates a new universally unique identifier (UUID). The uuid will
+be generated based on high-quality randomness from
+.IR /dev/urandom ,
+if available. If it is not available, then
+.B uuid_generate
+will use an alternative algorithm which uses the current time, the
+local ethernet MAC address (if available), and random data generated
+using a pseudo-random generator.
+.sp
+The
+.B uuid_generate_random
+function forces the use of the all-random UUID format, even if
+a high-quality random number generator (i.e.,
+.IR /dev/urandom )
+is not available, in which case a pseudo-random
+generator will be substituted. Note that the use of a pseudo-random
+generator may compromise the uniqueness of UUID's
+generated in this fashion.
+.sp
+The
+.B uuid_generate_time
+function forces the use of the alternative algorithm which uses the
+current time and the local ethernet MAC address (if available).
+This algorithm used to be the default one used to generate UUID, but
+because of the use of the ethernet MAC address, it can leak
+information about when and where the UUID was generated. This can cause
+privacy problems in some applications, so the
+.B uuid_generate
+function only uses this algorithm if a high-quality source of
+randomness is not available.
+.sp
+The UUID is 16 bytes (128 bits) long, which gives approximately 3.4x10^38
+unique values (there are approximately 10^80 elementary particles in
+the universe according to Carl Sagan's
+.IR Cosmos ).
+The new UUID can reasonably be considered unique among all UUIDs created
+on the local system, and among UUIDs created on other systems in the past
+and in the future.
+.SH RETURN VALUE
+The newly created UUID is returned in the memory location pointed to by
+.IR out .
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuidgen (1),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_is_null.3.in b/lib/uuid/uuid_is_null.3.in
new file mode 100644
index 0000000..e14288d
--- /dev/null
+++ b/lib/uuid/uuid_is_null.3.in
@@ -0,0 +1,62 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_IS_NULL 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_is_null \- compare the value of the UUID to the NULL value
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "int uuid_is_null(uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_is_null
+function compares the value of the supplied UUID variable
+.I uu
+to the NULL value. If the value is equal to the NULL UUID, 1 is returned,
+otherwise 0 is returned.
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_time (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_parse.3.in b/lib/uuid/uuid_parse.3.in
new file mode 100644
index 0000000..f85acfa
--- /dev/null
+++ b/lib/uuid/uuid_parse.3.in
@@ -0,0 +1,71 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_PARSE 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_parse \- convert an input UUID string into binary representation
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "int uuid_parse( char *" in ", uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_parse
+function converts the UUID string given by
+.I in
+into the binary representation. The input UUID is a string of the form
+1b4e28ba\-2fa1\-11d2\-883f\-b9a761bde3fb (in
+.BR printf (3)
+format "%08x\-%04x\-%04x\-%04x\-%012x", 36 bytes plus the trailing '\\0').
+.SH RETURN VALUE
+Upon successfully parsing the input string, 0 is returned, and the UUID is
+stored in the location pointed to by
+.IR uu ,
+otherwise \-1 is returned.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_time.3.in b/lib/uuid/uuid_time.3.in
new file mode 100644
index 0000000..2c8ef5b
--- /dev/null
+++ b/lib/uuid/uuid_time.3.in
@@ -0,0 +1,74 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_TIME 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_time \- extract the time at which the UUID was created
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "time_t uuid_time(uuid_t " uu ", struct timeval *" ret_tv )
+.fi
+.SH DESCRIPTION
+The
+.B uuid_time
+function extracts the time at which the supplied time-based UUID
+.I uu
+was created. Note that the UUID creation time is only encoded within
+certain types of UUIDs. This function can only reasonably expect to
+extract the creation time for UUIDs created with the
+.BR uuid_generate_time (3)
+function. It may or may not work with UUIDs created by other mechanisms.
+.SH "RETURN VALUES"
+The time at which the UUID was created, in seconds since January 1, 1970 GMT
+(the epoch), is returned (see
+.BR time "(2))."
+The time at which the UUID was created, in seconds and microseconds since
+the epoch, is also stored in the location pointed to by
+.I ret_tv
+(see
+.BR gettimeofday "(2))."
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
diff --git a/lib/uuid/uuid_time.c b/lib/uuid/uuid_time.c
new file mode 100644
index 0000000..b519d3c
--- /dev/null
+++ b/lib/uuid/uuid_time.c
@@ -0,0 +1,167 @@
+/*
+ * uuid_time.c --- Interpret the time field from a uuid. This program
+ * violates the UUID abstraction barrier by reaching into the guts
+ * of a UUID and interpreting it.
+ *
+ * Copyright (C) 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include "uuidP.h"
+
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
+{
+ struct timeval tv;
+ struct uuid uuid;
+ uint32_t high;
+ uint64_t clock_reg;
+
+ uuid_unpack(uu, &uuid);
+
+ high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
+ clock_reg = uuid.time_low | ((uint64_t) high << 32);
+
+ clock_reg -= (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+ tv.tv_sec = clock_reg / 10000000;
+ tv.tv_usec = (clock_reg % 10000000) / 10;
+
+ if (ret_tv)
+ *ret_tv = tv;
+
+ return tv.tv_sec;
+}
+
+int uuid_type(const uuid_t uu)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ return ((uuid.time_hi_and_version >> 12) & 0xF);
+}
+
+int uuid_variant(const uuid_t uu)
+{
+ struct uuid uuid;
+ int var;
+
+ uuid_unpack(uu, &uuid);
+ var = uuid.clock_seq;
+
+ if ((var & 0x8000) == 0)
+ return UUID_VARIANT_NCS;
+ if ((var & 0x4000) == 0)
+ return UUID_VARIANT_DCE;
+ if ((var & 0x2000) == 0)
+ return UUID_VARIANT_MICROSOFT;
+ return UUID_VARIANT_OTHER;
+}
+
+#ifdef DEBUG
+static const char *variant_string(int variant)
+{
+ switch (variant) {
+ case UUID_VARIANT_NCS:
+ return "NCS";
+ case UUID_VARIANT_DCE:
+ return "DCE";
+ case UUID_VARIANT_MICROSOFT:
+ return "Microsoft";
+ default:
+ return "Other";
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ uuid_t buf;
+ time_t time_reg;
+ struct timeval tv;
+ int type, variant;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s uuid\n", argv[0]);
+ exit(1);
+ }
+ if (uuid_parse(argv[1], buf)) {
+ fprintf(stderr, "Invalid UUID: %s\n", argv[1]);
+ exit(1);
+ }
+ variant = uuid_variant(buf);
+ type = uuid_type(buf);
+ time_reg = uuid_time(buf, &tv);
+
+ printf("UUID variant is %d (%s)\n", variant, variant_string(variant));
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Warning: This program only knows how to interpret "
+ "DCE UUIDs.\n\tThe rest of the output is likely "
+ "to be incorrect!!\n");
+ }
+ printf("UUID type is %d", type);
+ switch (type) {
+ case 1:
+ printf(" (time based)\n");
+ break;
+ case 2:
+ printf(" (DCE)\n");
+ break;
+ case 3:
+ printf(" (name-based)\n");
+ break;
+ case 4:
+ printf(" (random)\n");
+ break;
+ default:
+ printf("\n");
+ }
+ if (type != 1) {
+ printf("Warning: not a time-based UUID, so UUID time "
+ "decoding will likely not work!\n");
+ }
+ printf("UUID time is: (%ld, %ld): %s\n", (long)tv.tv_sec, (long)tv.tv_usec,
+ ctime(&time_reg));
+
+ return 0;
+}
+#endif
diff --git a/lib/uuid/uuid_types.h.in b/lib/uuid/uuid_types.h.in
new file mode 100644
index 0000000..f21ff4e
--- /dev/null
+++ b/lib/uuid/uuid_types.h.in
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+#if (!defined(_STDINT_H) && !defined(_UUID_STDINT_H))
+#define _UUID_STDINT_H
+
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+
+#if (@SIZEOF_INT@ == 8)
+typedef int int64_t;
+typedef unsigned int uint64_t;
+#elif (@SIZEOF_LONG@ == 8)
+typedef long int64_t;
+typedef unsigned long uint64_t;
+#elif (@SIZEOF_LONG_LONG@ == 8)
+#if defined(__GNUC__)
+typedef __signed__ long long int64_t;
+#else
+typedef signed long long int64_t;
+#endif
+typedef unsigned long long uint64_t;
+#endif
+
+#if (@SIZEOF_INT@ == 2)
+typedef int int16_t;
+typedef unsigned int uint16_t;
+#elif (@SIZEOF_SHORT@ == 2)
+typedef short int16_t;
+typedef unsigned short uint16_t;
+#else
+ ?==error: undefined 16 bit type
+#endif
+
+#if (@SIZEOF_INT@ == 4)
+typedef int int32_t;
+typedef unsigned int uint32_t;
+#elif (@SIZEOF_LONG@ == 4)
+typedef long int32_t;
+typedef unsigned long uint32_t;
+#elif (@SIZEOF_SHORT@ == 4)
+typedef short int32_t;
+typedef unsigned short uint32_t;
+#else
+ ?== error: undefined 32 bit type
+#endif
+
+#endif
diff --git a/lib/uuid/uuid_unparse.3.in b/lib/uuid/uuid_unparse.3.in
new file mode 100644
index 0000000..e8ca9de
--- /dev/null
+++ b/lib/uuid/uuid_unparse.3.in
@@ -0,0 +1,79 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" 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, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_UNPARSE 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+uuid_unparse \- convert an UUID from binary representation to a string
+.SH SYNOPSIS
+.nf
+.B #include <uuid/uuid.h>
+.sp
+.BI "void uuid_unparse(uuid_t " uu ", char *" out );
+.BI "void uuid_unparse_upper(uuid_t " uu ", char *" out );
+.BI "void uuid_unparse_lower(uuid_t " uu ", char *" out );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_unparse
+function converts the supplied UUID
+.I uu
+from the binary representation into a 36\-byte string (plus tailing '\\0')
+of the form 1b4e28ba\-2fa1\-11d2\-883f\-0016d3cca427 and stores this
+value in the character string pointed to by
+.IR out .
+The case of the hex digits returned by
+.B uuid_unparse
+may be upper or lower case, and is
+dependent on the system-dependent local default.
+.PP
+If the case of the
+hex digits is important then the functions
+.B uuid_unparse_upper
+and
+.B uuid_unparse_lower
+may be used.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y. Ts'o
+.SH AVAILABILITY
+.UR http://e2fsprogs.sourceforge.net/
+http://e2fsprogs.sourceforge.net/
+.UE
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_time (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3)
diff --git a/lib/uuid/uuidd.h b/lib/uuid/uuidd.h
new file mode 100644
index 0000000..c71f4b7
--- /dev/null
+++ b/lib/uuid/uuidd.h
@@ -0,0 +1,54 @@
+/*
+ * Definitions used by the uuidd daemon
+ *
+ * Copyright (C) 2007 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUIDD_H
+#define _UUID_UUIDD_H
+
+#define UUIDD_DIR "/var/lib/libuuid"
+#define UUIDD_SOCKET_PATH UUIDD_DIR "/request"
+#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid"
+#define UUIDD_PATH "/usr/sbin/uuidd"
+
+#define UUIDD_OP_GETPID 0
+#define UUIDD_OP_GET_MAXOP 1
+#define UUIDD_OP_TIME_UUID 2
+#define UUIDD_OP_RANDOM_UUID 3
+#define UUIDD_OP_BULK_TIME_UUID 4
+#define UUIDD_OP_BULK_RANDOM_UUID 5
+#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
+
+extern void uuid__generate_time(uuid_t out, int *num);
+extern void uuid__generate_random(uuid_t out, int *num);
+
+#endif /* _UUID_UUID_H */