pax_global_header00006660000000000000000000000064147127713720014525gustar00rootroot0000000000000052 comment=54fc1b615b7e9bf1c679e81677f034b86f746ae5 apfsprogs-0.2.0/000077500000000000000000000000001471277137200135305ustar00rootroot00000000000000apfsprogs-0.2.0/.gitignore000066400000000000000000000001541471277137200155200ustar00rootroot00000000000000*.swp *.d *.o *.a /apfsck/apfsck /mkapfs/mkapfs /apfs-snap/apfs-snap /apfs-label/apfs-label /tags version.h apfsprogs-0.2.0/LICENSE000066400000000000000000000431331471277137200145410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. apfsprogs-0.2.0/README000066400000000000000000000021211471277137200144040ustar00rootroot00000000000000 Apfsprogs is a suite of userland software to work with the Apple File System on Linux. It's intended mainly to help test the Linux kernel module that can be retrieved from . The following are included: o mkapfs: an experimental filesystem build tool o apfs-snap: a tool to take snapshots of a volume mounted with our module o apfs-label: a tool to list the labels of all volumes in a container o apfsck: a filesystem integrity checker, for now only useful for testers Usage ===== To build any of the tools, just cd to its directory and run make The resulting executable will have the name of the tool, and a man page is available under the same directory. If you run make install the files will be copied to the typical locations in the user home folder: the binary is placed in ~/bin, and the man page in ~/share/man/man8. You can override those paths by setting the BINDIR and MANDIR variables, for example: make install BINDIR=/sbin MANDIR=/usr/share/man/man8/ Credits ======= Written by Ernesto A. Fernández . apfsprogs-0.2.0/apfs-label/000077500000000000000000000000001471277137200155365ustar00rootroot00000000000000apfsprogs-0.2.0/apfs-label/Makefile000066400000000000000000000022171471277137200172000ustar00rootroot00000000000000SRCS = apfs-label.c OBJS = $(SRCS:.c=.o) DEPS = $(SRCS:.c=.d) LIBDIR = ../lib LIBRARY = $(LIBDIR)/libapfs.a DESTDIR ?= ~ BINDIR = /bin MANDIR = /share/man/man8 SPARSE_VERSION := $(shell sparse --version 2>/dev/null) GIT_COMMIT = $(shell git describe --always HEAD | tail -c 9) override CFLAGS += -Wall -Wno-address-of-packed-member -fno-strict-aliasing -I$(CURDIR)/../include apfs-label: $(OBJS) $(LIBRARY) @echo ' Linking...' @$(CC) $(CFLAGS) $(LDFLAGS) -o apfs-label $(OBJS) $(LIBRARY) @echo ' Build complete' # Build the common libraries $(LIBRARY): FORCE @echo ' Building libraries...' @$(MAKE) -C $(LIBDIR) --silent --no-print-directory @echo ' Library build complete' FORCE: %.o: %.c @echo ' Compiling $<...' @$(CC) $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif apfs-label.o: version.h version.h: FORCE @printf '#define GIT_COMMIT\t"%s"\n' $(GIT_COMMIT) > version.h -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) apfs-label version.h install: install -d $(DESTDIR)$(BINDIR) install -t $(DESTDIR)$(BINDIR) apfs-label install -d $(DESTDIR)$(MANDIR) install -m 644 -t $(DESTDIR)$(MANDIR) apfs-label.8 apfsprogs-0.2.0/apfs-label/apfs-label.8000066400000000000000000000014141471277137200176350ustar00rootroot00000000000000.\" apfs-label.8 - manpage for apfs-label .\" .\" Copyright (C) 2024 Ernesto A. Fernández .\" .TH apfs-label 8 "November 2024" "apfsprogs 0.2.0" .SH NAME apfs-label \- list the labels of all volumes in an APFS filesystem .SH SYNOPSIS .B apfs-label .I device .SH DESCRIPTION .B apfs-label is a simple tool that lists the index and label of all volumes in an APFS container, to help the user decide which one to mount with the driver. .SH OPTIONS .TP .B \-v Print the version number of .B apfs-label and exit. .SH REPORTING BUGS Please report bugs via email or, if preferred, file a github issue at \%https://github.com/eafer/apfsprogs/issues. .SH AUTHOR Written by Ernesto A. Fernández . .SH SEE ALSO .BR apfsck (8), .BR mkapfs (8) apfsprogs-0.2.0/apfs-label/apfs-label.c000066400000000000000000000210441471277137200177110ustar00rootroot00000000000000/* * Copyright (C) 2024 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include #include #include "version.h" static char *progname = NULL; static int dev_fd = -1; static unsigned long s_blocksize; static u64 nx_xid; /** * usage - Print usage information and exit */ static void usage(void) { fprintf(stderr, "usage: %s [-v] device\n", progname); exit(EXIT_FAILURE); } /** * version - Print version information and exit */ static void version(void) { if (*GIT_COMMIT) { printf("apfs-label %s\n", GIT_COMMIT); exit(EXIT_SUCCESS); } else { printf("apfs-label - unknown git commit id\n"); exit(EXIT_FAILURE); } } /** * system_error - Print a system error message and exit */ static __attribute__((noreturn)) void system_error(void) { perror(progname); exit(EXIT_FAILURE); } /** * fatal - Print a message and exit with an error code * @message: text to print */ static __attribute__((noreturn)) void fatal(const char *message) { fprintf(stderr, "%s: %s\n", progname, message); exit(EXIT_FAILURE); } /** * read_super_copy - Read the copy of the container superblock in block 0 * * Sets s_blocksize and returns a pointer to the raw superblock in memory. */ static struct apfs_nx_superblock *read_super_copy(void) { struct apfs_nx_superblock *msb_raw = NULL; int bsize_tmp; /* * For now assume a small blocksize, we only need it so that we can * read the actual blocksize from disk. */ bsize_tmp = APFS_NX_DEFAULT_BLOCK_SIZE; msb_raw = mmap(NULL, bsize_tmp, PROT_READ, MAP_PRIVATE, dev_fd, APFS_NX_BLOCK_NUM * bsize_tmp); if (msb_raw == MAP_FAILED) system_error(); if (le32_to_cpu(msb_raw->nx_magic) != APFS_NX_MAGIC) fatal("not an apfs container"); s_blocksize = le32_to_cpu(msb_raw->nx_block_size); if (s_blocksize < 4096) fatal("reported blocksize is too small"); if (s_blocksize != bsize_tmp) { munmap(msb_raw, bsize_tmp); msb_raw = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, APFS_NX_BLOCK_NUM * s_blocksize); if (msb_raw == MAP_FAILED) system_error(); } return msb_raw; } static int obj_verify_csum(struct apfs_obj_phys *obj) { return le64_to_cpu(obj->o_cksum) == fletcher64((char *)obj + APFS_MAX_CKSUM_SIZE, s_blocksize - APFS_MAX_CKSUM_SIZE); } /** * read_latest_super - Read the latest checkpoint superblock * @base: base of the checkpoint descriptor area * @blocks: block count for the checkpoint descriptor area */ static struct apfs_nx_superblock *read_latest_super(u64 base, u32 blocks) { struct apfs_nx_superblock *latest = NULL, *current = NULL; u64 xid = 0; u64 bno; for (bno = base; bno < base + blocks; ++bno) { if (current) munmap(current, s_blocksize); current = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, bno * s_blocksize); if (current == MAP_FAILED) system_error(); if (le32_to_cpu(current->nx_magic) != APFS_NX_MAGIC) continue; /* Not a superblock */ if (le64_to_cpu(current->nx_o.o_xid) <= xid) continue; /* Old */ if (!obj_verify_csum(¤t->nx_o)) continue; /* Corrupted */ xid = le64_to_cpu(current->nx_o.o_xid); latest = current; current = NULL; } if (!latest) fatal("no valid superblock in checkpoint area."); nx_xid = xid; return latest; } static struct apfs_nx_superblock *read_super(void) { struct apfs_nx_superblock *msb = NULL; u64 desc_base; u32 desc_blocks; msb = read_super_copy(); desc_base = le64_to_cpu(msb->nx_xp_desc_base); if (desc_base >> 63 != 0) { /* The highest bit is set when checkpoints are not contiguous */ fatal("checkpoint descriptor tree not yet supported."); } desc_blocks = le32_to_cpu(msb->nx_xp_desc_blocks); if (desc_blocks > 10000) /* Arbitrary loop limit, is it enough? */ fatal("too many checkpoint descriptors?"); munmap(msb, s_blocksize); msb = NULL; return read_latest_super(desc_base, desc_blocks); } static struct apfs_btree_node_phys *omap_bno_to_root(u64 omap_bno) { struct apfs_omap_phys *omap = NULL; struct apfs_btree_node_phys *root = NULL; u64 root_bno; omap = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, omap_bno * s_blocksize); if (omap == MAP_FAILED) system_error(); root_bno = le64_to_cpu(omap->om_tree_oid); munmap(omap, s_blocksize); root = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, root_bno * s_blocksize); if (root == MAP_FAILED) system_error(); /* I don't think this can happen so I don't support it for now */ if ((le16_to_cpu(root->btn_flags) & APFS_BTNODE_LEAF) == 0) fatal("container omap isn't a single node"); return root; } static void omap_node_locate_key(struct apfs_btree_node_phys *node, int index, int *off) { struct apfs_kvoff *entry; int keys_start, len; if (index >= APFS_NX_MAX_FILE_SYSTEMS) fatal("node index is out of bounds"); if ((le16_to_cpu(node->btn_flags) & APFS_BTNODE_FIXED_KV_SIZE) == 0) fatal("omap root should have fixed length keys/values"); keys_start = sizeof(*node) + le16_to_cpu(node->btn_table_space.off) + le16_to_cpu(node->btn_table_space.len); entry = (struct apfs_kvoff *)node->btn_data + index; *off = keys_start + le16_to_cpu(entry->k); len = 16; if (*off + len > s_blocksize) fatal("omap key out of bounds"); } static void omap_node_locate_val(struct apfs_btree_node_phys *node, int index, int *off) { struct apfs_kvoff *entry; int len; if (index >= APFS_NX_MAX_FILE_SYSTEMS) fatal("node index is out of bounds"); if ((le16_to_cpu(node->btn_flags) & APFS_BTNODE_FIXED_KV_SIZE) == 0) fatal("omap root should have fixed length keys/values"); entry = (struct apfs_kvoff *)node->btn_data + index; *off = s_blocksize - sizeof(struct apfs_btree_info) - le16_to_cpu(entry->v); len = 16; if (*off < 0 || *off + len > s_blocksize) fatal("omap value out of bounds"); } static int omap_keycmp(struct apfs_omap_key *k1, struct apfs_omap_key *k2) { if (le64_to_cpu(k1->ok_oid) != le64_to_cpu(k2->ok_oid)) return le64_to_cpu(k1->ok_oid) < le64_to_cpu(k2->ok_oid) ? -1 : 1; if (le64_to_cpu(k1->ok_xid) != le64_to_cpu(k2->ok_xid)) return le64_to_cpu(k1->ok_xid) < le64_to_cpu(k2->ok_xid) ? -1 : 1; return 0; } static u64 omap_lookup(struct apfs_btree_node_phys *node, u64 oid) { struct apfs_omap_key target_key; struct apfs_omap_key *curr_key = NULL; struct apfs_omap_val *value = NULL; int key_off, val_off; int index, left, right; int cmp; target_key.ok_oid = cpu_to_le64(oid); target_key.ok_xid = cpu_to_le64(nx_xid); index = le32_to_cpu(node->btn_nkeys); if (index > APFS_NX_MAX_FILE_SYSTEMS) fatal("too many records in container omap"); /* Search by bisection */ cmp = 1; left = 0; do { if (cmp > 0) { right = index - 1; if (right < left) fatal("missing omap record for volume"); index = (left + right) / 2; } else { left = index; index = DIV_ROUND_UP(left + right, 2); } omap_node_locate_key(node, index, &key_off); curr_key = (void *)node + key_off; cmp = omap_keycmp(curr_key, &target_key); if (cmp == 0) break; } while (left != right); if (cmp > 0) fatal("missing omap record for volume"); omap_node_locate_val(node, index, &val_off); value = (void *)node + val_off; return le64_to_cpu(value->ov_paddr); } /** * list_labels - Find all volumes in the device and print their labels */ static void list_labels(void) { struct apfs_nx_superblock *msb = NULL; struct apfs_superblock *vsb = NULL; struct apfs_btree_node_phys *omap = NULL; u64 vol_id, vol_bno; int i; msb = read_super(); omap = omap_bno_to_root(le64_to_cpu(msb->nx_omap_oid)); for (i = 0; i < APFS_NX_MAX_FILE_SYSTEMS; i++) { vol_id = le64_to_cpu(msb->nx_fs_oid[i]); /* I seem to recall some images had holes in the array */ if (vol_id == 0) continue; vol_bno = omap_lookup(omap, vol_id); vsb = mmap(NULL, s_blocksize, PROT_READ, MAP_PRIVATE, dev_fd, vol_bno * s_blocksize); if (vsb == MAP_FAILED) system_error(); if (vsb->apfs_volname[APFS_VOLNAME_LEN - 1]) fatal("volume label is not properly null-terminated"); printf("%d\t%s\n", i, vsb->apfs_volname); munmap(vsb, s_blocksize); } munmap(omap, s_blocksize); munmap(msb, s_blocksize); } int main(int argc, char *argv[]) { const char *filename = NULL; if (argc == 0) exit(EXIT_FAILURE); progname = argv[0]; while (1) { int opt = getopt(argc, argv, "v"); if (opt == -1) break; switch (opt) { case 'v': version(); default: usage(); } } if (optind != argc - 1) usage(); filename = argv[optind]; dev_fd = open(filename, O_RDONLY); if (dev_fd == -1) system_error(); list_labels(); return 0; } apfsprogs-0.2.0/apfs-snap/000077500000000000000000000000001471277137200154205ustar00rootroot00000000000000apfsprogs-0.2.0/apfs-snap/Makefile000066400000000000000000000016041471277137200170610ustar00rootroot00000000000000SRCS = apfs-snap.c OBJS = $(SRCS:.c=.o) DEPS = $(SRCS:.c=.d) DESTDIR ?= ~ BINDIR = /bin MANDIR = /share/man/man8 SPARSE_VERSION := $(shell sparse --version 2>/dev/null) GIT_COMMIT = $(shell git describe --always HEAD | tail -c 9) override CFLAGS += -Wall -Wno-address-of-packed-member -fno-strict-aliasing apfs-snap: $(OBJS) @echo ' Linking...' @$(CC) $(CFLAGS) $(LDFLAGS) -o apfs-snap $(OBJS) @echo ' Build complete' %.o: %.c @echo ' Compiling $<...' @$(CC) $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif apfs-snap.o: version.h version.h: FORCE @printf '#define GIT_COMMIT\t"%s"\n' $(GIT_COMMIT) > version.h FORCE: -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) apfs-snap version.h install: install -d $(DESTDIR)$(BINDIR) install -t $(DESTDIR)$(BINDIR) apfs-snap install -d $(DESTDIR)$(MANDIR) install -m 644 -t $(DESTDIR)$(MANDIR) apfs-snap.8 apfsprogs-0.2.0/apfs-snap/apfs-snap.8000066400000000000000000000014341471277137200174030ustar00rootroot00000000000000.\" apfs-snap.8 - manpage for apfs-snap .\" .\" Copyright (C) 2022 Ernesto A. Fernández .\" .TH apfs-snap 8 "November 2024" "apfsprogs 0.2.0" .SH NAME apfs-snap \- take an snapshot of an APFS filesystem .SH SYNOPSIS .B apfs-snap .I mountpoint .I name .SH DESCRIPTION .B apfs-snap is an experimental tool that takes a snapshot of the APFS filesystem mounted on the given .IR mountpoint . The label for the new snapshot must be specified in .IR name . .SH OPTIONS .TP .B \-v Print the version number of .B apfs-snap and exit. .SH REPORTING BUGS Please report bugs via email or, if preferred, file a github issue at \%https://github.com/eafer/apfsprogs/issues. .SH AUTHOR Written by Ernesto A. Fernández . .SH SEE ALSO .BR apfsck (8), .BR mkapfs (8) apfsprogs-0.2.0/apfs-snap/apfs-snap.c000066400000000000000000000043471471277137200174640ustar00rootroot00000000000000/* * Copyright (C) 2022 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include #include "version.h" static char *progname; /** * usage - Print usage information and exit */ static void usage(void) { fprintf(stderr, "usage: %s [-v] mountpoint name\n", progname); exit(EXIT_FAILURE); } /** * version - Print version information and exit */ static void version(void) { if (*GIT_COMMIT) { printf("apfs-snap %s\n", GIT_COMMIT); exit(EXIT_SUCCESS); } else { printf("apfs-snap - unknown git commit id\n"); exit(EXIT_FAILURE); } } /** * system_error - Print a system error message and exit */ static __attribute__((noreturn)) void system_error(void) { perror(progname); exit(EXIT_FAILURE); } /** * fatal - Print a message and exit with an error code * @message: text to print */ static __attribute__((noreturn)) void fatal(const char *message) { fprintf(stderr, "%s: %s\n", progname, message); exit(EXIT_FAILURE); } #define APFS_SNAP_MAX_NAMELEN 255 struct apfs_ioctl_snap_name { char name[APFS_SNAP_MAX_NAMELEN + 1]; }; #define APFS_IOC_TAKE_SNAPSHOT _IOW('@', 0x85, struct apfs_ioctl_snap_name) /** * create_snapshot - Submit the snapshot creation request to the driver * @fd: open file descriptor for the mountpoint * @name: label for the snapshot */ static void create_snapshot(int fd, const char *name) { static struct apfs_ioctl_snap_name arg = {0}; if (strlen(name) > APFS_SNAP_MAX_NAMELEN) fatal("snapshot label is too long"); strcpy(arg.name, name); if (ioctl(fd, APFS_IOC_TAKE_SNAPSHOT, &arg) < 0) system_error(); } int main(int argc, char *argv[]) { const char *mountpoint = NULL; const char *snapname = NULL; int fd; if (argc == 0) exit(EXIT_FAILURE); progname = argv[0]; while (1) { int opt = getopt(argc, argv, "v"); if (opt == -1) break; switch (opt) { case 'v': version(); default: usage(); } } if (optind != argc - 2) usage(); mountpoint = argv[optind]; snapname = argv[optind + 1]; fd = open(mountpoint, O_RDONLY); if (fd == -1) system_error(); create_snapshot(fd, snapname); return 0; } apfsprogs-0.2.0/apfsck/000077500000000000000000000000001471277137200147775ustar00rootroot00000000000000apfsprogs-0.2.0/apfsck/Makefile000066400000000000000000000025221471277137200164400ustar00rootroot00000000000000SRCS = apfsck.c btree.c compress.c crypto.c dir.c extents.c htable.c \ inode.c key.c object.c snapshot.c spaceman.c super.c xattr.c OBJS = $(SRCS:.c=.o) DEPS = $(SRCS:.c=.d) LIBDIR = ../lib LIBRARY = $(LIBDIR)/libapfs.a DESTDIR ?= ~ BINDIR = /bin MANDIR = /share/man/man8 SPARSE_VERSION := $(shell sparse --version 2>/dev/null) GIT_COMMIT = $(shell git describe --always HEAD | tail -c 9) override CFLAGS += -Wall -Wno-address-of-packed-member -fno-strict-aliasing -I$(CURDIR)/../include apfsck: $(OBJS) $(LIBRARY) @echo ' Linking...' @$(CC) $(CFLAGS) $(LDFLAGS) -o apfsck $(OBJS) $(LIBRARY) @echo ' Build complete' # Build the common libraries $(LIBRARY): FORCE @echo ' Building libraries...' @$(MAKE) -C $(LIBDIR) --silent --no-print-directory @echo ' Library build complete' FORCE: %.o: %.c @echo ' Compiling $<...' @$(CC) $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif apfsck.o: version.h version.h: FORCE @printf '#define GIT_COMMIT\t"%s"\n' $(GIT_COMMIT) > version.h -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) apfsck version.h install: install -d $(DESTDIR)$(BINDIR) install -t $(DESTDIR)$(BINDIR) apfsck ln -fs -T apfsck $(DESTDIR)$(BINDIR)/fsck.apfs install -d $(DESTDIR)$(MANDIR) install -m 644 -t $(DESTDIR)$(MANDIR) apfsck.8 ln -fs -T apfsck.8 $(DESTDIR)$(MANDIR)/fsck.apfs.8 apfsprogs-0.2.0/apfsck/apfsck.8000066400000000000000000000025431471277137200163430ustar00rootroot00000000000000.\" apfsck.8 - manpage for apfsck .\" .\" Copyright (C) 2019 Ernesto A. Fernández .\" .TH apfsck 8 "November 2024" "apfsprogs 0.2.0" .SH NAME apfsck \- check an APFS filesystem .SH SYNOPSIS .B apfsck [\-cuvw] [\-F .IR tier2 ] .I device .SH DESCRIPTION Unless you are an APFS developer, you are probably not interested in this tool. It's only intended for testing, and it could potentially be dangerous if run on a maliciously crafted filesystem. .PP .B apfsck is an experimental tool that checks an APFS filesystem for corruption. When an issue is found it is reported, but no repair is attempted. For that purpose, you should use the official tools provided by Apple. .SH OPTIONS .TP .B \-c Report if the filesystem shows signs of a recent crash. .TP .B \-u Report the presence of unknown/unsupported features. .TP .B \-v Print the version number of .B apfsck and exit. .TP .B \-w Report unexplained inconsistencies that may or may not be corruption. .TP .BI \-F " tier2" Specify the tier 2 device for a fusion drive. .SH EXIT STATUS The exit status is 0 if there was nothing to report, 1 otherwise. .SH REPORTING BUGS Please report bugs via email or, if preferred, file a github issue at \%https://github.com/eafer/apfsprogs/issues. .SH AUTHOR Written by Ernesto A. Fernández . .SH SEE ALSO .BR mkapfs (8) apfsprogs-0.2.0/apfsck/apfsck.c000066400000000000000000000065651471277137200164260ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include "apfsck.h" #include "super.h" #include "version.h" int fd_main; int fd_tier2 = -1; unsigned int options; static bool weird_state; static char *progname; /** * usage - Print usage information and exit */ static void usage(void) { fprintf(stderr, "usage: %s [-cuvw] [-F tier2] device\n", progname); exit(EXIT_FAILURE); } /** * version - Print version information and exit */ static void version(void) { if (*GIT_COMMIT) { printf("apfsck %s\n", GIT_COMMIT); exit(EXIT_SUCCESS); } else { printf("apfsck - unknown git commit id\n"); exit(EXIT_FAILURE); } } /** * system_error - Print a system error message and exit */ __attribute__((noreturn)) void system_error(void) { perror(progname); exit(EXIT_FAILURE); } /** * report - Report the issue discovered and exit * @context: structure where corruption was found (can be NULL) * @message: format string with a short explanation */ __attribute__((noreturn, format(printf, 2, 3))) void report(const char *context, const char *message, ...) { char buf[128]; va_list args; va_start(args, message); vsnprintf(buf, sizeof(buf), message, args); va_end(args); if (context) printf("%s: %s\n", context, buf); else printf("%s\n", buf); exit(EXIT_FAILURE); } /** * report_crash - Report that a crash was discovered and exit * @context: structure with signs of a crash * * Does nothing unless the -c cli option was used. */ void report_crash(const char *context) { if (options & OPT_REPORT_CRASH) report(context, "the filesystem was not unmounted cleanly."); } /** * report_unknown - Report the presence of unknown features and exit * @feature: the unsupported feature * * Does nothing unless the -u cli option was used. */ void report_unknown(const char *feature) { if (options & OPT_REPORT_UNKNOWN) report(feature, "not supported."); } /** * report_weird - Report unexplained inconsistencies * @context: structure where the inconsistency was found * * Does nothing unless the -w cli option was used. This function should * be called when the specification, and common sense, appear to be in * contradiction with the behaviour of actual filesystems. */ void report_weird(const char *context) { if (!(options & OPT_REPORT_WEIRD)) return; /* * Several of my test images have 'weird' issues, so don't exit right * away. Remember that an issue was found, for the exit code. */ printf("%s: odd inconsistency (may not be corruption).\n", context); weird_state = true; } int main(int argc, char *argv[]) { char *filename; progname = argv[0]; while (1) { int opt = getopt(argc, argv, "cuvwF:"); if (opt == -1) break; switch (opt) { case 'c': options |= OPT_REPORT_CRASH; break; case 'u': options |= OPT_REPORT_UNKNOWN; break; case 'w': options |= OPT_REPORT_WEIRD; break; case 'v': version(); case 'F': fd_tier2 = open(optarg, O_RDONLY); if (fd_tier2 == -1) system_error(); break; default: usage(); } } if (optind != argc - 1) usage(); filename = argv[optind]; fd_main = open(filename, O_RDONLY); if (fd_main == -1) system_error(); parse_filesystem(); if (weird_state) return 1; return 0; } apfsprogs-0.2.0/apfsck/apfsck.h000066400000000000000000000044701471277137200164240ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _APFSCK_H #define _APFSCK_H #include /* Declarations for global variables */ extern unsigned int options; /* Command line options */ extern struct super_block *sb; /* Filesystem superblock */ extern struct volume_superblock *vsb; /* Volume superblock */ extern int fd_main; /* File descriptor for the main device */ extern int fd_tier2; /* File descriptor for the tier 2 device, if any */ extern bool ongoing_query; /* Are we currently running a query? */ /* Option flags */ #define OPT_REPORT_CRASH 1 /* Report on-disk signs of a past crash */ #define OPT_REPORT_UNKNOWN 2 /* Report unknown or unsupported features */ #define OPT_REPORT_WEIRD 4 /* Report issues that may not be corruption */ extern __attribute__((noreturn, format(printf, 2, 3))) void report(const char *context, const char *message, ...); extern void report_crash(const char *context); extern void report_unknown(const char *feature); extern void report_weird(const char *context); extern __attribute__((noreturn)) void system_error(void); #include #include #include #include /* Forwards the mmap() call to the proper device of a fusion drive */ static inline void *apfs_mmap(void *addr, size_t length, int prot, int flags, u64 offset) { /* * TODO: check that the block is in the correct device for all callers * where that is known. */ if (offset >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR) { if (fd_tier2 == -1) report(NULL, "Address in missing tier 2 device."); offset -= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR; return mmap(addr, length, prot, flags, fd_tier2, (off_t)offset); } return mmap(addr, length, prot, flags, fd_main, (off_t)offset); } /* Forwards the pread() call to the proper device of a fusion drive */ static inline ssize_t apfs_pread(void *buf, size_t count, u64 offset) { /* * TODO: check that the block is in the correct device for all callers * where that is known. */ if (offset >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR) { if (fd_tier2 == -1) report(NULL, "Address in missing tier 2 device."); offset -= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR; return pread(fd_tier2, buf, count, (off_t)offset); } return pread(fd_main, buf, count, (off_t)offset); } #endif /* _APFSCK_H */ apfsprogs-0.2.0/apfsck/btree.c000066400000000000000000001653511471277137200162570ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "dir.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "key.h" #include "object.h" #include "snapshot.h" #include "spaceman.h" #include "super.h" #include "xattr.h" bool ongoing_query; /** * node_min_table_size - Return the minimum size for a node's table of contents * @node: the node */ static int node_min_table_size(struct node *node) { u32 type = node->object.subtype; bool leaf = (node->flags) & APFS_BTNODE_LEAF; int key_size, val_size, toc_size; int space, count; /* Trees with fixed key/value sizes preallocate the whole table */ switch (type) { case APFS_OBJECT_TYPE_OMAP: key_size = sizeof(struct apfs_omap_key); val_size = leaf ? sizeof(struct apfs_omap_val) : sizeof(__le64); toc_size = sizeof(struct apfs_kvoff); break; case APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE: key_size = sizeof(struct apfs_spaceman_free_queue_key); val_size = sizeof(__le64); /* We assume no ghosts here */ toc_size = sizeof(struct apfs_kvoff); break; case APFS_OBJECT_TYPE_OMAP_SNAPSHOT: key_size = sizeof(__le64); val_size = leaf ? sizeof(struct apfs_omap_snapshot) : sizeof(__le64); toc_size = sizeof(struct apfs_kvoff); break; case APFS_OBJECT_TYPE_FEXT_TREE: key_size = sizeof(struct apfs_fext_tree_key); val_size = leaf ? sizeof(struct apfs_fext_tree_val) : sizeof(__le64); toc_size = sizeof(struct apfs_kvoff); break; case APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE: key_size = sizeof(struct apfs_fusion_mt_key); val_size = leaf ? sizeof(struct apfs_fusion_mt_val) : sizeof(__le64); toc_size = sizeof(struct apfs_kvoff); break; default: /* It should at least have room for one record */ return sizeof(struct apfs_kvloc); } /* The footer of root nodes is ignored for some reason */ space = sb->s_blocksize - sizeof(struct apfs_btree_node_phys); count = space / (key_size + val_size + toc_size); return count * toc_size; } /** * node_is_valid - Check basic sanity of the node index * @node: node to check */ static bool node_is_valid(struct node *node) { u16 flags = node->flags; int records = node->records; int index_size = node->key - node->toc; int entry_size, min_index_size; if ((flags & APFS_BTNODE_MASK) != flags) return false; if (!node_is_root(node) && !records) return false; /* Empty children should just be deleted */ if (node->toc != sizeof(struct apfs_btree_node_phys)) return false; /* The table of contents follows the header */ if (node->data > sb->s_blocksize - (node_is_root(node) ? sizeof(struct apfs_btree_info) : 0)) return false; /* The value area must start before it ends... */ entry_size = (node_has_fixed_kv_size(node)) ? sizeof(struct apfs_kvoff) : sizeof(struct apfs_kvloc); min_index_size = node_min_table_size(node); if (index_size < min_index_size) return false; if (node_has_fixed_kv_size(node) && index_size != min_index_size) { /* * Free queue nodes have ghost records, which makes no sense if * their index is not allowed to grow bigger. */ if (node->object.subtype != APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE) return false; } /* All records must have an entry in the table of contents */ return records * entry_size <= index_size; } /** * node_parse_key_free_list - Parse a node's free key list into a bitmap * @node: the node to parse * * The bitmap will be set in @node->free_key_bmap. */ static void node_parse_key_free_list(struct node *node) { struct apfs_nloc *free = &node->raw->btn_key_free_list; void *area_raw = (void *)node->raw + node->key; int area_len = node->free - node->key; int total = le16_to_cpu(free->len); int off; /* Each bit represents a byte in the key area */ node->free_key_bmap = malloc((area_len + 7) / 8); if (!node->free_key_bmap) system_error(); memset(node->free_key_bmap, 0xFF, (area_len + 7) / 8); off = le16_to_cpu(free->off); while (total > 0) { int len, i; /* Tiny free areas may not be in the list */ if (off == APFS_BTOFF_INVALID) break; if (off + sizeof(*free) > area_len) report("B-tree node", "no room for free list entry in key area."); free = area_raw + off; len = le16_to_cpu(free->len); if (len < sizeof(*free)) report("B-tree node", "free key is too small."); if (off + len > area_len) report("B-tree node", "free key is out-of-bounds."); for (i = off; i < off + len; ++i) { u8 *byte = node->free_key_bmap + i / 8; u8 flag = 1 << i % 8; if (!(*byte & flag)) report("B-tree node", "byte listed twice in free key list."); *byte ^= flag; } total -= len; off = le16_to_cpu(free->off); } if (off != APFS_BTOFF_INVALID) report("B-tree node", "bad last key in free list."); } /** * node_parse_val_free_list - Parse a node's free value list into a bitmap * @node: the node to parse * * The bitmap will be set in @node->free_val_bmap. */ static void node_parse_val_free_list(struct node *node) { struct apfs_nloc *free = &node->raw->btn_val_free_list; void *end_raw = node->raw; int area_len; int total = le16_to_cpu(free->len); int off; /* Only the root has a footer */ area_len = sb->s_blocksize - node->data - (node_is_root(node) ? sizeof(struct apfs_btree_info) : 0); end_raw = (void *)node->raw + node->data + area_len; /* Each bit represents a byte in the value area */ node->free_val_bmap = malloc((area_len + 7) / 8); if (!node->free_val_bmap) system_error(); memset(node->free_val_bmap, 0xFF, (area_len + 7) / 8); off = le16_to_cpu(free->off); while (total > 0) { int len, i; /* Tiny free areas may not be in the list */ if (off == APFS_BTOFF_INVALID) break; if (off < sizeof(*free)) report("B-tree node", "no room for free list entry in value area."); free = end_raw - off; len = le16_to_cpu(free->len); if (len < sizeof(*free)) report("B-tree node", "free value is too small."); if (area_len < off || len > off) report("B-tree node", "free value is out-of-bounds."); for (i = area_len - off; i < area_len - off + len; ++i) { u8 *byte = node->free_val_bmap + i / 8; u8 flag = 1 << i % 8; if (!(*byte & flag)) report("B-tree node", "byte listed twice in free value list."); *byte ^= flag; } total -= len; off = le16_to_cpu(free->off); } if (off != APFS_BTOFF_INVALID) report("B-tree node", "bad last value in free list."); } /** * node_prepare_bitmaps - Do the basic setup of the nodes allocation bitmaps * @node: the node to parse * * The @node->free_key_bmap and @free_val_bmap bitmaps will be set entirely * from the information in the linked lists. The @node->used_key_bmap and * @node->used_val_bmap will only be allocated, to be set later when parsing * the keys. * * TODO: should we check that the free space in the node is zeroed? */ static void node_prepare_bitmaps(struct node *node) { int keys_len; int values_len; assert(node->raw); assert(!node->free_key_bmap); assert(!node->free_val_bmap); keys_len = node->free - node->key; /* Each bit represents a byte in the key area */ node->used_key_bmap = calloc(1, (keys_len + 7) / 8); if (!node->used_key_bmap) system_error(); /* Only the root has a footer */ values_len = sb->s_blocksize - node->data - (node_is_root(node) ? sizeof(struct apfs_btree_info) : 0); /* Each bit represents a byte in the value area */ node->used_val_bmap = calloc(1, (values_len + 7) / 8); if (!node->used_val_bmap) system_error(); node_parse_key_free_list(node); node_parse_val_free_list(node); } /** * verify_block_hash - Verify that a block has the given hash * @raw: pointer to the block contents * @hash: pointer to the SHA-256 hash */ static void verify_block_hash(u8 *raw, u8 *hash) { SHA256_CTX ctx = {0}; u8 true_hash[APFS_HASH_CCSHA256_SIZE] = {0}; if (!hash) return; sha256_init(&ctx); sha256_update(&ctx, raw, sb->s_blocksize); sha256_final(&ctx, true_hash); if (memcmp(hash, true_hash, APFS_HASH_CCSHA256_SIZE) != 0) report("Sealed volume", "incorrect hash for node."); } /** * read_node - Read a node header from disk * @oid: object id for the node * @btree: tree structure, with the omap_table already set * @hash: SHA-256 hash to check against (NULL if none) * * Returns a pointer to the resulting node structure. */ static struct node *read_node(u64 oid, struct btree *btree, u8 *hash) { struct apfs_btree_node_phys *raw; struct node *node; u32 obj_type, obj_subtype; bool noheader; node = calloc(1, sizeof(*node)); if (!node) system_error(); node->btree = btree; noheader = btree_is_catalog(btree) && apfs_volume_is_sealed(); /* The free-space queue is the only tree with ephemeral nodes so far */ if (btree_is_free_queue(btree)) raw = read_ephemeral_object(oid, &node->object); else if (noheader) raw = read_object_noheader(oid, btree->omap_table, &node->object); else raw = read_object(oid, btree->omap_table, &node->object); node->raw = raw; if (hash) verify_block_hash((u8 *)raw, hash); node->level = le16_to_cpu(raw->btn_level); node->flags = le16_to_cpu(raw->btn_flags); node->records = le32_to_cpu(raw->btn_nkeys); node->toc = sizeof(*raw) + le16_to_cpu(raw->btn_table_space.off); node->key = node->toc + le16_to_cpu(raw->btn_table_space.len); node->free = node->key + le16_to_cpu(raw->btn_free_space.off); node->data = node->free + le16_to_cpu(raw->btn_free_space.len); if (!node_is_valid(node)) { report("B-tree node", "block 0x%llx is not sane.", (unsigned long long)node->object.block_nr); } if ((bool)(node->flags & APFS_BTNODE_NOHEADER) != noheader) report("B-tree node", "wrong setting of hashed flag."); if ((bool)(node->flags & APFS_BTNODE_HASHED) != noheader) report("B-tree node", "wrong setting of hashed flag."); obj_type = node->object.type; if (!noheader && node_is_root(node) && obj_type != APFS_OBJECT_TYPE_BTREE) report("B-tree node", "wrong object type for root."); if (!noheader && !node_is_root(node) && obj_type != APFS_OBJECT_TYPE_BTREE_NODE) report("B-tree node", "wrong object type for nonroot."); obj_subtype = node->object.subtype; if (btree_is_omap(btree) && obj_subtype != APFS_OBJECT_TYPE_OMAP) report("Object map node", "wrong object subtype."); if (!noheader && btree_is_catalog(btree) && obj_subtype != APFS_OBJECT_TYPE_FSTREE) report("Catalog node", "wrong object subtype."); if (btree_is_extentref(btree) && obj_subtype != APFS_OBJECT_TYPE_BLOCKREFTREE) report("Extent reference tree node", "wrong object subtype."); if (btree_is_snap_meta(btree) && obj_subtype != APFS_OBJECT_TYPE_SNAPMETATREE) report("Snapshot metadata node", "wrong object subtype."); if (btree_is_free_queue(btree) && obj_subtype != APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE) report("Free queue node", "wrong object subtype."); if (btree_is_snapshots(btree) && obj_subtype != APFS_OBJECT_TYPE_OMAP_SNAPSHOT) report("Omap snapshot tree node", "wrong object subtype."); if (btree_is_fext(btree) && obj_subtype != APFS_OBJECT_TYPE_FEXT_TREE) report("File extents tree node", "wrong object subtype."); node_prepare_bitmaps(node); return node; } /** * node_free - Free a node structure * @node: node to free * * This function works under the assumption that the node flags are not * corrupted, but we are not yet checking that (TODO). */ static void node_free(struct node *node) { struct object *obj = NULL; if (node_is_root(node)) return; /* The root nodes are needed by the sb until the end */ obj = &node->object; /* * Ephemeral objects may wrap around so they are actually copied to * memory, not just mmapped. Hacky, like much of the fsck. */ if ((obj->flags & APFS_OBJ_STORAGETYPE_MASK) == APFS_OBJ_EPHEMERAL) free(node->raw); else munmap(node->raw, obj->size); free(node->free_key_bmap); free(node->free_val_bmap); free(node->used_key_bmap); free(node->used_val_bmap); free(node); } /** * node_locate_key - Locate the key of a node record * @node: node to be searched * @index: number of the entry to locate * @off: on return will hold the offset in the block * * Returns the length of the key. The function checks that this length fits * within the key area; callers must use the returned value to make sure they * never operate outside its bounds. */ static int node_locate_key(struct node *node, int index, int *off) { struct apfs_btree_node_phys *raw; int len, off_in_area; if (index >= node->records) report("B-tree node", "requested index out-of-bounds."); raw = node->raw; if (node_has_fixed_kv_size(node)) { struct apfs_kvoff *entry; entry = (struct apfs_kvoff *)raw->btn_data + index; len = btree_is_snapshots(node->btree) ? 8 : 16; off_in_area = le16_to_cpu(entry->k); } else { /* These node types have variable length keys and data */ struct apfs_kvloc *entry; entry = (struct apfs_kvloc *)raw->btn_data + index; len = le16_to_cpu(entry->k.len); off_in_area = le16_to_cpu(entry->k.off); } /* Translate offset in key area to offset in block */ *off = node->key + off_in_area; if (*off + len > node->free) report("B-tree", "key is out-of-bounds."); return len; } /** * node_locate_data - Locate the data of a node record * @node: node to be searched * @index: number of the entry to locate * @off: on return will hold the offset in the block * * Returns the length of the data. The function checks that this length fits * within the value area; callers must use the returned value to make sure they * never operate outside its bounds. */ static int node_locate_data(struct node *node, int index, int *off) { struct apfs_btree_node_phys *raw; int len, off_in_area, area_len; if (index >= node->records) report("B-tree", "requested index out-of-bounds."); /* Only the root has a footer */ area_len = sb->s_blocksize - node->data - (node_is_root(node) ? sizeof(struct apfs_btree_info) : 0); raw = node->raw; if (node_has_fixed_kv_size(node)) { /* These node types have fixed length keys and data */ struct apfs_kvoff *entry; struct btree *btree = node->btree; entry = (struct apfs_kvoff *)raw->btn_data + index; if (btree_is_free_queue(btree)) { /* A free-space queue record may have no value */ if (le16_to_cpu(entry->v) == APFS_BTOFF_INVALID) return 0; len = 8; } if (btree_is_omap(btree)) len = node_is_leaf(node) ? 16 : 8; if (btree_is_snapshots(btree)) len = node_is_leaf(node) ? sizeof(struct apfs_omap_snapshot) : 8; if (btree_is_fext(btree)) len = node_is_leaf(node) ? sizeof(struct apfs_fext_tree_val) : 8; /* Value offsets are backwards from the end of the value area */ off_in_area = area_len - le16_to_cpu(entry->v); } else { /* These node types have variable length keys and data */ struct apfs_kvloc *entry; entry = (struct apfs_kvloc *)raw->btn_data + index; len = le16_to_cpu(entry->v.len); /* Value offsets are backwards from the end of the value area */ off_in_area = area_len - le16_to_cpu(entry->v.off); } *off = node->data + off_in_area; if (*off < node->data || off_in_area >= area_len) report("B-tree", "value is out-of-bounds."); return len; } /** * bmap_mark_as_used - Mark a region of a node as used in the allocation bitmap * @bitmap: bitmap to update * @off: offset of the region, relative to its area in the node * @len: length of the region in the node */ static void bmap_mark_as_used(u8 *bitmap, int off, int len) { u8 *byte; u8 flag; int i; for (i = off; i < off + len; ++i) { byte = bitmap + i / 8; flag = 1 << i % 8; if (*byte & flag) report("B-tree node", "overlapping record data."); *byte |= flag; } } /** * compare_bmaps - Compare record allocation bitmaps for free and used space * @free_bmap: bitmap compiled from the free space linked list * @used_bmap: bitmap compiled from the table of contents * @area_len: length of the area to check in the node * * Verifies that @free_bmap and @used_bmap are consistent, and returns the * total number of free bytes (including those not counted in @free_bmap due * to fragmentation). */ static int compare_bmaps(u8 *free_bmap, u8 *used_bmap, int area_len) { int unused = 0; int i, j; for (i = 0; i < area_len / 8; ++i) { for (j = 0; j < 8; ++j) { u8 mask = 1 << j; if (!(used_bmap[i] & mask)) ++unused; } if ((free_bmap[i] | used_bmap[i]) != free_bmap[i]) report("B-tree node", "used record space listed as free."); } /* Last byte has some undefined bits by the end, so be careful */ for (j = 0; j < area_len % 8; ++j) { u8 mask = 1 << j; if (!(used_bmap[area_len / 8] & mask)) ++unused; if (used_bmap[area_len / 8] & mask && !(free_bmap[area_len / 8] & mask)) report("B-tree node", "used record space listed as free."); } return unused; } /** * node_compare_bmaps - Check consistency of the allocation bitmaps for a node * @node: node to check * * Verifies that the bitmaps built from the free lists match the actual unused * space bitmaps, built from the table of contents. This function shouldn't be * called, of course, until all the bitmaps are assembled. */ static void node_compare_bmaps(struct node *node) { struct apfs_nloc *free_head; int area_len; int free_count; /* * First check the key area. */ free_head = &node->raw->btn_key_free_list; area_len = node->free - node->key; free_count = compare_bmaps(node->free_key_bmap, node->used_key_bmap, area_len); if (free_count != le16_to_cpu(free_head->len)) report("B-tree", "wrong free space total for key area."); /* * Now check the value area. */ free_head = &node->raw->btn_val_free_list; /* Only the root has a footer */ area_len = sb->s_blocksize - node->data - (node_is_root(node) ? sizeof(struct apfs_btree_info) : 0); free_count = compare_bmaps(node->free_val_bmap, node->used_val_bmap, area_len); if (free_count != le16_to_cpu(free_head->len)) report("B-tree", "wrong free space total for value area."); } /** * parse_cat_record - Parse a catalog record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ static void parse_cat_record(void *key, void *val, int len) { switch (cat_type(key)) { case APFS_TYPE_INODE: parse_inode_record(key, val, len); break; case APFS_TYPE_DIR_REC: parse_dentry_record(key, val, len); break; case APFS_TYPE_FILE_EXTENT: parse_extent_record(key, val, len); break; case APFS_TYPE_SIBLING_LINK: parse_sibling_record(key, val, len); break; case APFS_TYPE_XATTR: parse_xattr_record(key, val, len); break; case APFS_TYPE_SIBLING_MAP: parse_sibling_map_record(key, val, len); break; case APFS_TYPE_DSTREAM_ID: parse_dstream_id_record(key, val, len); break; case APFS_TYPE_DIR_STATS: parse_dir_stats_record(key, val, len); break; case APFS_TYPE_CRYPTO_STATE: parse_crypto_state_record(key, val, len); break; case APFS_TYPE_FILE_INFO: parse_file_info_record(key, val, len); break; default: report(NULL, "Bug!"); } } /** * free_omap_record - Free an object map record list after a final check * @entry: the entry to free */ static void free_omap_record_list(struct htable_entry *entry) { struct omap_record_list *list = (struct omap_record_list *)entry; struct omap_record *curr_rec = list->o_records; while (curr_rec) { struct omap_record *next_rec = NULL; if (curr_rec->flags & APFS_OMAP_VAL_DELETED) { if (curr_rec->seen) report("Omap record", "deleted but still in use."); } else if (!curr_rec->seen) { /* * Old implementations that are unaware of extended * snapshot metadata are allowed to leak those blocks * when a snapshot gets deleted. */ if (vsb && curr_rec->xid < sb->s_xid) { struct apfs_obj_phys *raw = NULL; struct object obj = {0}; raw = read_object_nocheck(curr_rec->bno, sb->s_blocksize, &obj); if (obj.type != APFS_OBJECT_TYPE_SNAP_META_EXT || obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Leaked omap record", "unexpected object type."); container_bmap_mark_as_used(curr_rec->bno, 1); ++vsb->v_block_count; munmap(raw, obj.size); } else { report("Omap record", "oid-xid combination is never used."); } } next_rec = curr_rec->next; free(curr_rec); curr_rec = next_rec; } free(entry); } /** * free_omap_table - Free a hash table for omap records, and all its entries * @table: table to free */ void free_omap_table(struct htable_entry **table) { free_htable(table, free_omap_record_list); } /** * omap_list_clear_seen_for_snap - Clear seen_for_snap for all omap recs in list * @entry: a list of omap records */ static void omap_list_clear_seen_for_snap(struct htable_entry *entry) { struct omap_record_list *list = (struct omap_record_list *)entry; struct omap_record *rec = NULL; for (rec = list->o_records; rec; rec = rec->next) rec->seen_for_snap = false; } /** * omap_htable_clear_seen_for_snap - Clear seen_for_snap on all omap recs * @table: the hash table of omap records to be cleared */ void omap_htable_clear_seen_for_snap(struct htable_entry **table) { return apply_on_htable(table, omap_list_clear_seen_for_snap); } /** * get_omap_record - Find or create an omap record structure in a hash table * @oid: object id to be mapped * @xid: transaction id * @table: the hash table of omap records to be searched * * Returns the omap record structure, after creating it if necessary. */ static struct omap_record *get_omap_record(u64 oid, u64 xid, struct htable_entry **table) { struct omap_record_list *list = NULL; struct omap_record **omap_p = NULL; struct omap_record *omap = NULL; struct omap_record *new = NULL; list = (struct omap_record_list *)get_htable_entry(oid, sizeof(struct omap_record), table); omap_p = &list->o_records; omap = *omap_p; while (omap) { if (xid == omap->xid) return omap; if (xid < omap->xid) break; omap_p = &omap->next; omap = *omap_p; } new = calloc(1, sizeof(*new)); if (!new) system_error(); new->xid = xid; new->oid = oid; new->next = omap; *omap_p = new; return new; } /** * get_latest_omap_record - Find the most recent omap record before a given xid * @oid: object id to be mapped * @xid: transaction id * @table: the hash table of omap records to be searched * * Returns the omap record structure, or NULL if there is none matches. */ struct omap_record *get_latest_omap_record(u64 oid, u64 xid, struct htable_entry **table) { struct omap_record_list *list = NULL; struct omap_record *omap = NULL, *prev_omap = NULL; list = (struct omap_record_list *)get_htable_entry(oid, sizeof(struct omap_record), table); omap = list->o_records; while (omap) { if (xid < omap->xid) break; prev_omap = omap; omap = prev_omap->next; } return prev_omap; } /** * parse_omap_record - Parse an object map record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ static void parse_omap_record(struct apfs_omap_key *key, struct apfs_omap_val *val, int len) { struct omap_record *omap_rec; u32 flags; u32 size; /* In the omap, keys and values must be aligned to eight bytes */ if ((u64)key & 7 || (u64)val & 7) report("Omap record", "bad alignment for key or value."); if (len != sizeof(*val)) report("Omap record", "wrong size of value."); if (vsb) { /* We are parsing a volume's object map */ omap_rec = get_omap_record(le64_to_cpu(key->ok_oid), le64_to_cpu(key->ok_xid), vsb->v_omap_table); } else { /* We are parsing the container's object map */ omap_rec = get_omap_record(le64_to_cpu(key->ok_oid), le64_to_cpu(key->ok_xid), sb->s_omap_table); } if (omap_rec->bno) report("Object map", "two entries with the same oid-xid."); omap_rec->bno = le64_to_cpu(val->ov_paddr); omap_rec->flags = flags = le32_to_cpu(val->ov_flags); if ((flags & APFS_OMAP_VAL_FLAGS_VALID_MASK) != flags) report("Omap record", "invalid flag in use."); if (flags & APFS_OMAP_VAL_SAVED) report("Omap record", "saved flag is set."); if ((bool)(flags & APFS_OMAP_VAL_ENCRYPTED) != (vsb && vsb->v_encrypted)) report("Omap record", "wrong encryption flag."); if (flags & APFS_OMAP_VAL_CRYPTO_GENERATION) report_unknown("Crypto generation flag"); size = le32_to_cpu(val->ov_size); if (size & (sb->s_blocksize - 1)) report("Omap record", "size isn't multiple of block size."); if (size != sb->s_blocksize) report_unknown("Objects with more than one block"); } /** * parse_subtree - Parse a subtree and check for corruption * @root: root node of the subtree * @last_key: parent key, that must come before all the keys in this subtree; * on return, this will hold the last key of this subtree, that * must come before the next key of the parent node * @name_buf: buffer to store the name of @last_key when its node is freed * (can be NULL if the keys have no name) */ static void parse_subtree(struct node *root, struct key *last_key, char *name_buf) { struct btree *btree = root->btree; struct key curr_key; bool is_sealed_cat; int i; if (node_is_leaf(root)) { if (root->level != 0) report("B-tree", "nonleaf node flagged as leaf."); btree->key_count += root->records; } ++btree->node_count; if (btree_is_omap(btree) && !node_has_fixed_kv_size(root)) report("Object map", "key size should be fixed."); if (btree_is_catalog(btree) && node_has_fixed_kv_size(root)) report("Catalog", "key size should not be fixed."); if (btree_is_free_queue(btree) && !node_has_fixed_kv_size(root)) report("Free-space queue", "key size should be fixed."); if (btree_is_snap_meta(btree) && node_has_fixed_kv_size(root)) report("Snap meta tree", "key size shouldn't be fixed."); if (btree_is_snapshots(btree) && !node_has_fixed_kv_size(root)) report("Omap snapshot tree", "key size should be fixed."); if (btree_is_fext(btree) && !node_has_fixed_kv_size(root)) report("File extents tree", "key size should be fixed."); if (btree_is_fusion_mt(btree) && !node_has_fixed_kv_size(root)) report("Fusion middle-tree", "key size should be fixed."); /* This makes little sense, but it appears to be true */ if (btree_is_extentref(btree) && node_has_fixed_kv_size(root)) report("Extent reference tree", "key size shouldn't be fixed."); is_sealed_cat = btree_is_catalog(btree) && apfs_volume_is_sealed(); for (i = 0; i < root->records; ++i) { struct node *child; void *raw = root->raw; void *raw_key, *raw_val; int off, len; u64 child_id; u8 *child_hash = NULL; len = node_locate_key(root, i, &off); if (len > btree->longest_key) btree->longest_key = len; bmap_mark_as_used(root->used_key_bmap, off - root->key, len); raw_key = raw + off; if (btree_is_omap(btree)) { read_omap_key(raw_key, len, &curr_key); /* When a key is added, the node is updated */ if (curr_key.number > root->object.xid) report("Object map", "node xid is older than key xid."); } if (btree_is_catalog(btree)) read_cat_key(raw_key, len, &curr_key); if (btree_is_extentref(btree)) read_extentref_key(raw_key, len, &curr_key); if (btree_is_free_queue(btree)) read_free_queue_key(raw_key, len, &curr_key); if (btree_is_snap_meta(btree)) read_snap_key(raw_key, len, &curr_key); if (btree_is_snapshots(btree)) read_omap_snap_key(raw_key, len, &curr_key); if (btree_is_fext(btree)) read_fext_key(raw_key, len, &curr_key); if (keycmp(last_key, &curr_key) > 0) report("B-tree", "keys are out of order."); if (!i && !node_is_root(root) && keycmp(last_key, &curr_key)) report("B-tree", "index key absent from child node."); if (i != 0 && node_is_leaf(root) && !keycmp(last_key, &curr_key)) report("B-tree", "leaf keys are repeated."); *last_key = curr_key; len = node_locate_data(root, i, &off); bmap_mark_as_used(root->used_val_bmap, off - root->data, len); raw_val = raw + off; if (node_is_leaf(root)) { if (len > btree->longest_val) btree->longest_val = len; if (btree_is_catalog(btree)) parse_cat_record(raw_key, raw_val, len); if (btree_is_omap(btree)) parse_omap_record(raw_key, raw_val, len); if (btree_is_free_queue(btree)) parse_free_queue_record(raw_key, raw_val, len, btree); if (btree_is_extentref(btree)) /* Physical extents must not overlap */ last_key->id = parse_phys_ext_record(raw_key, raw_val, len); if (btree_is_snap_meta(btree)) parse_snap_record(raw_key, raw_val, len); if (btree_is_snapshots(btree)) parse_omap_snap_record(raw_key, raw_val, len); if (btree_is_fext(btree)) parse_fext_record(raw_key, raw_val, len); continue; } if (is_sealed_cat) { struct apfs_btn_index_node_val *index_val = NULL; u64 base_oid = btree->root->object.oid; if (len != sizeof(*index_val)) report("B-tree", "wrong size of hashed nonleaf record value."); index_val = (struct apfs_btn_index_node_val *)raw_val; /* * The reference is wrong here, binv_child_oid is not * the id for the child node, it's an offset from the * id of the root node. As usual, I have no idea what * this is about. */ child_id = le64_to_cpu(index_val->binv_child_oid) + base_oid; child_hash = index_val->binv_child_hash; } else { if (len != 8) report("B-tree", "wrong size of nonleaf record value."); child_id = le64_to_cpu(*(__le64 *)(raw_val)); } child = read_node(child_id, btree, child_hash); if (child->level != root->level - 1) report("B-tree", "node levels are corrupted."); if (node_is_root(child)) report("B-tree", "nonroot node is flagged as root."); /* If a physical node changes, the parent must update the bno */ if ((btree_is_omap(btree) || btree_is_extentref(btree) || btree_is_snap_meta(btree) || btree_is_snapshots(btree) || btree_is_fext(btree)) && root->object.xid < child->object.xid) report("Physical tree", "xid of node is older than xid of its child."); parse_subtree(child, last_key, name_buf); node_free(child); } /* All records of @root are processed, so it's a good time for this */ node_compare_bmaps(root); /* * last_key->name is just a pointer to the memory-mapped on-disk name * of the key. Since the caller will free the node, make a copy. */ if (node_is_leaf(root) && last_key->name) { assert(name_buf); strcpy(name_buf, last_key->name); last_key->name = name_buf; } } /** * check_btree_footer_flags - Check consistency of b-tree footer flags * @flags: the flags * @btree: the b-tree being checked * @ctx: context string for corruption reports */ static void check_btree_footer_flags(u32 flags, struct btree *btree, char *ctx) { bool aligned, is_free_queue, is_physical, is_sealed_cat; if ((flags & APFS_BTREE_FLAGS_VALID_MASK) != flags) report(ctx, "invalid flag in use."); if (flags & (APFS_BTREE_NONPERSISTENT)) report(ctx, "nonpersistent flag is set."); /* TODO: are these really the only allowed settings for the flag? */ aligned = btree_is_omap(btree) || btree_is_free_queue(btree) || btree_is_snapshots(btree) || btree_is_fext(btree) || btree_is_fusion_mt(btree); if (aligned != !(flags & APFS_BTREE_KV_NONALIGNED)) report(ctx, "wrong alignment flag."); is_free_queue = btree_is_free_queue(btree); if (is_free_queue != (bool)(flags & (APFS_BTREE_ALLOW_GHOSTS))) report(ctx, "wrong setting of ghosts flag."); if (is_free_queue != (bool)(flags & (APFS_BTREE_EPHEMERAL))) report(ctx, "wrong setting of ephemeral flag."); is_physical = !(btree_is_catalog(btree) || btree_is_free_queue(btree)); if (is_physical != (bool)(flags & APFS_BTREE_PHYSICAL)) report(ctx, "wrong setting of physical flag."); is_sealed_cat = btree_is_catalog(btree) && apfs_volume_is_sealed(); if (is_sealed_cat != (bool)(flags & APFS_BTREE_HASHED)) report(ctx, "wrong setting of hashed flag."); if (is_sealed_cat != (bool)(flags & APFS_BTREE_NOHEADER)) report(ctx, "wrong setting of no-header flag."); } /** * check_btree_footer - Check that btree_info matches the collected stats * @btree: b-tree to check */ static void check_btree_footer(struct btree *btree) { struct node *root = btree->root; struct apfs_btree_info *info; char *ctx; switch (btree->type) { case BTREE_TYPE_OMAP: ctx = "Object map"; break; case BTREE_TYPE_CATALOG: ctx = "Catalog"; break; case BTREE_TYPE_EXTENTREF: ctx = "Extent reference tree"; break; case BTREE_TYPE_SNAP_META: ctx = "Snapshot metadata tree"; break; case BTREE_TYPE_FREE_QUEUE: ctx = "Free-space queue"; break; case BTREE_TYPE_SNAPSHOTS: ctx = "Omap snapshot tree"; break; case BTREE_TYPE_FEXT: ctx = "File extents tree"; break; case BTREE_TYPE_FUSION_MT: ctx = "Fusion middle-tree"; break; default: report(NULL, "Bug!"); } /* This flag is not part of the footer, but its check fits best here */ if (!node_is_root(root)) report(ctx, "wrong flag in root node."); info = (void *)root->raw + sb->s_blocksize - sizeof(*info); if (le32_to_cpu(info->bt_fixed.bt_node_size) != sb->s_blocksize) report_unknown("Objects with more than one block"); check_btree_footer_flags(le32_to_cpu(info->bt_fixed.bt_flags), btree, ctx); if (le64_to_cpu(info->bt_key_count) != btree->key_count) report(ctx, "wrong key count in info footer."); if (le64_to_cpu(info->bt_node_count) != btree->node_count) report(ctx, "wrong node count in info footer."); if (btree_is_omap(btree)) { u32 longest_key = le32_to_cpu(info->bt_longest_key); u32 longest_val = le32_to_cpu(info->bt_longest_val); if (le32_to_cpu(info->bt_fixed.bt_key_size) != sizeof(struct apfs_omap_key)) report(ctx, "wrong key size in info footer."); if (le32_to_cpu(info->bt_fixed.bt_val_size) != sizeof(struct apfs_omap_val)) report(ctx, "wrong value size in info footer."); /* Containers with no volumes do exist */ if ((longest_key || btree->key_count) && longest_key != sizeof(struct apfs_omap_key)) report(ctx, "wrong maximum key size in info footer."); if ((longest_val || btree->key_count) && longest_val != sizeof(struct apfs_omap_val)) report(ctx, "wrong maximum value size in info footer."); return; } if (btree_is_free_queue(btree)) { if (le32_to_cpu(info->bt_fixed.bt_key_size) != sizeof(struct apfs_spaceman_free_queue_key)) report(ctx, "wrong key size in info footer."); /* Ghost records may also exist, but they don't count here */ if (le32_to_cpu(info->bt_fixed.bt_val_size) != 8) report(ctx, "wrong value size in info footer."); if (le32_to_cpu(info->bt_longest_key) != sizeof(struct apfs_spaceman_free_queue_key)) report(ctx, "wrong maximum key size in info footer."); if (le32_to_cpu(info->bt_longest_val) < btree->longest_val) report(ctx, "wrong maximum value size in info footer."); return; } if (btree_is_snapshots(btree)) { if (le32_to_cpu(info->bt_fixed.bt_key_size) != 8) report(ctx, "wrong key size in info footer."); if (le32_to_cpu(info->bt_fixed.bt_val_size) != sizeof(struct apfs_omap_snapshot)) report(ctx, "wrong value size in info footer."); if (le32_to_cpu(info->bt_longest_key) != 8) report(ctx, "wrong maximum key size in info footer."); if (le32_to_cpu(info->bt_longest_val) != sizeof(struct apfs_omap_snapshot)) report(ctx, "wrong maximum value size in info footer."); return; } if (btree_is_fext(btree)) { if (le32_to_cpu(info->bt_fixed.bt_key_size) != sizeof(struct apfs_fext_tree_key)) report(ctx, "wrong key size in info footer."); if (le32_to_cpu(info->bt_fixed.bt_val_size) != sizeof(struct apfs_fext_tree_val)) report(ctx, "wrong value size in info footer."); if (le32_to_cpu(info->bt_longest_key) != sizeof(struct apfs_fext_tree_key)) report(ctx, "wrong maximum key size in info footer."); if (le32_to_cpu(info->bt_longest_val) != sizeof(struct apfs_fext_tree_val)) report(ctx, "wrong maximum value size in info footer."); return; } if (btree_is_fusion_mt(btree)) { u32 longest_key = le32_to_cpu(info->bt_longest_key); u32 longest_val = le32_to_cpu(info->bt_longest_val); if (le32_to_cpu(info->bt_fixed.bt_key_size) != sizeof(struct apfs_fusion_mt_key)) report(ctx, "wrong key size in info footer."); if (le32_to_cpu(info->bt_fixed.bt_val_size) != sizeof(struct apfs_fusion_mt_val)) report(ctx, "wrong value size in info footer."); /* The fusion middle-tree may be empty */ if ((longest_key || btree->key_count) && longest_key != sizeof(struct apfs_fusion_mt_key)) report(ctx, "wrong maximum key size in info footer."); if ((longest_val || btree->key_count) && longest_val != sizeof(struct apfs_fusion_mt_val)) report(ctx, "wrong maximum value size in info footer."); return; } /* The remaining trees don't report fixed key/value sizes */ if (le32_to_cpu(info->bt_fixed.bt_key_size) != 0) report(ctx, "key size should not be set."); if (le32_to_cpu(info->bt_fixed.bt_val_size) != 0) report(ctx, "value size should not be set."); if (btree_is_catalog(btree) || btree_is_snap_meta(btree)) { if (le32_to_cpu(info->bt_longest_key) < btree->longest_key) report(ctx, "wrong maximum key size in info footer."); if (le32_to_cpu(info->bt_longest_val) < btree->longest_val) report(ctx, "wrong maximum value size in info footer."); return; } /* * The extentref only seems to have records of this one type. * No idea why it reports keys/values of variable size... */ if (btree_is_extentref(btree)) { u32 longest_key = le32_to_cpu(info->bt_longest_key); u32 longest_val = le32_to_cpu(info->bt_longest_val); if ((longest_key || btree->key_count) && longest_key != sizeof(struct apfs_phys_ext_key)) report(ctx, "wrong maximum key size in info footer."); if ((longest_val || btree->key_count) && longest_val != sizeof(struct apfs_phys_ext_val)) report(ctx, "wrong maximum val size in info footer."); } } /** * parse_free_queue_btree - Parse and check a free-space queue tree * @oid: object id for the b-tree root * @index: position of this free queue in the free queue array * * Returns a pointer to the free queue structure. */ struct free_queue *parse_free_queue_btree(u64 oid, int index) { struct free_queue *sfq; struct btree *btree; struct key last_key = {0}; sfq = calloc(1, sizeof(*sfq)); if (!sfq) system_error(); btree = &sfq->sfq_btree; sfq->sfq_index = index; if (oid == 0) { /* I've seen null fq's in fresh containers with no volumes */ return sfq; } btree->type = BTREE_TYPE_FREE_QUEUE; btree->omap_table = NULL; /* These are ephemeral objects */ btree->root = read_node(oid, btree, NULL /* hash */); parse_subtree(btree->root, &last_key, NULL /* name_buf */); check_btree_footer(btree); return sfq; } /** * parse_snap_meta_btree - Parse and check a snapshot metadata tree * @oid: object id for the b-tree root * * Returns a pointer to the btree struct for the snapshot metadata tree. */ struct btree *parse_snap_meta_btree(u64 oid) { struct btree *snap; struct key last_key = {0}; char name_buf[256]; snap = calloc(1, sizeof(*snap)); if (!snap) system_error(); snap->type = BTREE_TYPE_SNAP_META; snap->omap_table = NULL; /* These are physical objects */ snap->root = read_node(oid, snap, NULL /* hash */); parse_subtree(snap->root, &last_key, name_buf); check_btree_footer(snap); return snap; } /** * parse_cat_btree - Parse a catalog tree and check for corruption * @oid: object id for the catalog root * @omap_table: hash table of object map records for the b-tree * * Returns a pointer to the btree struct for the catalog. */ struct btree *parse_cat_btree(u64 oid, struct htable_entry **omap_table) { struct btree *cat; struct key last_key = {0}; char name_buf[256]; cat = calloc(1, sizeof(*cat)); if (!cat) system_error(); /* * We need to set this here so that parse_xattr_record() can read * dstream contents. Maybe all parse_*_btree() functions should do * this, for consistency... */ vsb->v_cat = cat; cat->type = BTREE_TYPE_CATALOG; cat->omap_table = omap_table; cat->root = read_node(oid, cat, apfs_volume_is_sealed() ? vsb->v_hash : NULL); parse_subtree(cat->root, &last_key, name_buf); check_btree_footer(cat); return cat; } /** * parse_fext_btree - Parse a fext tree and check for corruption * @oid: object id for the fext root * * Returns a pointer to the btree struct for the catalog. */ struct btree *parse_fext_btree(u64 oid) { struct btree *fext = NULL; struct key last_key = {0}; fext = calloc(1, sizeof(*fext)); if (!fext) system_error(); fext->type = BTREE_TYPE_FEXT; fext->omap_table = NULL; fext->root = read_node(oid, fext, NULL /* hash */); parse_subtree(fext->root, &last_key, NULL); check_btree_footer(fext); return fext; } /** * check_omap_flags - Check consistency of object map flags * @flags: the flags */ static void check_omap_flags(u32 flags) { if ((flags & APFS_OMAP_FLAGS_VALID_MASK) != flags) report("Object map", "invalid flag in use."); if (flags & (APFS_OMAP_ENCRYPTING | APFS_OMAP_DECRYPTING | APFS_OMAP_KEYROLLING | APFS_OMAP_CRYPTO_GENERATION)) report_unknown("Omap encryption"); if (vsb && (flags & APFS_OMAP_MANUALLY_MANAGED)) report("Volume object map", "is manually managed."); if (!vsb && !(flags & APFS_OMAP_MANUALLY_MANAGED)) report("Container object map", "isn't manually managed."); } static struct btree *parse_snapshot_tree(u64 oid) { struct btree *snaps = NULL; struct key last_key = {0}; snaps = calloc(1, sizeof(*snaps)); if (!snaps) system_error(); snaps->type = BTREE_TYPE_SNAPSHOTS; snaps->omap_table = NULL; snaps->root = read_node(oid, snaps, NULL /* hash */); parse_subtree(snaps->root, &last_key, NULL); check_btree_footer(snaps); return snaps; } /** * parse_omap_btree - Parse an object map and check for corruption * @oid: object id for the omap * * Returns a pointer to the btree struct for the omap. */ struct btree *parse_omap_btree(u64 oid) { struct apfs_omap_phys *raw; struct btree *omap; struct key last_key = {0}; struct object obj; /* Many checks are missing, of course */ raw = read_object(oid, NULL /* omap_table */, &obj); if (obj.type != APFS_OBJECT_TYPE_OMAP) report("Object map", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Object map", "wrong object subtype."); check_omap_flags(le32_to_cpu(raw->om_flags)); if (raw->om_snapshot_tree_oid) { if (!vsb) report("Container omap", "has snapshot tree."); vsb->v_snapshots = parse_snapshot_tree(le64_to_cpu(raw->om_snapshot_tree_oid)); if (vsb->v_snapshots->key_count != le32_to_cpu(raw->om_snap_count)) report("Omap snapshot tree", "snap count doesn't match keys."); if (vsb->v_snap_max_xid != le64_to_cpu(raw->om_most_recent_snap)) report("Omap snapshot tree", "latest xid doesn't match keys."); } else if (raw->om_snap_count || raw->om_most_recent_snap) { report("Object map", "has snapshots but no snapshot tree."); } /* Oddly, the type is still reported even when the tree is not set */ if (le32_to_cpu(raw->om_snapshot_tree_type) != (APFS_OBJECT_TYPE_BTREE | APFS_OBJ_PHYSICAL)) report("Object map", "wrong type for snapshot tree."); if (raw->om_pending_revert_min || raw->om_pending_revert_max) report_unknown("Revert in progress"); omap = calloc(1, sizeof(*omap)); if (!omap) system_error(); omap->type = BTREE_TYPE_OMAP; omap->omap_table = NULL; /* The omap doesn't have an omap of its own */ omap->root = read_node(le64_to_cpu(raw->om_tree_oid), omap, NULL /* hash */); /* The tree type reported by the omap must match the root node */ if (raw->om_tree_type != omap->root->raw->btn_o.o_type) report("Object map", "wrong type for tree."); parse_subtree(omap->root, &last_key, NULL /* name_buf */); check_btree_footer(omap); munmap(raw, obj.size); return omap; } /** * parse_fusion_middle_tree - Parse a fusion mt and check for corruption * @oid: object id for the fusion middle-tree * * Returns a pointer to the btree struct for the fusion middle-tree. */ struct btree *parse_fusion_middle_tree(u64 oid) { struct btree *mt = NULL; struct key last_key = {0}; if (apfs_is_fusion_drive() != (bool)oid) report("Fusion middle-tree", "oid incorrectly set/unset."); if (!oid) return NULL; mt = calloc(1, sizeof(*mt)); if (!mt) system_error(); mt->type = BTREE_TYPE_FUSION_MT; mt->omap_table = NULL; mt->root = read_node(oid, mt, NULL /* hash */); parse_subtree(mt->root, &last_key, NULL); check_btree_footer(mt); return mt; } /** * parse_extentref_btree - Parse and check an extent reference tree * @oid: object id for the b-tree root * * Returns a pointer to the btree struct for the extent reference tree. */ struct btree *parse_extentref_btree(u64 oid) { struct btree *extref; struct key last_key = {0}; extref = calloc(1, sizeof(*extref)); if (!extref) system_error(); extref->type = BTREE_TYPE_EXTENTREF; extref->omap_table = NULL; /* These are physical objects */ extref->root = read_node(oid, extref, NULL /* hash */); parse_subtree(extref->root, &last_key, NULL /* name_buf */); check_btree_footer(extref); return extref; } /** * child_from_query - Read the child id found by a successful nonleaf query * @query: the query that found the record * * Returns the child id in the nonleaf node record. */ static u64 child_from_query(struct query *query) { void *raw = query->node->raw; /* This check is actually redundant, at least for now */ if (query->len != 8) /* The data on a nonleaf node is the child id */ report("B-tree", "wrong size of nonleaf record value."); return le64_to_cpu(*(__le64 *)(raw + query->off)); } /** * extref_rec_from_query - Read the info found by a successful extentref query * @query: the query for the extent reference record * @extref: extent reference record struct to receive the result */ static void extref_rec_from_query(struct query *query, struct extref_record *extref) { struct apfs_phys_ext_val *extref_val; struct apfs_phys_ext_key *extref_key; void *raw = query->node->raw; int kind; if (query->len != sizeof(*extref_val)) report("Extent reference record", "wrong size of value."); extref_val = (struct apfs_phys_ext_val *)(raw + query->off); extref_key = (struct apfs_phys_ext_key *)(raw + query->key_off); /* The physical address is used as the id in the extentref tree */ extref->phys_addr = cat_cnid(&extref_key->hdr); extref->blocks = le64_to_cpu(extref_val->len_and_kind) & APFS_PEXT_LEN_MASK; extref->owner = le64_to_cpu(extref_val->owning_obj_id); extref->refcnt = le32_to_cpu(extref_val->refcnt); kind = le64_to_cpu(extref_val->len_and_kind) >> APFS_PEXT_KIND_SHIFT; extref->update = kind == APFS_KIND_UPDATE; } /* * In-memory fext record */ struct fext_record { u64 log_addr; /* Logical address */ u64 phys_bno; /* Physical block number */ u64 length; /* Length (in bytes) */ }; /** * fext_rec_from_query - Read the info found by a successful fext query * @query: the query for the fext record * @fext: fext record struct to receive the result */ static void fext_rec_from_query(struct query *query, struct fext_record *fext) { struct apfs_fext_tree_val *fext_val = NULL; struct apfs_fext_tree_key *fext_key = NULL; void *raw = query->node->raw; if (query->len != sizeof(*fext_val)) report("Fext record", "wrong size of value."); fext_val = (struct apfs_fext_tree_val *)(raw + query->off); fext_key = (struct apfs_fext_tree_key *)(raw + query->key_off); fext->log_addr = le64_to_cpu(fext_key->logical_addr); fext->phys_bno = le64_to_cpu(fext_val->phys_block_num); fext->length = le64_to_cpu(fext_val->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; } /** * fext_tree_lookup - Map a logical address to a physical one in a sealed volume * @oid: dstream id * @logaddr: logical address inside the dstream * @bno: on return, the physical block number * * Returns 0 on success, or -1 if nothing was found. */ int fext_tree_lookup(u64 oid, u64 logaddr, u64 *bno) { struct query *query = NULL; struct key key = {0}; struct fext_record fext = {0}; u64 off; int ret = -1; query = alloc_query(vsb->v_fext->root, NULL /* parent */); init_fext_key(oid, logaddr, &key); query->key = &key; query->flags |= QUERY_FEXT; /* * The fext nodes have already been parsed, and the allocation * bitmap has been updated accordingly. This global variable tells * read_object() to ignore the bitmap this time. */ ongoing_query = true; if (btree_query(&query)) goto fail; ongoing_query = false; fext_rec_from_query(query, &fext); if (fext.phys_bno == 0) { *bno = 0; } else { off = (logaddr - fext.log_addr) >> sb->s_blocksize_bits; *bno = fext.phys_bno + off; } ret = 0; fail: free_query(query); return ret; } /** * extent_from_query - Read the info found by a successful file extent query * @query: the query for the extent record * @fext: fext record struct to receive the result */ static void extent_from_query(struct query *query, struct fext_record *fext) { struct apfs_file_extent_val *ext; struct apfs_file_extent_key *ext_key; void *raw = query->node->raw; if (query->len != sizeof(*ext)) report("File extent record", "wrong size of value."); ext = (struct apfs_file_extent_val *)(raw + query->off); ext_key = (struct apfs_file_extent_key *)(raw + query->key_off); fext->log_addr = le64_to_cpu(ext_key->logical_addr); fext->phys_bno = le64_to_cpu(ext->phys_block_num); fext->length = le64_to_cpu(ext->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; } /** * file_extent_lookup - Map a logical address to a physical one in an unsealed volume * @oid: dstream id * @logaddr: logical address inside the dstream * @bno: on return, the physical block number * * Returns 0 on success, or -1 if nothing was found. */ int file_extent_lookup(u64 oid, u64 logaddr, u64 *bno) { struct query *query = NULL; struct key key = {0}; struct fext_record fext = {0}; u64 off; int ret = -1; query = alloc_query(vsb->v_cat->root, NULL /* parent */); init_file_extent_key(oid, logaddr, &key); query->key = &key; query->flags |= QUERY_CAT; /* * The catalog nodes have already been parsed, and the allocation * bitmap has been updated accordingly. This global variable tells * read_object() to ignore the bitmap this time. */ ongoing_query = true; if (btree_query(&query)) goto fail; ongoing_query = false; extent_from_query(query, &fext); if (fext.phys_bno == 0) { *bno = 0; } else { off = (logaddr - fext.log_addr) >> sb->s_blocksize_bits; *bno = fext.phys_bno + off; } ret = 0; fail: free_query(query); return ret; } /** * extentref_tree_lookup - Find best match for an extent in an extentref tree * @tbl: root of the extent reference tree to be searched * @bno: first block number for the extent * @extref: extentref record struct to receive the result * * Returns 0 on success, or -1 if nothing was found. */ static int extentref_tree_lookup(struct node *tbl, u64 bno, struct extref_record *extref) { struct query *query; struct key key; int ret = -1; query = alloc_query(tbl, NULL /* parent */); init_extref_key(bno, &key); query->key = &key; query->flags |= QUERY_EXTENTREF; /* * The extentref nodes have already been parsed, and the allocation * bitmap has been updated accordingly. This global variable tells * read_object() to ignore the bitmap this time. */ ongoing_query = true; if (btree_query(&query)) goto fail; ongoing_query = false; extref_rec_from_query(query, extref); ret = 0; fail: free_query(query); return ret; } /** * extentref_update_lookup - Find latest snap phys extent for an updated block * @bno: block number * @extref: extentref record struct to receive the result */ void extentref_update_lookup(u64 bno, struct extref_record *extref) { struct listed_btree *ext_tree = NULL; int ret; /* We look at the most recent snapshots first */ for (ext_tree = vsb->v_snap_extrefs; ext_tree; ext_tree = ext_tree->next) { ret = extentref_tree_lookup(ext_tree->btree->root, bno, extref); if (ret == 0 && extref->phys_addr <= bno && extref->phys_addr + extref->blocks > bno) return; } report("Physical extent record", "update of nonexistent record."); } /** * alloc_query - Allocates a query structure * @node: node to be searched * @parent: query for the parent node * * Callers other than btree_query() should set @parent to NULL, and @node * to the root of the b-tree. They should also initialize most of the query * fields themselves; when @parent is not NULL the query will inherit them. * * Returns the allocated query. */ struct query *alloc_query(struct node *node, struct query *parent) { struct query *query; query = malloc(sizeof(*query)); if (!query) system_error(); query->node = node; query->key = parent ? parent->key : NULL; query->flags = parent ? parent->flags & ~(QUERY_DONE | QUERY_NEXT) : 0; query->parent = parent; /* Start the search with the last record and go backwards */ query->index = node->records; query->depth = parent ? parent->depth + 1 : 0; return query; } /** * free_query - Free a query structure * @query: query to free * * Also frees the ancestor queries, if they are kept. */ void free_query(struct query *query) { while (query) { struct query *parent = query->parent; node_free(query->node); free(query); query = parent; } } /** * key_from_query - Read the current key from a query structure * @query: the query, with @query->key_off and @query->key_len already set * @key: return parameter for the key * * Reads the key into @key after some basic sanity checks. */ static void key_from_query(struct query *query, struct key *key) { void *raw = query->node->raw; void *raw_key = (void *)(raw + query->key_off); switch (query->flags & QUERY_TREE_MASK) { case QUERY_CAT: read_cat_key(raw_key, query->key_len, key); break; case QUERY_OMAP: read_omap_key(raw_key, query->key_len, key); break; case QUERY_EXTENTREF: read_extentref_key(raw_key, query->key_len, key); break; case QUERY_FEXT: read_fext_key(raw_key, query->key_len, key); break; default: report(NULL, "Bug!"); } if (query->flags & QUERY_MULTIPLE) { /* A multiple query must ignore these fields */ key->number = 0; key->name = NULL; } } /** * node_next - Find the next matching record in the current node * @query: multiple query in execution * * Returns 0 on success, -EAGAIN if the next record is in another node, and * -ENODATA if no more matching records exist. */ static int node_next(struct query *query) { struct node *node = query->node; struct key curr_key; int cmp; u64 bno = node->object.block_nr; if (query->flags & QUERY_DONE) /* Nothing left to search; the query failed */ return -ENODATA; if (!query->index) /* The next record may be in another node */ return -EAGAIN; --query->index; query->key_len = node_locate_key(node, query->index, &query->key_off); key_from_query(query, &curr_key); cmp = keycmp(&curr_key, query->key); if (cmp > 0) report("B-tree", "records are out of order."); if (cmp != 0 && node_is_leaf(node) && query->flags & QUERY_EXACT) return -ENODATA; query->len = node_locate_data(node, query->index, &query->off); if (query->len == 0) { report("B-tree", "corrupted record value in node 0x%llx.", (unsigned long long)bno); } if (cmp != 0) { /* * This is the last entry that can be relevant in this node. * Keep searching the children, but don't return to this level. */ query->flags |= QUERY_DONE; } return 0; } /** * node_query - Execute a query on a single node * @query: the query to execute * * The search will start at index @query->index, looking for the key that comes * right before @query->key, according to the order given by keycmp(). * * The @query->index will be updated to the last index checked. This is * important when searching for multiple entries, since the query may need * to remember where it was on this level. If we are done with this node, the * query will be flagged as QUERY_DONE, and the search will end in failure * as soon as we return to this level. The function may also return -EAGAIN, * to signal that the search should go on in a different branch. * * On success returns 0; the offset of the data within the block will be saved * in @query->off, and its length in @query->len. The function checks that this * length fits within the block; callers must use the returned value to make * sure they never operate outside its bounds. * * -ENODATA will be returned if no appropriate entry was found. */ static int node_query(struct query *query) { struct node *node = query->node; int left, right; int cmp; u64 bno = node->object.block_nr; if (query->flags & QUERY_NEXT) return node_next(query); /* Search by bisection */ cmp = 1; left = 0; do { struct key curr_key; if (cmp > 0) { right = query->index - 1; if (right < left) return -ENODATA; query->index = (left + right) / 2; } else { left = query->index; query->index = DIV_ROUND_UP(left + right, 2); } query->key_len = node_locate_key(node, query->index, &query->key_off); key_from_query(query, &curr_key); cmp = keycmp(&curr_key, query->key); if (cmp == 0 && !(query->flags & QUERY_MULTIPLE)) break; } while (left != right); if (cmp > 0) return -ENODATA; if (cmp != 0 && node_is_leaf(query->node) && query->flags & QUERY_EXACT) return -ENODATA; if (query->flags & QUERY_MULTIPLE) { if (cmp != 0) /* Last relevant entry in level */ query->flags |= QUERY_DONE; query->flags |= QUERY_NEXT; } query->len = node_locate_data(node, query->index, &query->off); if (query->len == 0) { report("B-tree", "corrupted record value in node 0x%llx.", (unsigned long long)bno); } return 0; } /** * btree_query - Execute a query on a b-tree * @query: the query to execute * * Searches the b-tree starting at @query->index in @query->node, looking for * the record corresponding to @query->key. * * Returns 0 in case of success and sets the @query->len, @query->off and * @query->index fields to the results of the query. @query->node will now * point to the leaf node holding the record. * * In case of failure returns -ENODATA. */ int btree_query(struct query **query) { struct node *node = (*query)->node; struct query *parent; struct btree *btree = node->btree; u64 child_id; int err; assert(ongoing_query); next_node: if ((*query)->depth >= 12) { /* This is the maximum depth allowed by the module */ report("B-tree", "is too deep."); } err = node_query(*query); if (err == -EAGAIN) { if (!(*query)->parent) /* We are at the root of the tree */ return -ENODATA; /* Move back up one level and continue the query */ parent = (*query)->parent; (*query)->parent = NULL; /* Don't free the parent */ free_query(*query); *query = parent; goto next_node; } if (err) return err; if (node_is_leaf((*query)->node)) /* All done */ return 0; /* Now go a level deeper and search the child */ child_id = child_from_query(*query); node = read_node(child_id, btree, NULL /* hash */); if ((*query)->flags & QUERY_MULTIPLE) { /* * We are looking for multiple entries, so we must remember * the parent node and index to continue the search later. */ *query = alloc_query(node, *query); } else { /* Reuse the same query structure to search the child */ node_free((*query)->node); (*query)->node = node; (*query)->index = node->records; (*query)->depth++; } goto next_node; } apfsprogs-0.2.0/apfsck/btree.h000066400000000000000000000162021471277137200162520ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _BTREE_H #define _BTREE_H #include #include #include "htable.h" #include "object.h" struct super_block; struct extref_record; struct free_queue; /* * Omap record data in memory */ struct omap_record { struct omap_record *next; /* Next entry in linked list */ u64 xid; /* Transaction id */ u64 oid; /* Virtual object id */ u64 bno; /* Block number */ u32 flags; /* Omap record flags */ /* Was this oid-xid pair ever seen in use? */ bool seen; /* Was it ever seen in use for the latest checkpoint? */ bool seen_for_latest; /* If we are currently checking a snapshot, was it seen in use for it? */ bool seen_for_snap; }; /* * List of all omap records for a given oid */ struct omap_record_list { struct htable_entry o_htable; /* Hash table entry header */ struct omap_record *o_records; /* Linked list of records */ }; /* * In-memory representation of an APFS node */ struct node { u16 flags; /* Node flags */ u32 records; /* Number of records in the node */ int level; /* Number of child levels below this node */ int toc; /* Offset of the TOC in the block */ int key; /* Offset of the key area in the block */ int free; /* Offset of the free area in the block */ int data; /* Offset of the data area in the block */ u8 *free_key_bmap; /* Free space bitmap for the key area */ u8 *free_val_bmap; /* Free space bitmap for the value area */ u8 *used_key_bmap; /* Used space bitmap for the key area */ u8 *used_val_bmap; /* Used space bitmap for the value area */ struct btree *btree; /* Btree the node belongs to */ struct apfs_btree_node_phys *raw; /* Raw node in memory */ struct object object; /* Object holding the node */ }; /** * apfs_node_is_leaf - Check if a b-tree node is a leaf * @node: the node to check */ static inline bool node_is_leaf(struct node *node) { return (node->flags & APFS_BTNODE_LEAF) != 0; } /** * apfs_node_is_root - Check if a b-tree node is the root * @node: the node to check */ static inline bool node_is_root(struct node *node) { return (node->flags & APFS_BTNODE_ROOT) != 0; } /** * apfs_node_has_fixed_kv_size - Check if a b-tree node has fixed key/value * sizes * @node: the node to check */ static inline bool node_has_fixed_kv_size(struct node *node) { return (node->flags & APFS_BTNODE_FIXED_KV_SIZE) != 0; } /* Flags for the query structure */ #define QUERY_TREE_MASK 0017 /* Which b-tree we query */ #define QUERY_OMAP 0001 /* This is a b-tree object map query */ #define QUERY_CAT 0002 /* This is a catalog tree query */ #define QUERY_EXTENTREF 0004 /* This is an extentref tree query */ #define QUERY_FEXT 0010 /* This is an extentref tree query */ #define QUERY_MULTIPLE 0020 /* Search for multiple matches */ #define QUERY_NEXT 0040 /* Find next of multiple matches */ #define QUERY_EXACT 0100 /* Search for an exact match */ #define QUERY_DONE 0200 /* The search at this level is over */ /* * Structure used to retrieve data from an APFS B-Tree. For now only used * on the calalog and the object map. */ struct query { struct node *node; /* Node being searched */ struct key *key; /* What the query is looking for */ struct query *parent; /* Query for parent node */ unsigned int flags; /* Set by the query on success */ int index; /* Index of the entry in the node */ int key_off; /* Offset of the key in the node */ int key_len; /* Length of the key */ int off; /* Offset of the data in the node */ int len; /* Length of the data */ int depth; /* Put a limit on recursion */ }; /* In-memory tree types */ #define BTREE_TYPE_OMAP 1 /* The tree is an object map */ #define BTREE_TYPE_CATALOG 2 /* The tree is a catalog */ #define BTREE_TYPE_EXTENTREF 3 /* The tree is for extent references */ #define BTREE_TYPE_SNAP_META 4 /* The tree is for snapshot metadata */ #define BTREE_TYPE_FREE_QUEUE 5 /* The tree is for a free-space queue */ #define BTREE_TYPE_SNAPSHOTS 6 /* The tree is for omap snapshots */ #define BTREE_TYPE_FEXT 7 /* The tree is for file extents */ #define BTREE_TYPE_FUSION_MT 8 /* The is the fusion middle-tree */ /* In-memory structure representing a b-tree */ struct btree { u8 type; /* Type of the tree */ struct node *root; /* Root of this b-tree */ /* Hash table for the tree's object map (can be NULL) */ struct htable_entry **omap_table; /* B-tree stats as measured by the fsck */ u64 key_count; /* Number of keys */ u64 node_count; /* Number of nodes */ int longest_key; /* Length of longest key */ int longest_val; /* Length of longest value */ }; /** * btree_is_free_queue - Check if a b-tree is a free-space queue * @btree: the b-tree to check */ static inline bool btree_is_free_queue(struct btree *btree) { return btree->type == BTREE_TYPE_FREE_QUEUE; } static inline bool btree_is_snapshots(struct btree *btree) { return btree->type == BTREE_TYPE_SNAPSHOTS; } /** * btree_is_omap - Check if a b-tree is an object map * @btree: the b-tree to check */ static inline bool btree_is_omap(struct btree *btree) { return btree->type == BTREE_TYPE_OMAP; } /** * btree_is_snap_meta - Check if a b-tree is for snapshot metadata * @btree: the b-tree to check */ static inline bool btree_is_snap_meta(struct btree *btree) { return btree->type == BTREE_TYPE_SNAP_META; } /** * btree_is_catalog - Check if a b-tree is a catalog * @btree: the b-tree to check */ static inline bool btree_is_catalog(struct btree *btree) { return btree->type == BTREE_TYPE_CATALOG; } /** * btree_is_extentref - Check if a b-tree is for extent references * @btree: the b-tree to check */ static inline bool btree_is_extentref(struct btree *btree) { return btree->type == BTREE_TYPE_EXTENTREF; } /** * btree_is_fext - Check if a b-tree is for file extents * @btree: the b-tree to check */ static inline bool btree_is_fext(struct btree *btree) { return btree->type == BTREE_TYPE_FEXT; } /** * btree_is_fusion_mt - Check if a b-tree is a fusion middle-tree * @btree: the b-tree to check */ static inline bool btree_is_fusion_mt(struct btree *btree) { return btree->type == BTREE_TYPE_FUSION_MT; } extern struct free_queue *parse_free_queue_btree(u64 oid, int index); extern struct btree *parse_snap_meta_btree(u64 oid); extern struct btree *parse_extentref_btree(u64 oid); extern struct btree *parse_omap_btree(u64 oid); extern struct btree *parse_cat_btree(u64 oid, struct htable_entry **omap_table); extern struct btree *parse_fext_btree(u64 oid); extern struct btree *parse_fusion_middle_tree(u64 oid); extern struct query *alloc_query(struct node *node, struct query *parent); extern void free_query(struct query *query); extern int btree_query(struct query **query); extern struct node *omap_read_node(u64 id); extern void free_omap_table(struct htable_entry **table); extern struct omap_record *get_latest_omap_record(u64 oid, u64 xid, struct htable_entry **table); extern void extentref_update_lookup(u64 bno, struct extref_record *extref); extern int fext_tree_lookup(u64 oid, u64 logaddr, u64 *bno); extern int file_extent_lookup(u64 oid, u64 logaddr, u64 *bno); extern void omap_htable_clear_seen_for_snap(struct htable_entry **table); #endif /* _BTREE_H */ apfsprogs-0.2.0/apfsck/compress.c000066400000000000000000000267631471277137200170140ustar00rootroot00000000000000/* * Copyright (C) 2020 Corellium LLC */ #include #include #include #include #include "apfs/raw.h" #include "apfs/libzbitmap.h" #include "apfs/zlib_inflate/zlib.h" #include "apfsck.h" #include "btree.h" #include "compress.h" #include "extents.h" #include "super.h" /* maximum size of compressed data currently supported */ #define MAX_FBUF_SIZE (1024 * 1024 * 1024) void read_whole_dstream(u64 oid, void *buf, loff_t size) { void *block = NULL; u64 curr_copylen; u64 bno; int ret; u64 i; i = 0; while(size) { curr_copylen = MIN(size, sb->s_blocksize); if(apfs_volume_is_sealed()) ret = fext_tree_lookup(oid, i << sb->s_blocksize_bits, &bno); else ret = file_extent_lookup(oid, i << sb->s_blocksize_bits, &bno); if(ret) report("Compressed file", "dstream read failed."); if(bno == 0) report("Compressed file", "has a hole."); block = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if(block == MAP_FAILED) system_error(); memcpy(buf, block, curr_copylen); munmap(block, sb->s_blocksize); block = NULL; size -= curr_copylen; buf += curr_copylen; ++i; } } static inline bool apfs_compressed_in_dstream(struct compress *compress) { /* For sealed volumes we may use a fake dstream that only holds the hash */ return compress->rsrc_dstream && !compress->rsrc_dstream->d_inline; } void apfs_compress_open(struct compress *compress) { struct dstream *rsrc = compress->rsrc_dstream; struct apfs_compress_file_data *fd; ssize_t res; u8 *cdata; ssize_t csize; fd = calloc(1, sizeof(*fd)); if(!fd) system_error(); if(!compress->decmpfs) report("Compressed file", "missing decmpfs xattr."); memcpy(&fd->hdr, compress->decmpfs, sizeof(fd->hdr)); if(apfs_compressed_in_dstream(compress) && le32_to_cpu(fd->hdr.algo) != APFS_COMPRESS_LZBITMAP_RSRC) { struct apfs_compress_rsrc_hdr *rsrc_hdr = NULL; struct apfs_compress_rsrc_data *rsrc_data = NULL; u32 data_end; fd->buf = malloc(APFS_COMPRESS_BLOCK); if(!fd->buf) system_error(); fd->bufblk = -1; if(rsrc->d_size > MAX_FBUF_SIZE) report_unknown("Large compressed file"); fd->size = rsrc->d_size; fd->data = malloc(fd->size); if(!fd->data) system_error(); read_whole_dstream(rsrc->d_id, fd->data, fd->size); if(fd->size < sizeof(*rsrc_hdr)) report("Resource compressed file", "header won't fit."); rsrc_hdr = fd->data; /* TODO: check for overlaps, figure out 'mgmt' */ compress->data_offs = be32_to_cpu(rsrc_hdr->data_offs); compress->data_size = be32_to_cpu(rsrc_hdr->data_size); data_end = compress->data_offs + compress->data_size; if(data_end < compress->data_offs || data_end > fd->size) report("Resource compressed file", "block metadata is too big."); if(compress->data_size < sizeof(*rsrc_data)) report("Resource compressed file", "block metadata header won't fit."); rsrc_data = fd->data + compress->data_offs; compress->block_num = le32_to_cpu(rsrc_data->num); if(compress->block_num > MAX_FBUF_SIZE) /* Rough bound to avoid overflow */ report_unknown("Large compressed file"); if(compress->data_size < compress->block_num * sizeof(rsrc_data->block[0]) + sizeof(*rsrc_data)) report("Resource compressed file", "block metadata won't fit."); /* TODO: figure out the 'unknown' field */ } else if(apfs_compressed_in_dstream(compress)) { __le32 *block_offs; int i; fd->buf = malloc(APFS_COMPRESS_BLOCK); if(!fd->buf) system_error(); fd->bufblk = -1; if(rsrc->d_size > MAX_FBUF_SIZE) report_unknown("Large compressed file"); fd->size = rsrc->d_size; fd->data = malloc(fd->size); if(!fd->data) system_error(); read_whole_dstream(rsrc->d_id, fd->data, fd->size); block_offs = fd->data; compress->data_offs = 0; compress->data_size = fd->size; /* Put a rough bound on block count to avoid overflow */ for(i = 0; i < MAX_FBUF_SIZE; ++i) { if((i + 1) * sizeof(*block_offs) >= fd->size) report("LZBITMAP-compressed file", "block offsets won't fit."); if(le32_to_cpu(block_offs[i]) == fd->size) break; } compress->block_num = i; if(compress->block_num == MAX_FBUF_SIZE) report("LZBITMAP-compressed file", "missing final block offset."); } else { if(le64_to_cpu(fd->hdr.size) > MAX_FBUF_SIZE) report("Inline compressed file", "size is too big."); fd->size = le64_to_cpu(fd->hdr.size); fd->data = malloc(le64_to_cpu(fd->hdr.size)); if(!fd->data) system_error(); cdata = compress->decmpfs + sizeof(fd->hdr); csize = compress->decmpfs_len - sizeof(fd->hdr); compress->block_num = 1; switch(le32_to_cpu(fd->hdr.algo)) { case APFS_COMPRESS_ZLIB_ATTR: if(cdata[0] == 0x78 && csize >= 2) { res = zlib_inflate_blob(fd->data, fd->size, cdata + 2, csize - 2); if(res != fd->size) report("Inline compressed file", "wrong reported length."); } else if((cdata[0] & 0x0F) == 0x0F) { if(csize - 1 != fd->size) report("Inline compressed file", "wrong reported length."); memcpy(fd->data, cdata + 1, csize - 1); } else { report("Inline compressed file", "invalid header for zlib."); } break; case APFS_COMPRESS_PLAIN_ATTR: if(csize - 1 != fd->size) report("Inline uncompressed file", "wron reported length."); memcpy(fd->data, cdata + 1, csize - 1); break; default: report_unknown("Compression algorithm"); } } compress->compress_data = fd; } /* If @buf is NULL, just return the size */ static ssize_t apfs_compress_read_block(struct compress *compress, char *buf, size_t size, loff_t off) { struct apfs_compress_file_data *fd = compress->compress_data; u32 doffs, coffs; loff_t block; u8 *cdata, *tmp = fd->buf; size_t csize, bsize; ssize_t res; if(off >= (loff_t)le64_to_cpu(fd->hdr.size)) return 0; if(size > le64_to_cpu(fd->hdr.size) - (size_t)off) size = le64_to_cpu(fd->hdr.size) - off; block = off / APFS_COMPRESS_BLOCK; off -= block * APFS_COMPRESS_BLOCK; if(block != fd->bufblk) { doffs = compress->data_offs; if(block >= compress->block_num) return 0; if(le32_to_cpu(fd->hdr.algo) != APFS_COMPRESS_LZBITMAP_RSRC) { struct apfs_compress_rsrc_data *cd = fd->data + doffs; bsize = le64_to_cpu(fd->hdr.size) - block * APFS_COMPRESS_BLOCK; if(bsize > APFS_COMPRESS_BLOCK) bsize = APFS_COMPRESS_BLOCK; csize = le32_to_cpu(cd->block[block].size); coffs = le32_to_cpu(cd->block[block].offs) + 4; } else { __le32 *block_offs = fd->data + doffs; bsize = le64_to_cpu(fd->hdr.size) - block * APFS_COMPRESS_BLOCK; if(bsize > APFS_COMPRESS_BLOCK) bsize = APFS_COMPRESS_BLOCK; coffs = le32_to_cpu(block_offs[block]); csize = le32_to_cpu(block_offs[block + 1]) - coffs; } if(coffs >= fd->size - doffs || fd->size - doffs - coffs < (loff_t)csize || csize > APFS_COMPRESS_BLOCK + 1) report("Resource compressed file", "invalid block size or position."); cdata = fd->data + doffs + coffs; switch(le32_to_cpu(fd->hdr.algo)) { case APFS_COMPRESS_ZLIB_RSRC: if(cdata[0] == 0x78 && csize >= 2) { res = zlib_inflate_blob(tmp, bsize, cdata + 2, csize - 2); if(res < 0) report("Resource compressed file", "invalid compression."); bsize = res; } else if((cdata[0] & 0x0F) == 0x0F) { memcpy(tmp, &cdata[1], csize - 1); bsize = csize - 1; } else { report("Resource compressed file", "invalid header for zlib."); } break; case APFS_COMPRESS_LZBITMAP_RSRC: if(cdata[0] == 0x5a) { res = zbm_decompress(tmp, bsize, cdata, csize, &bsize); if(res < 0) report("LZBITMAP compressed file", "invalid compression."); } else if((cdata[0] & 0x0F) == 0x0F) { memcpy(tmp, &cdata[1], csize - 1); bsize = csize - 1; } else { report("LZBITMAP compressed file", "invalid header."); } break; case APFS_COMPRESS_PLAIN_RSRC: memcpy(tmp, &cdata[1], csize - 1); bsize = csize - 1; break; default: return -EINVAL; } fd->bufblk = block; fd->bufsize = bsize; } else bsize = fd->bufsize; if (block != compress->block_num - 1 && bsize != APFS_COMPRESS_BLOCK) report("Resource compressed file", "wrong size for uncompressed block."); if(bsize < (size_t)off) return 0; bsize -= off; if(size > bsize) size = bsize; if(buf) memcpy(buf, tmp + off, size); return size; } ssize_t apfs_compress_read(struct compress *compress, char *buf, size_t size, loff_t *off) { struct apfs_compress_file_data *fd = compress->compress_data; loff_t step; ssize_t block, res; if(apfs_compressed_in_dstream(compress)) { step = 0; while(!buf || step < (int64_t)size) { block = APFS_COMPRESS_BLOCK - ((*off + step) & (APFS_COMPRESS_BLOCK - 1)); if(buf && block > (int64_t)size - step) block = size - step; res = apfs_compress_read_block(compress, buf ? buf + step : NULL, block, *off + step); if(res < block) { step += res > 0 ? res : 0; break; } step += block; } *off += step; return step; } else { if(!buf) return fd->size; if(*off >= fd->size) return 0; if((int64_t)size > fd->size - *off) size = fd->size - *off; memcpy(buf, fd->data + *off, size); *off += size; return size; } } void apfs_compress_check(struct compress *compress) { ssize_t size; loff_t off = 0; u64 block_num; /* Inline compression was already checked on open */ if (!compress->rsrc_dstream) return; size = apfs_compress_read(compress, NULL, 0, &off); if(compress->size != size) report("Resource compressed file", "wrong reported length."); block_num = (compress->size + APFS_COMPRESS_BLOCK - 1) / APFS_COMPRESS_BLOCK; if(block_num != compress->block_num) report("Resource compressed file", "inconsistent block count."); } void apfs_compress_close(struct compress *compress) { struct apfs_compress_file_data *fd = compress->compress_data; if (!fd) return; if(fd->data) free(fd->data); if(fd->buf) free(fd->buf); free(fd); compress->compress_data = NULL; } apfsprogs-0.2.0/apfsck/compress.h000066400000000000000000000017671471277137200170160ustar00rootroot00000000000000#ifndef _COMPRESS_H #define _COMPRESS_H #include struct apfs_compress_file_data { struct apfs_compress_hdr hdr; loff_t size; void *data; u8 *buf; ssize_t bufblk; size_t bufsize; }; struct compress { void *decmpfs; /* Copy of the inline data */ int decmpfs_len; /* Length of the inline data */ u64 size; /* Size of the compressed data */ u64 block_num; /* Reported count of compressed blocks */ u32 data_offs; u32 data_size; /* Data stream for the resource fork (NULL if none) */ struct dstream *rsrc_dstream; /* Internal data for the decompression code */ struct apfs_compress_file_data *compress_data; }; extern void apfs_compress_open(struct compress *compress); extern void apfs_compress_check(struct compress *compress); extern ssize_t apfs_compress_read(struct compress *compress, char *buf, size_t size, loff_t *off); extern void apfs_compress_close(struct compress *compress); extern void read_whole_dstream(u64 oid, void *buf, loff_t size); #endif /* _COMPRESS_H */ apfsprogs-0.2.0/apfsck/crypto.c000066400000000000000000000150711471277137200164670ustar00rootroot00000000000000#include #include #include #include #include #include #include "apfsck.h" #include "crypto.h" #include "spaceman.h" #include "super.h" static void check_volume_key_entry(const u8 *keydata, u16 keylen) { if (keylen != 0x28) report("Volume key entry in keybag", "wrong size."); } static void check_volume_unlock_records_entry(const u8 *keydata, u16 keylen) { struct apfs_prange *loc = NULL; u64 bno, blkcnt; if (keylen != sizeof(*loc)) report("Volume unlock records entry in keybag", "wrong size."); loc = (struct apfs_prange *)keydata; bno = le64_to_cpu(loc->pr_start_paddr); blkcnt = le64_to_cpu(loc->pr_block_count); if (blkcnt != 1) report_unknown("Multiblock keybag"); /* TODO: actually check the volume keybag */ container_bmap_mark_as_used(bno, blkcnt); } /* No idea what any of this may mean, but put some checks in place */ static void check_reserved_f8_entry(const u8 *keydata, u16 keylen) { u8 expected[] = "IMAKEYBAGCOOKIE!"; if (keylen != sizeof(expected) - 1) report_unknown("Reserved F8 entry"); if (memcmp(keydata, expected, keylen) != 0) report_unknown("Reserved F8 entry"); } /** * check_keybag_entry - Check a single entry in the keybag locker * @entry: the entry to check * @remaining: bytes left in the locker * * Returns the length of this entry. */ static u16 check_keybag_entry(struct apfs_keybag_entry *entry, u16 remaining) { u8 *keydata = NULL; u16 keylen, entry_len; if (remaining < sizeof(*entry)) report("Keybag entry", "won't fit in locker."); keylen = le16_to_cpu(entry->ke_keylen); if (keylen > APFS_VOL_KEYBAG_ENTRY_MAX_SIZE - sizeof(*entry)) report("Keybag entry", "is too big."); entry_len = keylen + sizeof(*entry); if (entry_len > remaining) report("Keybag entry", "too big for allocated bytes."); if (entry->padding) report("Keybag entry", "non-zero padding."); keydata = entry->ke_keydata; switch (le16_to_cpu(entry->ke_tag)) { case KB_TAG_VOLUME_KEY: check_volume_key_entry(keydata, keylen); break; case KB_TAG_VOLUME_UNLOCK_RECORDS: check_volume_unlock_records_entry(keydata, keylen); break; case KB_TAG_RESERVED_F8: /* The reference calls this reserved but I've seen it in use */ check_reserved_f8_entry(keydata, keylen); break; case KB_TAG_VOLUME_PASSPHRASE_HINT: case KB_TAG_VOLUME_M_KEY: report("Keybag", "volume entry in container."); case KB_TAG_WRAPPING_M_KEY: report_unknown("Wrapped media key"); break; case KB_TAG_UNKNOWN: report("Keybag entry", "null type."); case KB_TAG_RESERVED_1: report("Keybag entry", "reserved type."); default: report("Keybag entry", "invalid type."); } return entry_len; } static bool keybag_entry_is_null(struct apfs_keybag_entry *entry) { if (!uuid_is_null(entry->ke_uuid)) return false; if (entry->ke_tag || entry->ke_keylen || entry->padding) return false; return true; } static void check_keybag_locker(struct apfs_kb_locker *locker) { struct apfs_keybag_entry *entry = NULL; u16 nkeys, i; u32 nbytes; if (le16_to_cpu(locker->kl_version) != APFS_KEYBAG_VERSION) report("Keybag locker", "wrong version."); if (locker->padding) report("Keybag locker", "non-zero padding."); nkeys = le16_to_cpu(locker->kl_nkeys); nbytes = le32_to_cpu(locker->kl_nbytes); if (nbytes > sb->s_blocksize - sizeof(*locker)) report("Keybag locker", "won't fit in block."); entry = &locker->kl_entries[0]; for (i = 0; i < nkeys; ++i) { u16 entry_len; entry_len = check_keybag_entry(entry, nbytes); entry = (void *)entry + entry_len; nbytes -= entry_len; } /* * So far I've always seen a leftover of 0x18 bytes here. I'm guessing * it's a terminating null entry. */ if (nbytes != sizeof(*entry)) report("Keybag locker", "bad byte count."); if (!keybag_entry_is_null(entry)) report("Keybag locker", "missing null termination."); } static void check_keybag_plaintext(void *raw) { struct apfs_obj_phys *obj = raw; if (!obj_verify_csum(obj)) report("Keybag header", "bad checksum."); /* Keybag objects are special: not physical/virtual/ephemeral */ if (obj->o_oid) report("Keybag header", "has object id."); if (le32_to_cpu(obj->o_type) != 0x6b657973) /* "syek" */ report("Keybag header", "wrong type."); if (obj->o_subtype != 0) report("Keybag header", "wrong subtype."); if (!obj->o_xid || le64_to_cpu(obj->o_xid) > sb->s_xid) report("Keybag header", "bad transaction id."); check_keybag_locker(raw + sizeof(*obj)); } static void check_keybag_plaintext_block(u64 bno) { u8 *plain = NULL; plain = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if (plain == MAP_FAILED) system_error(); check_keybag_plaintext(plain); munmap(plain, sb->s_blocksize); plain = NULL; } /* * The reference claims that the keybag is wrapped with RFC3394, but it actually * uses AES in XTS mode. I got this information from the apfs-fuse sources, so * credit to Simon Gander for figuring it * out. */ static void check_keybag_ciphertext_block(u64 bno) { u8 *uuid = (u8 *)sb->s_raw->nx_uuid; u8 *cipher = NULL, *plain = NULL; u64 sector; /* * The sector number is used for the XTS tweak value. Sectors are * always 512 bytes. */ sector = bno * (sb->s_blocksize / 0x200); cipher = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if (cipher == MAP_FAILED) system_error(); plain = calloc(1, sb->s_blocksize); if (!plain) system_error(); if (aes_xts_decrypt(uuid, uuid, sector, cipher, sb->s_blocksize, plain)) report("Container keybag", "decryption failed."); check_keybag_plaintext(plain); free(plain); plain = NULL; munmap(cipher, sb->s_blocksize); cipher = NULL; } static bool is_plaintext_obj(u64 bno) { void *raw = NULL; bool ret; raw = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if (raw == MAP_FAILED) system_error(); /* It's impossible for the 64-bit checksum to randomly match */ ret = obj_verify_csum(raw); munmap(raw, sb->s_blocksize); return ret; } void check_keybag(u64 bno, u64 blkcnt) { /* TODO: do all containers have a keybag? */ if (!bno) { if (blkcnt) report("Container keybag", "zero length."); return; } if (blkcnt != 1) report_unknown("Multiblock keybag"); /* * I've encountered iOS images with an unencrypted keybag. I have no * idea if this is reported elsewhere (TODO), so for now just check if * it looks like plaintext or not. */ if (is_plaintext_obj(bno)) check_keybag_plaintext_block(bno); else check_keybag_ciphertext_block(bno); container_bmap_mark_as_used(bno, blkcnt); } apfsprogs-0.2.0/apfsck/crypto.h000066400000000000000000000002601471277137200164660ustar00rootroot00000000000000#ifndef _CRYPTO_H #define _CRYPTO_H #include #include #include extern void check_keybag(u64 bno, u64 count); #endif /* _CRYPTO_H */ apfsprogs-0.2.0/apfsck/dir.c000066400000000000000000000205501471277137200157230ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include "apfsck.h" #include "dir.h" #include "inode.h" #include "key.h" #include "super.h" /** * read_sibling_id_xfield - Parse a sibling id xfield and check its consistency * @xval: pointer to the xfield value * @len: remaining length of the dentry value * @sibling_id: on return, the sibling id * * Returns the length of the xfield value. */ static int read_sibling_id_xfield(char *xval, int len, u64 *sibling_id) { __le64 *id_raw; if (len < 8) report("Sibling link xfield", "doesn't fit in dentry record."); id_raw = (__le64 *)xval; *sibling_id = le64_to_cpu(*id_raw); return sizeof(*id_raw); } /** * parse_dentry_xfields - Parse and check a dentry extended fields * @xblob: pointer to the beginning of the xfields in the dentry value * @len: length of the xfields * @sibling_id: on return, the sibling id (0 if none) * * Internal consistency of @key must be checked before calling this function. */ static void parse_dentry_xfields(struct apfs_xf_blob *xblob, int len, u64 *sibling_id) { struct apfs_x_field *xfield; char *xval; int xlen; *sibling_id = 0; if (len == 0) /* No extended fields */ return; len -= sizeof(*xblob); if (len < 0) report("Dentry record", "no room for extended fields."); if (le16_to_cpu(xblob->xf_num_exts) != 1) report("Dentry record", "bad xfield count."); xfield = (struct apfs_x_field *)xblob->xf_data; xval = (char *)xfield + sizeof(*xfield); len -= sizeof(*xfield); if (len < 0) report("Dentry record", "xfield cannot fit."); /* The official reference seems to be wrong here */ if (le16_to_cpu(xblob->xf_used_data) != len) report("Dentry record", "value size incompatible with xfields."); check_xfield_flags(xfield->x_flags); if (xfield->x_type != APFS_DREC_EXT_TYPE_SIBLING_ID) report("Dentry xfield", "invalid type."); xlen = read_sibling_id_xfield(xval, len, sibling_id); if (xlen != le16_to_cpu(xfield->x_size)) report("Dentry xfield", "wrong size"); if (xlen != len) report("Dentry record", "wrong used space for xfields."); } /** * check_orphan_name - Check the dentry name for an orphan inode * @name: the filename (as reported by the dentry, not the inode) * @ino: the inode number */ static void check_orphan_name(const char *name, u64 ino) { const char *format = "0x%llx-dead"; char *buf; int buflen; buflen = snprintf(NULL, 0, format, (unsigned long long)ino); if (buflen < 0) system_error(); buf = calloc(1, ++buflen); if (!buf) system_error(); buflen = snprintf(buf, buflen, format, (unsigned long long)ino); if (buflen < 0) system_error(); if (strcmp(name, buf) != 0) report("Orphan inode", "wrong name."); free(buf); } /** * parse_dentry_record - Parse a dentry record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_dentry_record(void *key, struct apfs_drec_val *val, int len) { u64 ino, parent_ino; struct inode *inode, *parent; char *name; int namelen; u16 filetype, dtype, flags; u64 sibling_id; struct sibling *sibling; if (apfs_is_normalization_insensitive()) { struct apfs_drec_hashed_key *hkey = key; name = (char *)hkey->name; namelen = le32_to_cpu(hkey->name_len_and_hash) & 0x3FFU; } else { struct apfs_drec_key *ukey = key; name = (char *)ukey->name; namelen = le16_to_cpu(ukey->name_len); } if (len < sizeof(*val)) report("Dentry record", "value is too small."); ino = le64_to_cpu(val->file_id); inode = get_inode(ino); parent_ino = cat_cnid(key); /* The purgeable dentry doesn't count for nlink */ if (parent_ino != APFS_PURGEABLE_DIR_INO_NUM) inode->i_link_count++; if (ino == APFS_ROOT_DIR_INO_NUM && strcmp(name, "root")) report("Root directory", "wrong name."); if (ino == APFS_PRIV_DIR_INO_NUM && strcmp(name, "private-dir")) report("Private directory", "wrong name."); /* Not required by the specification, but follow what apple does */ if (parent_ino == APFS_PRIV_DIR_INO_NUM) check_orphan_name(name, ino); if (parent_ino == APFS_PURGEABLE_DIR_INO_NUM) { if (inode->i_purg_name) report("Inode", "has two purgeable dentry records."); inode->i_purg_name = strdup(name); if (!inode->i_purg_name) system_error(); } check_inode_ids(ino, parent_ino); if (parent_ino != APFS_ROOT_DIR_PARENT && parent_ino != APFS_PURGEABLE_DIR_INO_NUM) { parent = get_inode(parent_ino); if (!parent->i_seen) /* The b-tree keys are in order */ report("Dentry record", "parent inode missing"); if ((parent->i_mode & S_IFMT) != S_IFDIR) report("Dentry record", "parent inode not directory."); parent->i_child_count++; } /* The purgeable dentry is never reported as inode name and parent id */ if (parent_ino != APFS_PURGEABLE_DIR_INO_NUM && !inode->i_first_name) { /* No dentry for this inode has been seen before */ inode->i_first_name = malloc(namelen); if (!inode->i_first_name) system_error(); strcpy(inode->i_first_name, name); inode->i_first_parent = parent_ino; } flags = le16_to_cpu(val->flags); dtype = flags & APFS_DREC_TYPE_MASK; if (flags & ~(APFS_DREC_TYPE_MASK | APFS_DREC_PURGEABLE)) report("Dentry record", "reserved flags in use."); if ((bool)(flags & APFS_DREC_PURGEABLE) != (parent_ino == APFS_PURGEABLE_DIR_INO_NUM)) report("Dentry record", "the purgeable dir is for purgeable dentries."); if (flags & APFS_DREC_PURGEABLE) { /* No idea. The inode and other hardlinks have normal modes */ if (flags & APFS_DREC_PURGEABLE_2 && dtype != 5) report("Dentry record", "wrong type for purgeable dentry."); if (flags & APFS_DREC_PURGEABLE_8 && dtype != 4) report("Dentry record", "wrong type for purgeable dentry."); } else { /* The mode may have already been set by the inode or another dentry */ filetype = inode->i_mode >> 12; if (filetype && filetype != dtype) report("Dentry record", "file mode doesn't match dentry type."); if (dtype == 0) /* Don't save a 0, that means the mode is not set */ report("Dentry record", "invalid dentry type."); inode->i_mode |= dtype << 12; } /* * Orphans aren't counted for the file totals, but I can't tell them * apart inside parse_inode_record(), so do it here instead. */ if (parent_ino == APFS_PRIV_DIR_INO_NUM) { switch (dtype << 12) { case S_IFREG: vsb->v_file_count--; break; case S_IFDIR: if (inode->i_ino >= APFS_MIN_USER_INO_NUM) vsb->v_dir_count--; break; case S_IFLNK: vsb->v_symlink_count--; break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: vsb->v_special_count--; break; default: report("Dentry record", "invalid file mode."); } } parse_dentry_xfields((struct apfs_xf_blob *)val->xfields, len - sizeof(*val), &sibling_id); if (!sibling_id) /* No sibling record for this dentry */ return; sibling = get_sibling(sibling_id, inode); set_or_check_sibling(parent_ino, namelen, (u8 *)name, sibling); } /* TODO: update the checks for cnid reuse, here and elsewhere */ static void free_dirstat(struct htable_entry *entry) { struct dirstat *stats = (struct dirstat *)entry; /* The inodes must be parsed before the dirstats */ assert(!vsb->v_inode_table); if (!stats->ds_origin_seen) report("Directory stats", "have no inode."); if (stats->ds_num_children != stats->ds_child_count) report("Directory stats", "wrong child count."); if (stats->ds_total_size != stats->ds_child_size) report("Directory stats", "wrong total size."); if (stats->ds_chained_key) report_unknown("Chained key in directory stats"); free(entry); } void free_dirstat_table(struct htable_entry **table) { free_htable(table, free_dirstat); } struct dirstat *get_dirstat(u64 oid) { struct htable_entry *entry; entry = get_htable_entry(oid, sizeof(struct dirstat), vsb->v_dirstat_table); return (struct dirstat *)entry; } void parse_dir_stats_record(void *key, struct apfs_dir_stats_val *val, int len) { struct dirstat *stats = NULL; if (len != sizeof(*val)) report("Dir stats record", "wrong size of value."); stats = get_dirstat(cat_cnid(key)); stats->ds_seen = true; stats->ds_num_children = le64_to_cpu(val->num_children); stats->ds_total_size = le64_to_cpu(val->total_size); stats->ds_chained_key = le64_to_cpu(val->chained_key); } apfsprogs-0.2.0/apfsck/dir.h000066400000000000000000000022171471277137200157300ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _DIR_H #define _DIR_H #include #include "htable.h" /* * Directory stats in memory */ struct dirstat { struct htable_entry ds_htable; /* Hash table entry header */ bool ds_seen; /* Has the record been seen? */ bool ds_origin_seen; /* Has the origin inode been seen? */ /* Directory stats read from the dir stats record */ u64 ds_num_children;/* Number of files and folders inside */ u64 ds_total_size; /* Total size of all files inside */ u64 ds_chained_key; /* TODO: figure this out */ /* Directory stats measured by the fsck */ u64 ds_child_count; /* Number of files and folders inside */ u64 ds_child_size; /* Total size of all files inside */ u64 ds_origin; /* Id of owning inode */ }; #define ds_oid ds_htable.h_id /* Object id */ extern void parse_dentry_record(void *key, struct apfs_drec_val *val, int len); extern void parse_dir_stats_record(void *key, struct apfs_dir_stats_val *val, int len); extern struct dirstat *get_dirstat(u64 oid); extern void free_dirstat_table(struct htable_entry **table); #endif /* _DIR_H */ apfsprogs-0.2.0/apfsck/extents.c000066400000000000000000000601031471277137200166350ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "compress.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "key.h" #include "super.h" /** * check_extent - Perform a final check on an extent structure * @extent: the extent to check */ static void check_extent(struct htable_entry *entry) { struct extent *extent = (struct extent *)entry; if (extent->e_refcnt != extent->e_references) report("Physical extent record", "bad reference count."); /* Reset the real reference count, for the next snapshot */ extent->e_references = 0; } /** * free_extent - Free an extent structure after performing a final check * @entry: the entry to free */ static void free_extent(struct htable_entry *entry) { check_extent(entry); free(entry); } /** * free_extent_table - Free the extent hash table and all its entries * @table: table to free */ void free_extent_table(struct htable_entry **table) { if (vsb->v_in_snapshot) report("Physical extent record", "BUG!"); free_htable(table, free_extent); } /** * check_and_reset_extent_table - Checks the extents and resets their references * @table: table to check */ void check_and_reset_extent_table(struct htable_entry **table) { if (!vsb->v_in_snapshot) report("Physical extent record", "BUG!"); apply_on_htable(table, check_extent); } /** * get_extent - Find or create an extent structure in the extent hash table * @bno: first physical block of the extent * * Returns the extent structure, after creating it if necessary. */ static struct extent *get_extent(u64 bno) { struct htable_entry *entry = NULL; entry = get_htable_entry(bno, sizeof(struct extent), vsb->v_extent_table); return (struct extent *)entry; } /** * extent_exists - Check if an extent exists in the extent hash table * @bno: first physical block of the extent */ static bool extent_exists(u64 bno) { return htable_entry_exists(bno, vsb->v_extent_table); } /** * get_subextent - Break up an extent in the hash table, and return a part * @whole_bno: base of the whole extent (only a lower bound, really) * @part_bno: base of the extent to extract * @part_bkcnt: maximum block count for the extent to extract */ static struct extent *get_subextent(u64 whole_bno, u64 part_bno, u64 part_blkcnt) { struct extent *whole_extent = NULL, *part_extent = NULL, *tail_extent = NULL, *head_extent = NULL; u64 whole_blkcnt; u32 whole_refcnt; if (whole_bno > part_bno) report("Physical extent record", "BUG!"); /* * The whole extent base was probably found from the physical extent * b-trees, but our in-memory extent may have been split by previous * reference updates. */ while (whole_bno <= part_bno) { if (!extent_exists(whole_bno)) { ++whole_bno; continue; } whole_extent = get_extent(whole_bno); whole_blkcnt = whole_extent->e_blocks; if (whole_blkcnt == 0) report("Physical extent record", "has no blocks?"); if (whole_bno + whole_blkcnt < whole_bno) report("Physical extent record", "overflow?"); if (whole_bno + whole_blkcnt > part_bno) break; whole_bno += whole_blkcnt; } if (whole_bno > part_bno) report("Physical extent record", "update for nonexisting extent."); whole_refcnt = whole_extent->e_refcnt; /* * We make the subextent as big as possible, but the caller may have * to continue breaking up the next extent. */ part_blkcnt = MIN(part_blkcnt, whole_blkcnt - (part_bno - whole_bno)); if (whole_bno == part_bno) { if (whole_extent->e_blocks == part_blkcnt) return whole_extent; part_extent = whole_extent; whole_extent = NULL; part_extent->e_blocks = part_blkcnt; tail_extent = get_extent(whole_bno + part_blkcnt); if (tail_extent->e_old_entry) report("Physical extent record", "weird overlapping extents."); tail_extent->e_old_entry = true; tail_extent->e_blocks = whole_blkcnt - part_blkcnt; tail_extent->e_refcnt = whole_refcnt; return part_extent; } head_extent = whole_extent; whole_extent = NULL; head_extent->e_blocks = part_bno - whole_bno; part_extent = get_extent(part_bno); if (part_extent->e_old_entry) report("Physical extent record", "weird overlapping extents."); part_extent->e_old_entry = true; part_extent->e_blocks = part_blkcnt; part_extent->e_refcnt = whole_refcnt; if (head_extent->e_blocks + part_extent->e_blocks < whole_blkcnt) { tail_extent = get_extent(part_bno + part_blkcnt); if (tail_extent->e_old_entry) report("Physical extent record", "weird overlapping extents."); tail_extent->e_old_entry = true; tail_extent->e_blocks = whole_blkcnt - (head_extent->e_blocks + part_extent->e_blocks); tail_extent->e_refcnt = whole_refcnt; } return part_extent; } /** * change_extent_refcnts - Change the refcnts for all known extents in a range * @bno: base of the range * @bkcnt: block count for the range * @refdiff: refcnt change to apply */ static void change_extent_refcnts(u64 bno, u64 blkcnt, int32_t refdiff) { while (blkcnt > 0) { struct extref_record rec = {0}; struct extent *extent = NULL; extentref_update_lookup(bno, &rec); extent = get_subextent(rec.phys_addr, bno, blkcnt); extent->e_refcnt += refdiff; blkcnt -= extent->e_blocks; bno += extent->e_blocks; } } /** * check_dstream_stats - Verify the stats gathered by the fsck vs the metadata * @dstream: dstream structure to check */ static void check_dstream_stats(struct dstream *dstream) { if (!dstream->d_references) report("Data stream", "has no references."); if (dstream->d_id < APFS_MIN_USER_INO_NUM) report("Data stream", "invalid or reserved id."); if (dstream->d_id >= vsb->v_next_obj_id) report("Data stream", "free id in use."); if (dstream->d_obj_type == APFS_TYPE_XATTR) { if (dstream->d_seen || dstream->d_references != 1) report("Data stream", "xattrs can't be cloned."); if (dstream->d_sparse_bytes != 0) { /* * I'm not actually sure about this, but let's leave a * check and see if it happens anywhere. */ report("Data stream", "xattrs can't have holes."); } } else { if (!dstream->d_seen) report("Data stream", "missing reference count."); if (dstream->d_refcnt != dstream->d_references) report("Data stream", "bad reference count."); } /* Orphan inodes can have missing extents */ if (dstream->d_orphan) { if (dstream->d_size > dstream->d_alloced_size) report("Orphan dstream", "reported sizes make no sense."); if (dstream->d_bytes != 0 && dstream->d_logic_start + dstream->d_bytes != dstream->d_alloced_size) report_weird("Orphan dstream"); } else { if (dstream->d_logic_start != 0) report("Data stream", "missing leading extents."); if (dstream->d_bytes < dstream->d_size) report("Data stream", "some extents are missing."); if (dstream->d_bytes != dstream->d_alloced_size) report("Data stream", "wrong allocated space."); } } static void verify_compressed_dstream_info_hash(struct listed_hash *info, struct dstream *dstream, struct compress *compress) { SHA256_CTX ctx = {0}; u8 true_hash[APFS_HASH_CCSHA256_SIZE] = {0}; u8 *block = NULL; loff_t off; ssize_t res; u16 i; block = malloc(sb->s_blocksize); if (!block) system_error(); sha256_init(&ctx); for (i = 0; i < info->blkcnt; ++i) { off = info->addr + i * sb->s_blocksize; res = apfs_compress_read(compress, (char *)block, sb->s_blocksize, &off); memset(block + res, 0, sb->s_blocksize - res); sha256_update(&ctx, block, sb->s_blocksize); } sha256_final(&ctx, true_hash); free(block); block = NULL; if (memcmp(info->hash, true_hash, APFS_HASH_CCSHA256_SIZE) != 0) report("File info record", "incorrect hash of file data."); } /** * verify_dstream_info_hash - Verify that a a file info hash is correct * @info: information about the hash to check * @dstream: dstream for the file to check */ static void verify_dstream_info_hash(struct listed_hash *info, struct dstream *dstream) { SHA256_CTX ctx = {0}; u8 true_hash[APFS_HASH_CCSHA256_SIZE] = {0}; u64 bno; u8 *block = NULL; u16 i; sha256_init(&ctx); for (i = 0; i < info->blkcnt; ++i) { if (fext_tree_lookup(dstream->d_id, info->addr + i * sb->s_blocksize, &bno)) report("Fext tree", "query failed."); if (bno == 0) /* A hole */ block = mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); else block = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if (block == MAP_FAILED) system_error(); sha256_update(&ctx, block, sb->s_blocksize); munmap(block, sb->s_blocksize); block = NULL; } sha256_final(&ctx, true_hash); if (memcmp(info->hash, true_hash, APFS_HASH_CCSHA256_SIZE) != 0) report("File info record", "incorrect hash of file data."); } void verify_dstream_hashes(struct dstream *dstream, struct compress *compress) { struct listed_hash *hash = NULL; if (!apfs_volume_is_sealed()) return; if (!dstream) report(NULL, "Bug!"); hash = dstream->d_hashes; while (hash) { if (compress) verify_compressed_dstream_info_hash(hash, dstream, compress); else verify_dstream_info_hash(hash, dstream); dstream->d_hashes = hash->prev; free(hash); hash = dstream->d_hashes; } } static void increase_extent_references(u64 bno, u64 blkcnt, u64 owner, u8 owner_type) { while (blkcnt > 0) { u64 base_bno = bno; struct extent *extent = NULL; int limit = 5000; /* * Sometimes there is more than one consecutive logical extents * sharing a single physical extent, and sometimes they don't * even belong to the same dstream. No idea why. */ while (!extent_exists(base_bno)) { if (--limit == 0) report("Logical extent record", "doesn't seem covered by any physical extent."); --base_bno; } extent = get_subextent(base_bno, bno, blkcnt); if (extent->e_references) { if (extent->e_obj_type != owner_type) report("Physical extent record", "owners have inconsistent types."); /* Only count the extent once for each owner */ if (extent->e_latest_owner != owner) extent->e_references++; } else { extent->e_references++; } extent->e_obj_type = owner_type; extent->e_latest_owner = owner; /* An extent may have an unused tail */ blkcnt -= extent->e_blocks; bno += extent->e_blocks; } } /** * free_dstream - Free a dstream structure after performing some final checks * @entry: the entry to free */ static void free_dstream(struct htable_entry *entry) { struct dstream *dstream = (struct dstream *)entry; struct listed_cnid *cnid; struct listed_extent *curr_extent = dstream->d_extents; /* The dstreams must be freed before the cnids */ assert(vsb->v_cnid_table); /* To check for reuse, put all filesystem object ids in a list */ cnid = get_listed_cnid(dstream->d_id); cnid_set_state_flag(cnid, CNID_IN_DSTREAM); /* * Not a real dstream, just a hack for working with inline compressed * files inside sealed volumes. Don't check anything. */ if(dstream->d_inline) return free(entry); /* Increase the refcount of each physical extent used by the dstream */ while (curr_extent) { struct listed_extent *next_extent; increase_extent_references(curr_extent->paddr, curr_extent->blkcnt, dstream->d_owner, dstream->d_obj_type); next_extent = curr_extent->next; free(curr_extent); curr_extent = next_extent; } /* TODO: support resource forks for compressed files too */ if (!dstream->d_xattr) verify_dstream_hashes(dstream, NULL); check_dstream_stats(dstream); free(entry); } /** * free_dstream_table - Free the dstream hash table and all its entries * @table: table to free */ void free_dstream_table(struct htable_entry **table) { free_htable(table, free_dstream); } /** * get_dstream - Find or create a dstream structure in the dstream hash table * @id: id of the dstream * * Returns the dstream structure, after creating it if necessary. */ struct dstream *get_dstream(u64 id) { struct htable_entry *entry; entry = get_htable_entry(id, sizeof(struct dstream), vsb->v_dstream_table); return (struct dstream *)entry; } /** * attach_prange_to_dstream - Attach a physical range to a dstream structure * @paddr: physical address of the range * @blk_count: number of blocks in the range * @dstream: dstream structure */ static void attach_extent_to_dstream(u64 paddr, u64 blk_count, struct dstream *dstream) { struct listed_extent **ext_p = NULL; struct listed_extent *ext = NULL; struct listed_extent *new = NULL; u64 paddr_end; paddr_end = paddr + blk_count; if (paddr_end < paddr) /* Overflow */ report("Extent record", "physical address is too big."); ext_p = &dstream->d_extents; ext = *ext_p; /* Entries are ordered by their physical address */ while (ext) { /* Count physical extents only once for each owner */ if (paddr == ext->paddr) return; if (paddr < ext->paddr) break; ext_p = &ext->next; ext = *ext_p; } new = malloc(sizeof(*new)); if (!new) system_error(); new->paddr = paddr; new->blkcnt = blk_count; new->next = ext; *ext_p = new; } /** * parse_extent_record - Parse an extent record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_extent_record(struct apfs_file_extent_key *key, struct apfs_file_extent_val *val, int len) { struct dstream *dstream; u64 length, flags, logical_addr; u64 crypid; bool prealloc_flagged; if (apfs_volume_is_sealed()) report("Extent record", "shouldn't exist in a sealed volume."); if (len != sizeof(*val)) report("Extent record", "wrong size of value."); crypid = le64_to_cpu(val->crypto_id); if (crypid && crypid != APFS_CRYPTO_SW_ID) ++get_crypto_state(crypid)->c_references; length = le64_to_cpu(val->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; if (!length) report("Extent record", "length is zero."); if (length & (sb->s_blocksize - 1)) report("Extent record", "length isn't multiple of block size."); logical_addr = le64_to_cpu(key->logical_addr); flags = (le64_to_cpu(val->len_and_flags) & APFS_FILE_EXTENT_FLAG_MASK) >> APFS_FILE_EXTENT_FLAG_SHIFT; if ((flags & APFS_VALID_FILE_EXTENT_FLAGS) != flags) report("Extent record", "invalid flag in use."); if (flags & APFS_FILE_EXTENT_CRYPTO_FLAG) report_unknown("Encrypted extent"); prealloc_flagged = flags & APFS_FILE_EXTENT_PREALLOCATED; dstream = get_dstream(cat_cnid(&key->hdr)); if (dstream->d_bytes == 0 && logical_addr != 0) { /* An orphan may have already lost its leading extents */ dstream->d_logic_start = logical_addr; } if (dstream->d_logic_start + dstream->d_bytes != logical_addr) report("Data stream", "extents are not consecutive."); dstream->d_bytes += length; /* Mostly conjecture, but if I'm wrong this will trip on something */ if (apfs_volume_has_extent_prealloc_flag()) { if (prealloc_flagged != (bool)(logical_addr >= dstream->d_size)) report("Extent record", "bad preallocation flag."); } else { if (prealloc_flagged) report("Extent record", "uses prealloc flag without that feature."); } if (!le64_to_cpu(val->phys_block_num)) { /* This is a hole */ dstream->d_sparse_bytes += length; return; } attach_extent_to_dstream(le64_to_cpu(val->phys_block_num), length >> sb->s_blocksize_bits, dstream); } /** * parse_dstream_id_record - Parse a dstream id record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_dstream_id_record(struct apfs_dstream_id_key *key, struct apfs_dstream_id_val *val, int len) { struct dstream *dstream; if (len != sizeof(*val)) report("Dstream id record", "wrong size of value."); dstream = get_dstream(cat_cnid(&key->hdr)); dstream->d_seen = true; dstream->d_refcnt = le32_to_cpu(val->refcnt); } /** * parse_phys_ext_record - Parse and check a physical extent record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Returns the physical address of the last block in the extent. * * Internal consistency of @key must be checked before calling this function. */ u64 parse_phys_ext_record(struct apfs_phys_ext_key *key, struct apfs_phys_ext_val *val, int len) { struct extent *extent = NULL; u8 kind; u32 refcnt; u64 length, owner, paddr; if (len != sizeof(*val)) report("Physical extent record", "wrong size of value."); kind = le64_to_cpu(val->len_and_kind) >> APFS_PEXT_KIND_SHIFT; if (kind != APFS_KIND_NEW && kind != APFS_KIND_UPDATE) report("Physical extent record", "invalid kind"); length = le64_to_cpu(val->len_and_kind) & APFS_PEXT_LEN_MASK; if (!length) report("Physical extent record", "has no blocks."); /* * If the owner of a physical extent got removed, I would expect this * field to be meaningless. At least check that the number is in range. */ owner = le64_to_cpu(val->owning_obj_id); if (owner == APFS_OWNING_OBJ_ID_INVALID) { if (kind != APFS_KIND_UPDATE) report("Physical extent record", "invalid owner id for NEW."); } else { if (kind != APFS_KIND_NEW) report("Physical extent record", "valid owner id for UPDATE."); if (owner < APFS_MIN_USER_INO_NUM) report("Physical extent record", "reserved id."); if (owner >= vsb->v_next_obj_id) report("Physical extent record", "free id in use."); } refcnt = le32_to_cpu(val->refcnt); if (!refcnt) report("Physical extent record", "should have been deleted."); paddr = cat_cnid(&key->hdr); if (kind == APFS_KIND_NEW) { extent = get_extent(paddr); extent->e_blocks = length; extent->e_refcnt = refcnt; if (extent->e_old_entry) report("Physical extent record", "is NEW but has already used blocks."); extent->e_old_entry = true; vsb->v_block_count += extent->e_blocks; container_bmap_mark_as_used(extent->e_bno, extent->e_blocks); } else { change_extent_refcnts(paddr, length, refcnt); } return paddr + length - 1; } /** * free_crypto_state - Free a crypto state after performing some final checks * @entry: the entry to free */ static void free_crypto_state(struct htable_entry *entry) { struct crypto_state *crypto = (struct crypto_state *)entry; /* * It seems that the overprovisioning file may have no wrapped key, * even if it does have a state record. */ if (crypto->c_keylen == 0 && !crypto->c_overprov) report_unknown("Encrypted metadata"); if (crypto->c_refcnt != crypto->c_references) report("Crypto state record", "bad reference count."); free(crypto); } /** * free_crypto_table - Free the crypto state hash table and all its entries * @table: table to free */ void free_crypto_table(struct htable_entry **table) { free_htable(table, free_crypto_state); } /** * get_crypto_state - Find or create a crypto state struct in their hash table * @id: id of the crypto state * * Returns the crypto state structure, after creating it if necessary. */ struct crypto_state *get_crypto_state(u64 id) { struct htable_entry *entry; entry = get_htable_entry(id, sizeof(struct crypto_state), vsb->v_crypto_table); return (struct crypto_state *)entry; } /** * parse_crypto_state_record - Parse and check a crypto state record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_crypto_state_record(struct apfs_crypto_state_key *key, struct apfs_crypto_state_val *val, int len) { struct apfs_wrapped_crypto_state *wrapped = NULL; struct crypto_state *crypto; u16 key_len; if (!vsb->v_encrypted) report("Unencrypted volume", "has crypto state records."); if (len < sizeof(*val)) report("Crypto state record", "value size too small."); wrapped = &val->state; key_len = le16_to_cpu(wrapped->key_len); if (key_len > APFS_CP_MAX_WRAPPEDKEYSIZE) report("Crypto state record", "wrapped key is too long."); if (len != sizeof(*val) + le16_to_cpu(wrapped->key_len)) report("Crypto state record", "wrong size of value."); if (le16_to_cpu(wrapped->major_version) != APFS_WMCS_MAJOR_VERSION) report("Crypto state record", "wrong major version."); if (le16_to_cpu(wrapped->minor_version) != APFS_WMCS_MINOR_VERSION) report("Crypto state record", "wrong minor version."); if (wrapped->cpflags) report("Crypto state record", "unknown flag."); /* TODO: deal with the protection class */ if (!wrapped->key_revision) report("Crypto state record", "key revision is not set."); /* * I don't know how unofficial implementations are supposed to handle * this field, but I'm guessing it shouldn't be zero. */ if (!wrapped->key_os_version) report("Crypto state record", "os version is not set."); crypto = get_crypto_state(cat_cnid(&key->hdr)); switch (crypto->c_id) { case 0: report("Crypto state record", "null id."); case APFS_CRYPTO_SW_ID: report("Crypto state record", "id for software encryption."); case APFS_CRYPTO_RESERVED_5: report("Crypto state record", "reserved crypto id."); case APFS_UNASSIGNED_CRYPTO_ID: report("Crypto state record", "unassigned crypto id."); } crypto->c_refcnt = le32_to_cpu(val->refcnt); if (!crypto->c_refcnt) report("Crypto state record", "has no references."); crypto->c_keylen = key_len; } static void attach_hash_to_dstream(u64 addr, u64 blkcnt, u8 *hash, struct dstream *dstream) { struct listed_hash *new = NULL; new = calloc(1, sizeof(*new)); if (!new) system_error(); new->addr = addr; new->blkcnt = blkcnt; memcpy(new->hash, hash, APFS_HASH_CCSHA256_SIZE); new->prev = dstream->d_hashes; dstream->d_hashes = new; } /** * parse_file_info_record - Parse an info record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_file_info_record(struct apfs_file_info_key *key, struct apfs_file_info_val *val, int len) { struct apfs_file_data_hash_val *dhash = NULL; struct dstream *dstream = NULL; u64 paddr; u16 blkcnt; if (!apfs_volume_is_sealed()) report("File info record", "volume is unsealed."); dhash = &val->dhash; if (len < sizeof(*dhash)) report("File info record", "value is too small."); if (dhash->hash_size != APFS_HASH_CCSHA256_SIZE) report("File info record", "unusual hash length."); if (len != sizeof(*dhash) + dhash->hash_size) report("File info record", "wrong size of value."); blkcnt = le16_to_cpu(dhash->hashed_len); if (!blkcnt) report("File info record", "length is zero."); paddr = le64_to_cpu(key->info_and_lba) & APFS_FILE_INFO_LBA_MASK; dstream = get_dstream(cat_cnid(&key->hdr)); attach_hash_to_dstream(paddr, blkcnt, dhash->hash, dstream); } /** * parse_fext_record - Parse a fext record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_fext_record(struct apfs_fext_tree_key *key, struct apfs_fext_tree_val *val, int len) { struct dstream *dstream = NULL; u64 length, flags; /* * Keys and values must be aligned to eight bytes. TODO: add this * check to aligned trees of all types. */ if ((u64)key & 7 || (u64)val & 7) report("Omap record", "bad alignment for key or value."); if (len != sizeof(*val)) report("Fext record", "wrong size of value."); length = le64_to_cpu(val->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; if (!length) report("Fext record", "length is zero."); if (length & (sb->s_blocksize - 1)) report("Fext record", "length isn't multiple of block size."); flags = le64_to_cpu(val->len_and_flags) & APFS_FILE_EXTENT_FLAG_MASK; if (flags) report("Fext record", "no flags should be set."); dstream = get_dstream(le64_to_cpu(key->private_id)); if (dstream->d_bytes != le64_to_cpu(key->logical_addr)) report("Data stream", "fexts are not consecutive."); dstream->d_bytes += length; if (!le64_to_cpu(val->phys_block_num)) { /* This is a hole */ dstream->d_sparse_bytes += length; return; } attach_extent_to_dstream(le64_to_cpu(val->phys_block_num), length >> sb->s_blocksize_bits, dstream); } apfsprogs-0.2.0/apfsck/extents.h000066400000000000000000000105371471277137200166500ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _EXTENTS_H #define _EXTENTS_H #include #include "compress.h" #include "htable.h" struct apfs_file_extent_key; struct apfs_dstream_id_key; struct apfs_phys_ext_key; /* * Physical extent record in memory, to be filled by extentref_lookup() */ struct extref_record { u64 phys_addr; /* First block number */ u64 blocks; /* Block count */ u64 owner; /* Owning object id */ u32 refcnt; /* Reference count */ bool update; /* An update record? */ }; /* * Physical extent data in memory */ struct extent { struct htable_entry e_htable; /* Hash table entry header */ bool e_old_entry; /* Is it an existing hash table entry? */ u8 e_obj_type; /* Type of the owner objects */ u32 e_refcnt; /* Reference count reported on-disk */ u32 e_references; /* Actual reference count to extent */ u64 e_blocks; /* Block count */ u64 e_latest_owner; /* Last owner counted on e_references */ }; #define e_bno e_htable.h_id /* First physical block in the extent */ /* * Structure used to register each physical extent for a dstream, so that the * references can later be counted. The same extent structure might be shared * by several dstreams. */ struct listed_extent { u64 paddr; /* Physical address for the extent */ u64 blkcnt; /* Block count for the extent */ struct listed_extent *next; /* Next entry in linked list */ }; /* * Structure used to register each hash for a dstream in a sealed volume, so * that they can later be checked. */ struct listed_hash { u64 addr; /* Dstream offset of the hashed area */ u64 blkcnt; /* Block count of the hashed area */ u8 hash[APFS_HASH_CCSHA256_SIZE]; struct listed_hash *prev; /* Previous entry in linked list */ }; /* * Dstream data in memory */ struct dstream { struct htable_entry d_htable; /* Hash table entry header */ /* Linked list of physical extents for dstream */ struct listed_extent *d_extents; /* Linked list of hashes for the dstream */ struct listed_hash *d_hashes; u8 d_obj_type; /* Type of the owner objects */ u64 d_owner; /* Owner id for the extentref tree */ bool d_seen; /* Has the dstream record been seen? */ bool d_orphan; /* Is this an orphan file? */ bool d_xattr; /* Is this a xattr dstream? */ bool d_inline; /* Is this actually an inline xattr? */ /* Dstream stats read from the dstream structures */ u64 d_size; /* Dstream size */ u64 d_alloced_size; /* Dstream size, including unused */ u32 d_refcnt; /* Reference count */ /* Dstream stats measured by the fsck */ u64 d_logic_start; /* Logical address of first extent */ u64 d_bytes; /* Size of the extents read so far */ u64 d_sparse_bytes; /* Size of the holes read so far */ u32 d_references; /* Number of references to dstream */ }; #define d_id d_htable.h_id /* Dstream id */ /* * Crypto state data in memory */ struct crypto_state { struct htable_entry c_htable; /* Hash table entry header */ /* Crypto state dtats read from the record */ u32 c_refcnt; u16 c_keylen; /* Crypto state stats measured by the fsck */ u32 c_references; /* Number of refs to crypto state */ bool c_overprov; /* Used by an overprovisioning file */ }; #define c_id c_htable.h_id /* Crypto id */ extern void free_dstream_table(struct htable_entry **table); extern void free_extent_table(struct htable_entry **table); extern struct dstream *get_dstream(u64 ino); extern void parse_extent_record(struct apfs_file_extent_key *key, struct apfs_file_extent_val *val, int len); extern void parse_dstream_id_record(struct apfs_dstream_id_key *key, struct apfs_dstream_id_val *val, int len); extern u64 parse_phys_ext_record(struct apfs_phys_ext_key *key, struct apfs_phys_ext_val *val, int len); extern void free_crypto_table(struct htable_entry **table); extern struct crypto_state *get_crypto_state(u64 id); extern void parse_crypto_state_record(struct apfs_crypto_state_key *key, struct apfs_crypto_state_val *val, int len); extern void parse_file_info_record(struct apfs_file_info_key *key, struct apfs_file_info_val *val, int len); extern void parse_fext_record(struct apfs_fext_tree_key *key, struct apfs_fext_tree_val *val, int len); extern void verify_dstream_hashes(struct dstream *dstream, struct compress *compress); extern void check_and_reset_extent_table(struct htable_entry **table); #endif /* _EXTENTS_H */ apfsprogs-0.2.0/apfsck/htable.c000066400000000000000000000071531471277137200164100ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include "apfsck.h" #include "htable.h" #include "super.h" /** * alloc_htable - Allocates and returns an empty hash table */ struct htable_entry **alloc_htable(void) { struct htable_entry **table; table = calloc(HTABLE_BUCKETS, sizeof(*table)); if (!table) system_error(); return table; } /** * free_htable - Free a hash table and all its entries * @table: the catalog table to free * @free_entry: function that checks and frees an entry */ void free_htable(struct htable_entry **table, void (*free_entry)(struct htable_entry *)) { struct htable_entry *current; struct htable_entry *next; int i; for (i = 0; i < HTABLE_BUCKETS; ++i) { current = table[i]; while (current) { next = current->h_next; free_entry(current); current = next; } } free(table); } /** * apply_on_htable - Apply a function to all entries in a hash table * @table: the hash table * @fn: function to apply */ void apply_on_htable(struct htable_entry **table, void (*fn)(struct htable_entry *)) { struct htable_entry *current; struct htable_entry *next; int i; for (i = 0; i < HTABLE_BUCKETS; ++i) { current = table[i]; while (current) { next = current->h_next; fn(current); current = next; } } } /** * get_htable_entry - Find or create an entry in a hash table * @id: id of the entry * @size: size of the entry * @table: the hash table * * Returns the entry, after creating it if necessary. */ struct htable_entry *get_htable_entry(u64 id, int size, struct htable_entry **table) { int index = id % HTABLE_BUCKETS; /* Trivial hash function */ struct htable_entry **entry_p = table + index; struct htable_entry *entry = *entry_p; struct htable_entry *new; /* In each linked list, entries are ordered by id */ while (entry) { if (id == entry->h_id) return entry; if (id < entry->h_id) break; entry_p = &entry->h_next; entry = *entry_p; } new = calloc(1, size); if (!new) system_error(); new->h_id = id; new->h_next = entry; *entry_p = new; return new; } /** * htable_entry_exists - Check if an entry exists in a hash table * @id: id of the entry * @table: the hash table */ bool htable_entry_exists(u64 id, struct htable_entry **table) { int index = id % HTABLE_BUCKETS; /* Trivial hash function */ struct htable_entry *entry = *(table + index); /* In each linked list, entries are ordered by id */ while (entry) { if (id == entry->h_id) return true; if (id < entry->h_id) return false; entry = entry->h_next; } return false; } static void free_cnid(struct htable_entry *entry) { struct listed_cnid *cnid = (struct listed_cnid *)entry; if (cnid->c_state & CNID_IN_SIBLING_LINK) { if (cnid->c_state & ~CNID_IN_SIBLING_LINK) report("Catalog", "sibling link oid reused elsewhere."); } free(entry); } /** * free_cnid_table - Free the cnid hash table and all its entries * @table: table to free * * Also performs some consistency checks that can only be done after the whole * catalog has been parsed. */ void free_cnid_table(struct htable_entry **table) { /* No checks needed here, just call free() on each entry */ free_htable(table, free_cnid); } /** * get_listed_cnid - Find or create a cnid structure in the cnid hash table * @id: the cnid * * Returns the cnid structure, after creating it if necessary. */ struct listed_cnid *get_listed_cnid(u64 id) { struct htable_entry *entry; entry = get_htable_entry(id, sizeof(struct listed_cnid), vsb->v_cnid_table); return (struct listed_cnid *)entry; } apfsprogs-0.2.0/apfsck/htable.h000066400000000000000000000032171471277137200164120ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _HTABLE_H #define _HTABLE_H #include #define HTABLE_BUCKETS 512 /* So the hash table array fits in 4k */ /* * Structure of the common header for hash table entries */ struct htable_entry { struct htable_entry *h_next; /* Next entry in linked list */ u64 h_id; /* Catalog object id of entry */ }; /* State of the in-memory listed cnid structure */ #define CNID_UNUSED 0 /* The cnid is unused */ #define CNID_IN_SIBLING_LINK 1 /* The cnid was seen in a sibling link */ #define CNID_IN_INODE 2 /* The cnid was seen in an inode */ #define CNID_IN_DSTREAM 4 /* The cnid was seen in a dstream */ /* * Structure used to register each catalog node id (cnid) that has been seen, * and check that they are not repeated. */ struct listed_cnid { struct htable_entry c_htable; /* Hash table entry header */ u8 c_state; }; static inline void cnid_set_state_flag(struct listed_cnid *cnid, u8 flag) { if (cnid->c_state & flag) report("Catalog", "a filesystem object id was used twice."); cnid->c_state |= flag; } extern struct htable_entry **alloc_htable(); extern void free_htable(struct htable_entry **table, void (*free_entry)(struct htable_entry *)); extern void apply_on_htable(struct htable_entry **table, void (*fn)(struct htable_entry *)); extern struct htable_entry *get_htable_entry(u64 id, int size, struct htable_entry **table); extern bool htable_entry_exists(u64 id, struct htable_entry **table); extern void free_cnid_table(struct htable_entry **table); extern struct listed_cnid *get_listed_cnid(u64 id); #endif /* _HTABLE_H */ apfsprogs-0.2.0/apfsck/inode.c000066400000000000000000001015001471277137200162360ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include "apfsck.h" #include "compress.h" #include "dir.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "key.h" #include "super.h" /** * xbmap_set - Set an xfield type in the xfield bitmap * @bmap: the xfield bitmap * @type: the extended field type */ static inline void xbmap_set(u16 *bmap, u8 type) { *bmap |= 1 << type; } /** * xbmap_test - Test if an xfield type is present in the xfield bitmap * @bmap: the xfield bitmap * @type: the extended field type */ static inline bool xbmap_test(u16 bmap, u8 type) { return bmap & (1 << type); } /** * check_finder_info - Check the inode flag that reports finder info * @inode: the inode to check * * I've encountered some old images that put the finder info in a xattr instead * of an xfield, but they still set the inode flag, so this check has to be done * after all xattrs have been parsed. */ static void check_finder_info(struct inode *inode) { bool xfield = xbmap_test(inode->i_xfield_bmap, APFS_INO_EXT_TYPE_FINDER_INFO); bool xattr = inode->i_xattr_bmap & XATTR_BMAP_FINDER_INFO; bool flag = inode->i_flags & APFS_INODE_HAS_FINDER_INFO; if (xfield && xattr) report("Inode record", "duplicated finder info."); if (flag != (xfield || xattr)) report("Inode record", "wrong setting for finder info flag."); } /** * check_inode_stats - Verify the stats gathered by the fsck vs the metadata * @inode: inode structure to check */ static void check_inode_stats(struct inode *inode) { struct dstream *dstream; /* The inodes must be freed before the dstreams */ assert(vsb->v_dstream_table); if ((inode->i_mode & S_IFMT) == S_IFDIR) { if (inode->i_link_count != 1) report("Inode record", "directory has hard links."); if (inode->i_nchildren != inode->i_child_count) report("Inode record", "wrong directory child count."); if (inode->i_first_parent == APFS_PRIV_DIR_INO_NUM && inode->i_nchildren != 0) report("Orphan directory", "has children of its own."); } else if (inode->i_first_parent == APFS_PRIV_DIR_INO_NUM) { if (inode->i_link_count != 1) report("Orphan inode", "not really orphaned."); if (inode->i_nlink != 0) report("Orphan inode", "has a link count."); } else { if (inode->i_nlink != inode->i_link_count) report("Inode record", "wrong link count."); } dstream = inode->i_dstream; if (dstream) { if (dstream->d_refcnt > 1 && !(inode->i_flags & (APFS_INODE_WAS_CLONED | APFS_INODE_WAS_EVER_CLONED))) report("Inode record", "wrong flags for cloned inode."); if (inode->i_first_parent == APFS_PRIV_DIR_INO_NUM) { dstream->d_orphan = true; } else if (dstream->d_sparse_bytes != inode->i_sparse_bytes) { /* The official fsck ignores this field for orphans */ report("Inode record", "wrong count of sparse bytes."); } } else { if (inode->i_sparse_bytes) report("Inode record", "sparse bytes without dstream."); } if ((bool)(inode->i_xattr_bmap & XATTR_BMAP_SYMLINK) != (bool)((inode->i_mode & S_IFMT) == S_IFLNK)) report("Inode record", "symlink inode should come with target xattr."); if ((bool)(inode->i_xattr_bmap & XATTR_BMAP_RSRC_FORK) != (bool)(inode->i_flags & APFS_INODE_HAS_RSRC_FORK)) report("Inode record", "wrong flag for resource fork."); if ((bool)(inode->i_xattr_bmap & XATTR_BMAP_SECURITY) != (bool)(inode->i_flags & APFS_INODE_HAS_SECURITY_EA)) report("Inode record", "wrong flag for access control list."); if ((bool)(inode->i_purg_name) != (bool)(inode->i_flags & APFS_INODE_IS_PURGEABLE)) report("Inode record", "wrong purgeability flag."); check_finder_info(inode); } static void check_purgeable_name(struct inode *inode) { const char *format = "0x%llx-0x%llx:%u"; char *buf; int buflen; struct dstream *dstream = NULL; unsigned long long filesize; dstream = inode->i_dstream; filesize = dstream ? dstream->d_bytes : 0; buflen = snprintf(NULL, 0, format, filesize, (unsigned long long)inode->i_ino, (unsigned int)inode->i_owner); if (buflen < 0) system_error(); buf = calloc(1, ++buflen); if (!buf) system_error(); buflen = snprintf(buf, buflen, format, filesize, (unsigned long long)inode->i_ino, (unsigned int)inode->i_owner); if (buflen < 0) system_error(); if (strcmp(inode->i_purg_name, buf) != 0) report("Purgeable inode", "wrong name for purgeable dentry."); free(buf); } /** * free_inode_names - Free all data on an inode's names * @inode: inode to free * * Frees the primary name and all sibling links, but not before running a few * remaining consistency checks. */ static void free_inode_names(struct inode *inode) { struct sibling *current = inode->i_siblings; struct sibling *next; u32 count = 0; if (!inode->i_name) /* Oddly, this seems to be always required */ report("Inode record", "no name for primary link."); if (!inode->i_first_name) report("Catalog", "inode with no dentries."); if (inode->i_flags & APFS_INODE_ACTIVE_FILE_TRIMMED) { /* No idea if any of this is actually required */ if (strcmp(inode->i_name, ".overprovisioning_file")) report("Overprovisioning file", "wrong name."); if (inode->i_link_count != 1) report("Overprovisioning file", "has hard links."); } if (inode->i_purg_name) { check_purgeable_name(inode); free(inode->i_purg_name); inode->i_purg_name = NULL; } if (current) { /* Primary link has lowest id, so it comes first in the list */ if (strcmp(inode->i_name, (char *)current->s_name)) report("Inode record", "wrong name for primary link."); if (inode->i_parent_id != current->s_parent_ino) report("Inode record", "bad parent for primary link."); } else if (inode->i_first_parent != APFS_PRIV_DIR_INO_NUM) { /* * No siblings, so the primary link is the first and only. * Files moved to the private directory preserve their original * name and parent_id, so there's nothing to check. */ if (strcmp(inode->i_name, inode->i_first_name)) report("Inode record", "wrong name for only link."); if (inode->i_parent_id != inode->i_first_parent) report("Inode record", "bad parent for only link."); } free(inode->i_name); inode->i_name = NULL; free(inode->i_first_name); inode->i_first_name = NULL; while (current) { struct listed_cnid *cnid; /* Put all filesystem object ids in a list to check for reuse */ cnid = get_listed_cnid(current->s_id); cnid_set_state_flag(cnid, CNID_IN_SIBLING_LINK); if (!current->s_checked) report("Catalog", "orphaned or missing sibling link."); if (!current->s_mapped) report("Catalog", "no sibling map for link."); next = current->s_next; free(current->s_name); free(current); current = next; ++count; } if (inode->i_first_parent == APFS_PRIV_DIR_INO_NUM) { if (count != 0) report("Orphan inode", "has sibling links."); return; } /* Inodes with one link can have a sibling record, but don't need it */ if (inode->i_link_count == 1 && count == 0) return; if (count != inode->i_link_count) report("Inode record", "link count inconsistent with sibling records."); } /** * free_inode - Free an inode structure after performing some final checks * @entry: the entry to free */ static void free_inode(struct htable_entry *entry) { struct inode *inode = (struct inode *)entry; struct compress *compress = inode->i_compress; struct listed_cnid *cnid; /* All of these must still be around for the inodes to access */ assert(vsb->v_cnid_table); assert(vsb->v_dirstat_table); assert(vsb->v_dstream_table); /* To check for reuse, put all filesystem object ids in a list */ cnid = get_listed_cnid(inode->i_ino); cnid_set_state_flag(cnid, CNID_IN_INODE); if (compress) { apfs_compress_open(compress); apfs_compress_check(compress); verify_dstream_hashes(compress->rsrc_dstream, compress); apfs_compress_close(compress); free(compress->decmpfs); free(compress); inode->i_compress = NULL; } check_inode_stats(inode); free_inode_names(inode); free(entry); } static void collect_dirstats(struct htable_entry *entry) { struct inode *inode = (struct inode *)entry; u16 filetype = inode->i_mode & S_IFMT; struct dirstat *stat = inode->i_dirstat; struct dirstat *parent_stat = NULL; /* Orphans report a stale parent id, don't take that seriously */ if (inode->i_first_parent == APFS_PRIV_DIR_INO_NUM) return; if (inode->i_parent_id >= APFS_MIN_USER_INO_NUM) { parent_stat = get_inode(inode->i_parent_id)->i_dirstat; if (stat && parent_stat && stat != parent_stat) report("Inode record", "dirstat id differs from parent."); } if (inode->i_flags & APFS_INODE_MAINTAIN_DIR_STATS) { assert(stat); if (!stat->ds_seen) report("Inode record", "missing a dirstats record."); if (!(inode->i_flags & APFS_INODE_DIR_STATS_ORIGIN) && !parent_stat) report("Directory statistics", "origin missing."); stat->ds_child_count += inode->i_nchildren; } else if (parent_stat) { if (inode->i_flags & APFS_INODE_DIR_STATS_ORIGIN) report_unknown("Nested dirstat origins."); if (filetype == S_IFDIR) report("Inode record", "should gather dir stats for ancestor."); else if (inode->i_dstream) parent_stat->ds_total_size += inode->i_dstream->d_size; } } /** * free_inode_table - Free the inode hash table and all its entries * @table: table to free * * Also performs some consistency checks that can only be done after the whole * catalog has been parsed. */ void free_inode_table(struct htable_entry **table) { /* * Collect directory statistics, and check that descendant directories * inherit the APFS_INODE_MAINTAIN_DIR_STATS flag. */ apply_on_htable(table, collect_dirstats); free_htable(table, free_inode); } /** * get_inode - Find or create an inode structure in the inode hash table * @ino: inode number * * Returns the inode structure, after creating it if necessary. */ struct inode *get_inode(u64 ino) { struct htable_entry *entry; entry = get_htable_entry(ino, sizeof(struct inode), vsb->v_inode_table); return (struct inode *)entry; } /** * read_sparse_bytes_xfield - Parse and check an xfield that counts sparse bytes * @xval: pointer to the xfield value * @len: remaining length of the inode value * @inode: struct to receive the results * * Returns the length of the xfield value. */ static int read_sparse_bytes_xfield(char *xval, int len, struct inode *inode) { __le64 *sbytes; if (len < 8) report("Sparse bytes xfield", "doesn't fit in inode record."); sbytes = (__le64 *)xval; inode->i_sparse_bytes = le64_to_cpu(*sbytes); return sizeof(*sbytes); } /** * read_document_id_xfield - Parse and check a document id xfield * @xval: pointer to the xfield value * @len: remaining length of the inode value * @inode: inode structure * * Returns the length of the xfield value. */ static int read_document_id_xfield(char *xval, int len, struct inode *inode) { __le32 *id_raw; u32 id; if (len < 4) report("Document id xfield", "doesn't fit in inode record."); id_raw = (__le32 *)xval; id = le32_to_cpu(*id_raw); if (id < APFS_MIN_DOC_ID) report("Document id xfield", "invalid id in use."); if (id >= vsb->v_next_doc_id) report("Document id xfield", "free id in use."); return sizeof(*id_raw); } /** * read_rdev_xfield - Parse and check a device identifier xfield * @xval: pointer to the xfield value * @len: remaining length of the inode value * @inode: struct to receive the results * * Returns the length of the xfield value. */ static int read_rdev_xfield(char *xval, int len, struct inode *inode) { u16 filetype = inode->i_mode & S_IFMT; __le32 *rdev; assert(filetype); /* Mode must be set before parsing xfields */ if (filetype != S_IFCHR && filetype != S_IFBLK) report("Inode record", "not device but has device identifier."); if (len < 4) report("Device ID xfield", "doesn't fit in inode record."); rdev = (__le32 *)xval; inode->i_rdev = le32_to_cpu(*rdev); return sizeof(*rdev); } /** * read_name_xfield - Parse a name xfield and check its consistency * @xval: pointer to the xfield value * @len: remaining length of the inode value * @inode: struct to receive the results * * Returns the length of the xfield value. */ static int read_name_xfield(char *xval, int len, struct inode *inode) { int xlen; xlen = strnlen(xval, len - 1) + 1; if (xval[xlen - 1] != 0) report("Name xfield", "name with no null termination"); inode->i_name = malloc(xlen); if (!inode->i_name) system_error(); strcpy(inode->i_name, xval); return xlen; } /** * read_dstream_xfield - Parse a dstream xfield and check its consistency * @xval: pointer to the xfield value * @len: remaining length of the inode value * @inode: struct to receive the results * * Returns the length of the xfield value. */ static int read_dstream_xfield(char *xval, int len, struct inode *inode) { struct apfs_dstream *dstream_raw; struct dstream *dstream; u64 size, alloced_size; u64 crypid; if ((inode->i_mode & S_IFMT) != S_IFREG) report("Inode record", "has dstream but isn't a regular file."); if (len < sizeof(*dstream_raw)) report("Dstream xfield", "doesn't fit in inode record."); dstream_raw = (struct apfs_dstream *)xval; size = le64_to_cpu(dstream_raw->size); alloced_size = le64_to_cpu(dstream_raw->alloced_size); crypid = le64_to_cpu(dstream_raw->default_crypto_id); if (crypid && crypid != APFS_CRYPTO_SW_ID) { /* * I'm not yet sure how this crypto cloning thing is supposed * to work, but it's very common (TODO). */ if (crypid == APFS_UNASSIGNED_CRYPTO_ID) { if (!(inode->i_flags & APFS_INODE_WAS_CLONED)) report("Dstream xfield", "not a clone but has unassigned crypto."); } else { struct crypto_state *crypto = get_crypto_state(crypid); ++crypto->c_references; if (inode->i_flags & APFS_INODE_ACTIVE_FILE_TRIMMED) crypto->c_overprov = true; } } dstream = get_dstream(inode->i_private_id); if (dstream->d_references) { /* A dstream structure for this id has already been seen */ if (dstream->d_obj_type != APFS_TYPE_INODE) report("Dstream xfield", "shared by inode and xattr."); if (dstream->d_size != size) report("Dstream xfield", "inconsistent size for stream."); if (dstream->d_alloced_size != alloced_size) report("Dstream xfield", "inconsistent allocated size for stream."); } else { dstream->d_obj_type = APFS_TYPE_INODE; dstream->d_size = size; dstream->d_alloced_size = alloced_size; } dstream->d_xattr = false; dstream->d_references++; dstream->d_owner = dstream->d_id; inode->i_dstream = dstream; return sizeof(*dstream_raw); } /** * read_dir_stats_xfield - Parse a dir stats xfield and check its consistency * @xval: pointer to the xfield value * @len: remaining length of the inode value * @inode: struct to receive the results * * Returns the length of the xfield value. */ static int read_dir_stats_xfield(char *xval, int len, struct inode *inode) { u16 filetype = inode->i_mode & S_IFMT; __le64 *oid = NULL; struct dirstat *stats = NULL; /* TODO: I'm yet to see a file with stats, this is probably wrong */ if (filetype != S_IFDIR) report("Dir stats xfield", "inode is not a directory."); if (len < sizeof(*oid)) report("Dir stats xfield", "doesn't fit in inode record."); oid = (__le64 *)xval; stats = inode->i_dirstat = get_dirstat(le64_to_cpu(*oid)); if (inode->i_flags & APFS_INODE_DIR_STATS_ORIGIN) { /* TODO: this will be a problem for nested origins */ if (stats->ds_origin_seen) report("Dir stats", "has two origins."); stats->ds_origin_seen = true; stats->ds_origin = inode->i_ino; } return sizeof(*oid); } static int read_purgeable_flags_xfield(char *xval, int len, struct inode *inode) { __le64 *flags; if (len < sizeof(*flags)) report("Purgeable flags xfield", "doesn't fit in inode record."); flags = (__le64 *)xval; /* * TODO: Figure out these flags. So far I have only seen them set to * 0x10005, in a directory that was not purgeable itself, but had mostly * purgeable entries. */ inode->i_purg_flags = le64_to_cpu(*flags); return sizeof(*flags); } /** * check_xfield_flags - Run common flag checks for all xfield types * @flags: flags to check */ void check_xfield_flags(u8 flags) { if (flags & APFS_XF_RESERVED_4 || flags & APFS_XF_RESERVED_40 || flags & APFS_XF_RESERVED_80) report("Inode xfield", "reserved flag in use."); if (flags & APFS_XF_USER_FIELD && flags & APFS_XF_SYSTEM_FIELD) report("Inode xfield", "created by both user and kernel."); } /** * check_xfield_inode_flags - Check that xfields are consistent with inode flags * @bmap: bitmap of xfield types seen in the inode * @flags: inode flags * * This doesn't check the finder info flag, that happens when we free the inode. */ static void check_xfield_inode_flags(u16 bmap, u64 flags) { if (xbmap_test(bmap, APFS_INO_EXT_TYPE_DIR_STATS_KEY) != (bool)(flags & APFS_INODE_MAINTAIN_DIR_STATS)) report("Inode record", "wrong setting for dir stats flag."); if (xbmap_test(bmap, APFS_INO_EXT_TYPE_SPARSE_BYTES) != (bool)(flags & APFS_INODE_IS_SPARSE)) report("Inode record", "wrong setting for sparse flag."); if (xbmap_test(bmap, APFS_INO_EXT_TYPE_PURGEABLE_FLAGS) != (bool)(flags & APFS_INODE_HAS_PURGEABLE_FLAGS)) report("Inode record", "wrong setting for purgeable flags option."); } /** * parse_inode_xfields - Parse and check an inode extended fields * @xblob: pointer to the beginning of the xfields in the inode value * @len: length of the xfields * @inode: struct to receive the results * * Internal consistency of @key must be checked before calling this function. */ static void parse_inode_xfields(struct apfs_xf_blob *xblob, int len, struct inode *inode) { u16 filetype = inode->i_mode & S_IFMT; struct apfs_x_field *xfield; u16 type_bitmap = 0; char *xval; int xcount; int i; if (len == 0) { /* No extended fields */ check_xfield_inode_flags(type_bitmap, inode->i_flags); return; } len -= sizeof(*xblob); if (len < 0) report("Inode records", "no room for extended fields."); xcount = le16_to_cpu(xblob->xf_num_exts); if (!xcount) report("Inode record", "xfield blob has no xfields."); xfield = (struct apfs_x_field *)xblob->xf_data; xval = (char *)xfield + xcount * sizeof(xfield[0]); len -= xcount * sizeof(xfield[0]); if (len < 0) report("Inode record", "number of xfields cannot fit."); /* The official reference seems to be wrong here */ if (le16_to_cpu(xblob->xf_used_data) != len) report("Inode record", "value size incompatible with xfields."); for (i = 0; i < le16_to_cpu(xblob->xf_num_exts); ++i) { int xlen, xpad_len; u8 xflags = xfield[i].x_flags; check_xfield_flags(xflags); switch (xfield[i].x_type) { case APFS_INO_EXT_TYPE_FS_UUID: xlen = 16; report_unknown("UUID xfield"); break; case APFS_INO_EXT_TYPE_PREV_FSIZE: xlen = 8; report_crash("Inode xfield"); if (xflags != 0) report("Previous size xfield", "wrong flags."); break; case APFS_INO_EXT_TYPE_SNAP_XID: xlen = 8; report_unknown("Snapshot id xfield"); break; case APFS_INO_EXT_TYPE_DELTA_TREE_OID: xlen = 8; report_unknown("Snapshot's extent delta list xfield"); break; case APFS_INO_EXT_TYPE_SPARSE_BYTES: xlen = read_sparse_bytes_xfield(xval, len, inode); if (xflags != (APFS_XF_SYSTEM_FIELD | APFS_XF_CHILDREN_INHERIT)) report("Sparse bytes xfield", "wrong flags."); break; case APFS_INO_EXT_TYPE_DOCUMENT_ID: xlen = read_document_id_xfield(xval, len, inode); report_unknown("Document id xfield"); break; case APFS_INO_EXT_TYPE_FINDER_INFO: xlen = 32; report_unknown("Finder info xfield"); break; case APFS_INO_EXT_TYPE_RDEV: xlen = read_rdev_xfield(xval, len, inode); break; case APFS_INO_EXT_TYPE_NAME: xlen = read_name_xfield(xval, len, inode); if (xflags != APFS_XF_DO_NOT_COPY) report("Name xfield", "wrong flags."); break; case APFS_INO_EXT_TYPE_DSTREAM: xlen = read_dstream_xfield(xval, len, inode); if (xflags != APFS_XF_SYSTEM_FIELD) report("Data stream xfield", "wrong flags."); break; case APFS_INO_EXT_TYPE_DIR_STATS_KEY: xlen = read_dir_stats_xfield(xval, len, inode); if (xflags != (APFS_XF_SYSTEM_FIELD | APFS_XF_DO_NOT_COPY)) report("Dir stats xfield", "wrong flags."); break; case APFS_INO_EXT_TYPE_PURGEABLE_FLAGS: xlen = read_purgeable_flags_xfield(xval, len, inode); break; case APFS_INO_EXT_TYPE_ORIG_SYNC_ROOT_ID: xlen = 8; report_unknown("Sync root id xfield"); break; case APFS_INO_EXT_TYPE_RESERVED_6: case APFS_INO_EXT_TYPE_RESERVED_9: case APFS_INO_EXT_TYPE_RESERVED_12: report("Inode xfield", "reserved type in use."); break; default: report("Inode xfield", "invalid type."); } if (xbmap_test(type_bitmap, xfield[i].x_type)) report("Inode record", "two xfields of the same type."); xbmap_set(&type_bitmap, xfield[i].x_type); if (xlen != le16_to_cpu(xfield[i].x_size)) report("Inode xfield", "wrong size"); len -= xlen; xval += xlen; /* Attribute length is padded with zeroes to a multiple of 8 */ xpad_len = ROUND_UP(xlen, 8) - xlen; len -= xpad_len; if (len < 0) report("Inode xfield", "does not fit in record value."); for (; xpad_len; ++xval, --xpad_len) if (*xval) report("Inode xfield", "non-zero padding."); } if (len) report("Inode record", "length of xfields does not add up."); check_xfield_inode_flags(type_bitmap, inode->i_flags); inode->i_xfield_bmap = type_bitmap; if ((filetype == S_IFCHR || filetype == S_IFBLK) && !xbmap_test(type_bitmap, APFS_INO_EXT_TYPE_RDEV)) report("Inode record", "device file with no device ID."); } /** * ino_after_cloneinfo_epoch - Is WAS_EVER_CLONED valid for this inode? * @ino: inode number to check */ static bool ino_after_cloneinfo_epoch(u64 ino) { struct apfs_superblock *raw = vsb->v_raw; u64 id_epoch, xid; id_epoch = le64_to_cpu(raw->apfs_cloneinfo_id_epoch); xid = le64_to_cpu(raw->apfs_cloneinfo_xid); /* * The epoch hasn't even started for this filesystem, all flags should * be assumed corrupted. */ if (!xid) return false; /* * This filesystem was last mounted by a buggy implementation, so flags * for newer inodes may have become corrupted. */ if (xid != vsb->v_last_xid) return false; /* * This filesystem was never even mounted by a buggy implementation: * all flags should be correct. */ if (!id_epoch) return true; /* * The reference seems to claim that this should be '>', but I have my * doubts beause I've seen fresh images with epoch == MIN_USER_INO_NUM. * There is no harm in being more strict here and waiting to see if we * trip on something. */ return ino >= id_epoch; } /** * check_inode_internal_flags - Check basic consistency of inode flags * @flags: flags to check * @ino: inode number */ static void check_inode_internal_flags(u64 flags, u64 ino) { if ((flags & APFS_VALID_INTERNAL_INODE_FLAGS) != flags) report("Inode record", "invalid flags in use."); if ((flags & APFS_INODE_DIR_STATS_ORIGIN) && !(flags & APFS_INODE_MAINTAIN_DIR_STATS)) report("Inode record", "incompatible directory stats flags."); if (flags & APFS_INODE_HAS_RSRC_FORK && flags & APFS_INODE_NO_RSRC_FORK) report("Inode record", "incompatible resource fork flags."); if (flags & APFS_INODE_BEING_TRUNCATED) report_crash("Inode internal flags"); if (flags & APFS_INODE_PINNED_MASK) { /* * Preboot volume seems to be mostly pinned to main, even in * non-fusion drives. */ if ((flags & APFS_INODE_PINNED_TO_TIER2) || apfs_volume_role() != APFS_VOL_ROLE_PREBOOT) report_unknown("Fusion drive"); if ((flags & APFS_INODE_PINNED_TO_TIER2) && (flags & APFS_INODE_PINNED_TO_MAIN)) report("Inode record", "pinned to both tiers."); } if (flags & APFS_INODE_ALLOCATION_SPILLEDOVER) report_unknown("Fusion drive"); if (flags & APFS_INODE_IS_APFS_PRIVATE) report_unknown("Private implementation inode"); if (flags & APFS_INODE_WAS_CLONED && !(flags & APFS_INODE_WAS_EVER_CLONED)) { if (ino_after_cloneinfo_epoch(ino)) report("Inode record", "inconsistent clone flags."); else printf("Warning: bad WAS_EVER_CLONED flag for inode 0x%llx\n", (unsigned long long)ino); } } /** * inos_valid_for_sysvol_in_group - Can a system vol in a group use these inos? * @ino: inode number * @parent_ino: parent inode number */ static bool inos_valid_for_sysvol_in_group(u64 ino, u64 parent_ino) { /* * Reserved inode numbers in volume groups always seem to come * from the data range, even if this contradicts the reference. */ if (ino < APFS_MIN_USER_INO_NUM && parent_ino < APFS_MIN_USER_INO_NUM) return true; if (ino < APFS_UNIFIED_ID_SPACE_MARK + APFS_MIN_USER_INO_NUM) return false; if (parent_ino < APFS_MIN_USER_INO_NUM) return true; if (parent_ino < APFS_UNIFIED_ID_SPACE_MARK + APFS_MIN_USER_INO_NUM) return false; return true; } /** * check_inode_ids - Check that an inode id is consistent with its parent id * @ino: inode number * @parent_ino: parent inode number */ void check_inode_ids(u64 ino, u64 parent_ino) { if (ino >= vsb->v_next_obj_id || parent_ino >= vsb->v_next_obj_id) report("Inode record", "free inode number in use."); if (ino < APFS_MIN_USER_INO_NUM) { switch (ino) { case APFS_INVALID_INO_NUM: case APFS_ROOT_DIR_PARENT: case APFS_PURGEABLE_DIR_INO_NUM: report("Inode record", "invalid inode number."); case APFS_ROOT_DIR_INO_NUM: case APFS_PRIV_DIR_INO_NUM: case APFS_SNAP_DIR_INO_NUM: /* All children of this fake parent? TODO: check this */ if (parent_ino != APFS_ROOT_DIR_PARENT) report("Root inode record", "bad parent id"); break; default: report("Inode record", "reserved inode number."); } return; } if (parent_ino < APFS_MIN_USER_INO_NUM) { switch (parent_ino) { case APFS_INVALID_INO_NUM: report("Inode record", "invalid parent inode number."); case APFS_ROOT_DIR_PARENT: report("Inode record", "root parent id for nonroot."); case APFS_ROOT_DIR_INO_NUM: case APFS_PRIV_DIR_INO_NUM: case APFS_SNAP_DIR_INO_NUM: case APFS_PURGEABLE_DIR_INO_NUM: /* These are fine */ break; default: report("Inode record", "reserved parent inode number."); } } if (apfs_is_data_volume_in_group()) { if (ino >= APFS_UNIFIED_ID_SPACE_MARK || parent_ino >= APFS_UNIFIED_ID_SPACE_MARK) report("Inode record", "bad number for data volume inode."); } else if (apfs_is_system_volume_in_group()) { if (!inos_valid_for_sysvol_in_group(ino, parent_ino)) report("Inode record", "bad number for system volume inode."); } } /** * parse_inode_record - Parse an inode record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_inode_record(struct apfs_inode_key *key, struct apfs_inode_val *val, int len) { struct inode *inode; u16 mode, filetype; u32 def_prot_class, bsd_flags; if (len < sizeof(*val)) report("Inode record", "value is too small."); inode = get_inode(cat_cnid(&key->hdr)); if (inode->i_seen) report("Catalog", "inode numbers are repeated."); inode->i_seen = true; inode->i_private_id = le64_to_cpu(val->private_id); inode->i_parent_id = le64_to_cpu(val->parent_id); check_inode_ids(inode->i_ino, inode->i_parent_id); if (inode->i_parent_id == APFS_PRIV_DIR_INO_NUM) { /* The reported parent id is not updated when orphaned */ report("Inode record", "parent is private directory."); } if (inode->i_ino == APFS_ROOT_DIR_INO_NUM) vsb->v_has_root = true; if (inode->i_ino == APFS_PRIV_DIR_INO_NUM) vsb->v_has_priv = true; inode->i_flags = le64_to_cpu(val->internal_flags); check_inode_internal_flags(inode->i_flags, inode->i_ino); if (inode->i_ino != inode->i_private_id && !(inode->i_flags & (APFS_INODE_WAS_CLONED | APFS_INODE_WAS_EVER_CLONED))) report("Inode record", "not a clone but changed private id."); def_prot_class = le32_to_cpu(val->default_protection_class); if (def_prot_class > APFS_PROTECTION_CLASS_F || def_prot_class == 5) report("Inode record", "invalid default protection class"); bsd_flags = le32_to_cpu(val->bsd_flags); if (bsd_flags & APFS_INOBSD_COMPRESSED) { inode->i_compress = calloc(1, sizeof(*inode->i_compress)); if (!inode->i_compress) system_error(); } mode = le16_to_cpu(val->mode); filetype = mode & S_IFMT; /* A dentry may have already set the mode, but only the type bits */ if (inode->i_mode && inode->i_mode != filetype) report("Inode record", "file mode doesn't match dentry type."); inode->i_mode = mode; switch (filetype) { case S_IFREG: vsb->v_file_count++; break; case S_IFDIR: if (inode->i_ino >= APFS_MIN_USER_INO_NUM) vsb->v_dir_count++; break; case S_IFLNK: vsb->v_symlink_count++; break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: vsb->v_special_count++; break; default: report("Inode record", "invalid file mode."); } inode->i_nlink = le32_to_cpu(val->nlink); inode->i_owner = le32_to_cpu(val->owner); if (le16_to_cpu(val->pad1)) report("Inode record", "padding should be zeroes."); if (!(inode->i_flags & APFS_INODE_HAS_UNCOMPRESSED_SIZE) && le64_to_cpu(val->uncompressed_size)) report("Inode record", "should not report uncompressed size."); parse_inode_xfields((struct apfs_xf_blob *)val->xfields, len - sizeof(*val), inode); } /** * get_sibling - Find or create a sibling link structure for an inode * @id: sibling id * @inode: the inode * * Returns the sibling structure, after creating it if necessary. */ struct sibling *get_sibling(u64 id, struct inode *inode) { struct sibling **entry_p = &inode->i_siblings; struct sibling *entry = *entry_p; struct sibling *new; /* Siblings are ordered by id in the inode's linked list */ while (entry) { if (id == entry->s_id) return entry; if (id < entry->s_id) break; entry_p = &entry->s_next; entry = *entry_p; } new = calloc(1, sizeof(*new)); if (!new) system_error(); new->s_checked = false; new->s_id = id; new->s_next = entry; *entry_p = new; return new; } /** * set_or_check_sibling - Set or check the fields of a sibling structure * @parent_id: parent id * @namelen: length of the name * @name: name of the sibling * @sibling: the sibling structure * * When first called for @sibling, sets the three given fields. On the second * call, checks that they are set to the correct values. */ void set_or_check_sibling(u64 parent_id, int namelen, u8 *name, struct sibling *sibling) { /* Whichever was read first, dentry or sibling, sets the fields */ if (!sibling->s_name) { sibling->s_parent_ino = parent_id; sibling->s_name_len = namelen; sibling->s_name = malloc(namelen); if (!sibling->s_name) system_error(); strcpy((char *)sibling->s_name, (char *)name); return; } /* Fields already set, check them */ if (sibling->s_name_len != namelen) report("Sibling record", "name length doesn't match dentry's."); if (strcmp((char *)sibling->s_name, (char *)name)) report("Sibling record", "name doesn't match dentry's."); if (sibling->s_parent_ino != parent_id) report("Sibling record", "parent id doesn't match dentry's."); sibling->s_checked = true; } /** * parse_sibling_record - Parse and check a sibling link record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_sibling_record(struct apfs_sibling_link_key *key, struct apfs_sibling_val *val, int len) { struct inode *inode; struct sibling *sibling; int namelen; if (len < sizeof(*val)) report("Sibling link record", "value is too small."); namelen = le16_to_cpu(val->name_len); if (len != sizeof(*val) + namelen) report("Sibling link record", "wrong size of value."); if (val->name[namelen - 1] != 0) report("Sibling link record", "name lacks NULL-termination."); /* Name length doesn't need checking: it's the same for the dentry */ inode = get_inode(cat_cnid(&key->hdr)); if (!inode->i_seen) /* The b-tree keys are in order */ report("Sibling link record", "inode is missing"); sibling = get_sibling(le64_to_cpu(key->sibling_id), inode); /* It seems that sibling ids come from the same pool as inode numbers */ if (sibling->s_id < APFS_MIN_USER_INO_NUM) report("Sibling record", "invalid sibling id."); if (sibling->s_id >= vsb->v_next_obj_id) report("Sibling record", "free id in use."); set_or_check_sibling(le64_to_cpu(val->parent_id), namelen, val->name, sibling); } /** * parse_sibling_record - Parse and check a sibling map record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_sibling_map_record(struct apfs_sibling_map_key *key, struct apfs_sibling_map_val *val, int len) { struct inode *inode; struct sibling *sibling; if (len != sizeof(*val)) report("Sibling map record", "wrong size of value."); inode = get_inode(le64_to_cpu(val->file_id)); sibling = get_sibling(cat_cnid(&key->hdr), inode); sibling->s_mapped = true; } apfsprogs-0.2.0/apfsck/inode.h000066400000000000000000000063601471277137200162530ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _INODE_H #define _INODE_H #include /* The macros for the inode mode */ #include #include "htable.h" struct apfs_inode_key; struct apfs_sibling_link_key; struct apfs_sibling_map_key; /* Flags for the bitmap of seen system xattrs (i_xattr_bmap) */ #define XATTR_BMAP_SYMLINK 0x01 /* Symlink target xattr */ #define XATTR_BMAP_RSRC_FORK 0x02 /* Resource fork xattr */ #define XATTR_BMAP_SECURITY 0x04 /* Security xattr */ #define XATTR_BMAP_FINDER_INFO 0x08 /* Finder info xattr */ #define XATTR_BMAP_COMPRESSED 0x10 /* Compression header xattr */ /* * Inode data in memory */ struct inode { struct htable_entry i_htable; /* Hash table entry header */ u64 i_private_id; /* Id of the inode's data stream */ bool i_seen; /* Has this inode been seen? */ /* Inode information read from its record (or from its dentries) */ u16 i_mode; /* File mode */ union { u32 i_nchildren; /* Number of children of directory */ u32 i_nlink; /* Number of hard links to file */ }; u64 i_sparse_bytes; /* Number of sparse bytes */ u64 i_flags; /* Internal flags */ u32 i_rdev; /* Device ID */ char *i_name; /* Name of primary link */ u64 i_parent_id; /* Parent id for the primary link */ struct dstream *i_dstream; /* The inode's dstream (can be NULL) */ u64 i_purg_flags; /* Inode purgeable flags */ char *i_purg_name; /* Purgeable dentry name (can be NULL) */ u32 i_owner; /* Id of the owner user */ struct dirstat *i_dirstat; /* Directory statistics (can be NULL) */ struct compress *i_compress; /* Compression data (can be NULL) */ /* Inode stats measured by the fsck */ u8 i_xattr_bmap; /* Bitmap of system xattrs for inode */ u16 i_xfield_bmap; /* Bitmap of xfields for inode */ u32 i_child_count; /* Number of children of directory */ u32 i_link_count; /* Number of dentries for file */ char *i_first_name; /* Name of first dentry encountered */ u64 i_first_parent; /* Parent id of the first dentry seen */ struct sibling *i_siblings; /* Linked list of siblings for inode */ }; #define i_ino i_htable.h_id /* Inode number */ /* * Sibling link data in memory */ struct sibling { struct sibling *s_next; /* Next sibling in linked list */ u64 s_id; /* Sibling id */ bool s_checked; /* Has this sibling been checked? */ bool s_mapped; /* Has the sibling map been seen? */ u64 s_parent_ino; /* Inode number for parent */ u16 s_name_len; /* Name length */ u8 *s_name; /* In-memory copy of the name */ }; extern void free_inode_table(struct htable_entry **table); extern struct inode *get_inode(u64 ino); extern void check_inode_ids(u64 ino, u64 parent_ino); extern void parse_inode_record(struct apfs_inode_key *key, struct apfs_inode_val *val, int len); extern struct sibling *get_sibling(u64 id, struct inode *inode); extern void set_or_check_sibling(u64 parent_id, int namelen, u8 *name, struct sibling *sibling); extern void parse_sibling_record(struct apfs_sibling_link_key *key, struct apfs_sibling_val *val, int len); extern void parse_sibling_map_record(struct apfs_sibling_map_key *key, struct apfs_sibling_map_val *val, int len); extern void check_xfield_flags(u8 flags); #endif /* _INODE_H */ apfsprogs-0.2.0/apfsck/key.c000066400000000000000000000301411471277137200157320ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include "apfsck.h" #include "key.h" #include "super.h" /** * read_omap_key - Parse an on-disk object map key * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ void read_omap_key(void *raw, int size, struct key *key) { u64 xid; if (size != sizeof(struct apfs_omap_key)) report("Object map", "wrong size of key."); xid = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_xid); if (!xid) report("Object map", "transaction id for key is zero."); key->id = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_oid); key->type = 0; key->number = xid; key->name = NULL; } /** * read_free_queue_key - Parse an on-disk free-space queue key * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ void read_free_queue_key(void *raw, int size, struct key *key) { struct apfs_spaceman_free_queue_key *sfqk; u64 xid; if (size != sizeof(*sfqk)) report("Free-space queue", "wrong size of key."); sfqk = (struct apfs_spaceman_free_queue_key *)raw; xid = le64_to_cpu(sfqk->sfqk_xid); if (!xid) report("Free-space queue", "transaction id for key is zero."); key->id = xid; key->type = 0; key->number = le64_to_cpu(sfqk->sfqk_paddr); key->name = NULL; } /** * keycmp - Compare two keys * @k1, @k2: keys to compare * * returns 0 if @k1 and @k2 are equal * < 0 if @k1 comes before @k2 in the btree * > 0 if @k1 comes after @k2 in the btree */ int keycmp(struct key *k1, struct key *k2) { if (k1->id != k2->id) return k1->id < k2->id ? -1 : 1; if (k1->type != k2->type) return k1->type < k2->type ? -1 : 1; if (k1->number != k2->number) return k1->number < k2->number ? -1 : 1; if (!k1->name) /* Keys of this type have no name */ return 0; /* Normalization seems to be ignored here, even for directory records */ return strcmp(k1->name, k2->name); } /** * dentry_hash - Find the key hash for a given filename * @name: filename to hash */ static u32 dentry_hash(const char *name) { struct unicursor cursor; bool case_fold = apfs_is_case_insensitive(); u32 hash = 0xFFFFFFFF; init_unicursor(&cursor, name); while (1) { unicode_t utf32; utf32 = normalize_next(&cursor, case_fold); if (!utf32) break; hash = crc32c(hash, &utf32, sizeof(utf32)); } /* Leave room for the filename length */ return (hash & 0x3FFFFF) << 10; } /** * read_dir_rec_key - Parse an on-disk dentry key and check its consistency * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ static void read_dir_rec_key(void *raw, int size, struct key *key) { bool hashed = apfs_is_normalization_insensitive(); int namelen; if (hashed && size < sizeof(struct apfs_drec_hashed_key) + 1) report("Hashed directory record", "wrong size of key."); if (!hashed && size < sizeof(struct apfs_drec_key) + 1) report("Unhashed directory record", "wrong size of key."); if (*((char *)raw + size - 1) != 0) report("Directory record", "filename lacks NULL-termination."); if (hashed) { struct apfs_drec_hashed_key *raw_key = raw; /* The filename length is ignored for the ordering, so mask it away */ key->number = le32_to_cpu(raw_key->name_len_and_hash) & ~0x3FFU; key->name = (char *)raw_key->name; if (key->number != dentry_hash(key->name)) report("Directory record", "filename hash is corrupted."); namelen = le32_to_cpu(raw_key->name_len_and_hash) & 0x3FFU; if (size != sizeof(*raw_key) + namelen) { report("Hashed directory record", "size of key doesn't match the name length."); } } else { struct apfs_drec_key *raw_key = raw; key->number = 0; key->name = (char *)raw_key->name; namelen = le16_to_cpu(raw_key->name_len); if (size != sizeof(*raw_key) + namelen) { report("Unhashed directory record", "size of key doesn't match the name length."); } } if (namelen > 256) { /* The name must fit in name_buf from parse_subtree() */ report("Directory record", "name is too long."); } if (strlen(key->name) + 1 != namelen) { /* APFS counts the NULL termination for the filename length */ report("Directory record", "wrong name length in key."); } } /** * read_xattr_key - Parse an on-disk xattr key and check its consistency * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ static void read_xattr_key(void *raw, int size, struct key *key) { struct apfs_xattr_key *raw_key; int namelen; if (size < sizeof(struct apfs_xattr_key) + 1) report("Xattr record", "wrong size of key."); if (*((char *)raw + size - 1) != 0) report("Xattr record", "name lacks NULL-termination."); raw_key = raw; key->number = 0; key->name = (char *)raw_key->name; namelen = le16_to_cpu(raw_key->name_len); if (namelen > 256) { /* The name must fit in name_buf from parse_subtree() */ report("Xattr record", "name is too long."); } if (strlen(key->name) + 1 != namelen) { /* APFS counts the NULL termination in the string length */ report("Xattr record", "wrong name length."); } if (size != sizeof(struct apfs_xattr_key) + namelen) { report("Xattr record", "size of key doesn't match the name length."); } } /** * read_snap_name_key - Parse an on-disk snapshot name key and check its * consistency * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result * * TODO: this is the same as read_xattr_key(), maybe they could be merged. */ static void read_snap_name_key(void *raw, int size, struct key *key) { struct apfs_snap_name_key *raw_key; int namelen; if (size < sizeof(struct apfs_snap_name_key) + 1) report("Snapshot name record", "wrong size of key."); if (*((char *)raw + size - 1) != 0) report("Snapshot name record", "name lacks NULL-termination."); raw_key = raw; key->number = 0; key->name = (char *)raw_key->name; namelen = le16_to_cpu(raw_key->name_len); if (namelen > 256) { /* The name must fit in name_buf from parse_subtree() */ report("Snapshot name record", "name is too long."); } if (strlen(key->name) + 1 != namelen) { /* APFS counts the NULL termination in the string length */ report("Snapshot name record", "wrong name length."); } if (size != sizeof(struct apfs_snap_name_key) + namelen) { report("Snapshot name record", "size of key doesn't match the name length."); } } /** * read_file_extent_key - Parse an on-disk extent key and check its consistency * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ static void read_file_extent_key(void *raw, int size, struct key *key) { struct apfs_file_extent_key *raw_key; if (size != sizeof(struct apfs_file_extent_key)) report("Extent record", "wrong size of key."); raw_key = raw; key->number = le64_to_cpu(raw_key->logical_addr); key->name = NULL; if (key->number & (sb->s_blocksize - 1)) report("Extent record", "offset isn't multiple of block size."); } /** * read_file_info_key - Parse an on-disk file info key and check its consistency * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ static void read_file_info_key(void *raw, int size, struct key *key) { struct apfs_file_info_key *raw_key; u64 info_and_lba; if (size != sizeof(struct apfs_file_info_key)) report("File info record", "wrong size of key."); raw_key = raw; info_and_lba = le64_to_cpu(raw_key->info_and_lba); if ((info_and_lba >> APFS_FILE_INFO_TYPE_SHIFT) != APFS_FILE_INFO_DATA_HASH) report("File info record", "undocumented type."); key->number = info_and_lba & APFS_FILE_INFO_LBA_MASK; key->name = NULL; if (key->number & (sb->s_blocksize - 1)) report("File info record", "offset isn't multiple of block size."); } /** * read_sibling_link_key - Parse an on-disk sibling link key and check its * consistency * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ static void read_sibling_link_key(void *raw, int size, struct key *key) { struct apfs_sibling_link_key *raw_key; if (size != sizeof(struct apfs_sibling_link_key)) report("Siblink link record", "wrong size of key."); raw_key = raw; key->number = le64_to_cpu(raw_key->sibling_id); /* Only guessing */ key->name = NULL; } /** * read_cat_key - Parse an on-disk catalog key * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ void read_cat_key(void *raw, int size, struct key *key) { if (size < sizeof(struct apfs_key_header)) report("Catalog tree", "key is too small."); key->id = cat_cnid((struct apfs_key_header *)raw); key->type = cat_type((struct apfs_key_header *)raw); if (!key->type || key->type > APFS_TYPE_MAX_VALID) report("Catalog tree", "invalid key type."); switch (key->type) { case APFS_TYPE_DIR_REC: read_dir_rec_key(raw, size, key); return; case APFS_TYPE_XATTR: read_xattr_key(raw, size, key); return; case APFS_TYPE_FILE_EXTENT: read_file_extent_key(raw, size, key); return; case APFS_TYPE_FILE_INFO: read_file_info_key(raw, size, key); return; case APFS_TYPE_SIBLING_LINK: read_sibling_link_key(raw, size, key); return; case APFS_TYPE_EXTENT: report("Catalog tree", "has extent reference record."); case APFS_TYPE_SNAP_METADATA: report("Catalog tree", "has snapshot metadata record."); case APFS_TYPE_SNAP_NAME: report("Catalog tree", "has snapshot name record."); default: /* All other key types are just the header */ if (size != sizeof(struct apfs_key_header)) report("Catalog tree record", "wrong size of key."); key->number = 0; key->name = NULL; return; } } /** * read_fext_key - Parse an on-disk fext key * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ void read_fext_key(void *raw, int size, struct key *key) { struct apfs_fext_tree_key *raw_key; if (size != sizeof(*raw_key)) report("File extents tree", "wrong size of key."); raw_key = raw; key->id = le64_to_cpu(raw_key->private_id); key->type = 0; key->number = le64_to_cpu(raw_key->logical_addr); key->name = NULL; if (key->number & (sb->s_blocksize - 1)) report("Fext record", "offset isn't multiple of block size."); } /** * read_extentref_key - Parse an on-disk extent reference key * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ void read_extentref_key(void *raw, int size, struct key *key) { int type; if (size != sizeof(struct apfs_phys_ext_key)) report("Extent reference tree", "wrong size of key."); type = cat_type((struct apfs_key_header *)raw); if (type != APFS_TYPE_EXTENT) report("Extent reference tree", "wrong record type."); key->id = cat_cnid((struct apfs_key_header *)raw); key->type = type; key->number = 0; key->name = NULL; } /** * read_snap_key - Parse an on-disk key from the snapshot metadata tree * @raw: pointer to the raw key * @size: size of the raw key * @key: key structure to store the result */ void read_snap_key(void *raw, int size, struct key *key) { if (size < sizeof(struct apfs_key_header)) report("Snapshot metadata tree", "key is too small."); key->id = cat_cnid((struct apfs_key_header *)raw); key->type = cat_type((struct apfs_key_header *)raw); switch (key->type) { case APFS_TYPE_SNAP_METADATA: if (size != sizeof(struct apfs_key_header)) report("Snapshot metadata record", "wrong size of key."); key->number = 0; key->name = NULL; return; case APFS_TYPE_SNAP_NAME: if (key->id != (~0ULL & APFS_OBJ_ID_MASK)) report("Snapshot name record", "invalid key header."); return read_snap_name_key(raw, size, key); default: report("Snapshot metadata tree", "invalid key type."); } } void read_omap_snap_key(void *raw, int size, struct key *key) { __le64 *xid; if (size != sizeof(*xid)) report("Omap snapshot tree", "wrong size of key."); xid = raw; key->id = le64_to_cpu(*xid); key->type = 0; key->number = 0; key->name = NULL; } apfsprogs-0.2.0/apfsck/key.h000066400000000000000000000067501471277137200157500ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _KEY_H #define _KEY_H #include struct super_block; /* * In-memory representation of a key, as relevant for a b-tree query. */ struct key { u64 id; u64 number; /* Extent offset or name hash */ const char *name; /* On-disk name string */ u8 type; /* Record type (0 for the omap) */ }; /** * init_omap_key - Initialize an in-memory key for an omap query * @oid: object id * @xid: current transaction id * @key: apfs_key structure to initialize */ static inline void init_omap_key(u64 oid, u64 xid, struct key *key) { key->id = oid; key->type = 0; key->number = xid; key->name = NULL; } /** * init_extref_key - Initialize an in-memory key for an extentref query * @bno: first block number * @key: apfs_key structure to initialize */ static inline void init_extref_key(u64 bno, struct key *key) { key->id = bno; key->type = APFS_TYPE_EXTENT; key->number = 0; key->name = NULL; } /** * init_fext_key - Initialize an in-memory key for a fext query * @id: dstream id * @addr: logical address * @key: apfs_key structure to initialize */ static inline void init_fext_key(u64 id, u64 addr, struct key *key) { key->id = id; key->type = 0; key->number = addr; key->name = NULL; } /** * init_inode_key - Initialize an in-memory key for an inode query * @ino: inode number * @key: key structure to initialize */ static inline void init_inode_key(u64 ino, struct key *key) { key->id = ino; key->type = APFS_TYPE_INODE; key->number = 0; key->name = NULL; } /** * init_file_extent_key - Initialize an in-memory key for an extent query * @id: extent id * @offset: logical address (0 for a multiple query) * @key: key structure to initialize */ static inline void init_file_extent_key(u64 id, u64 offset, struct key *key) { key->id = id; key->type = APFS_TYPE_FILE_EXTENT; key->number = offset; key->name = NULL; } /** * init_xattr_key - Initialize an in-memory key for a xattr query * @ino: inode number of the parent file * @name: xattr name (NULL for a multiple query) * @key: key structure to initialize */ static inline void init_xattr_key(u64 ino, const char *name, struct key *key) { key->id = ino; key->type = APFS_TYPE_XATTR; key->number = 0; key->name = name; } /** * cat_type - Read the record type of a catalog key * @key: the raw catalog key * * The record type is stored in the last byte of the cnid field; this function * returns that value. */ static inline int cat_type(struct apfs_key_header *key) { return (le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_TYPE_MASK) >> APFS_OBJ_TYPE_SHIFT; } /** * cat_cnid - Read the cnid value on a catalog key * @key: the raw catalog key * * The cnid value shares the its field with the record type. This function * masks that part away and returns the result. */ static inline u64 cat_cnid(struct apfs_key_header *key) { return le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_ID_MASK; } extern int keycmp(struct key *k1, struct key *k2); extern void read_cat_key(void *raw, int size, struct key *key); extern void read_omap_key(void *raw, int size, struct key *key); extern void read_extentref_key(void *raw, int size, struct key *key); extern void read_free_queue_key(void *raw, int size, struct key *key); extern void read_snap_key(void *raw, int size, struct key *key); extern void read_omap_snap_key(void *raw, int size, struct key *key); extern void read_fext_key(void *raw, int size, struct key *key); #endif /* _KEY_H */ apfsprogs-0.2.0/apfsck/object.c000066400000000000000000000254201471277137200164140ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "htable.h" #include "object.h" #include "super.h" static int obj_verify_csum_with_size(struct apfs_obj_phys *obj, u32 size) { return le64_to_cpu(obj->o_cksum) == fletcher64((char *)obj + APFS_MAX_CKSUM_SIZE, size - APFS_MAX_CKSUM_SIZE); } int obj_verify_csum(struct apfs_obj_phys *obj) { return obj_verify_csum_with_size(obj, sb->s_blocksize); } /** * read_object_nocheck_internal - Read an object header from disk * @bno: first block number for the object * @size: object size in bytes * @obj: object struct to receive the results * @noheader: does this object have no header? * * Returns a pointer to the raw data of the object in memory, without running * any checks other than the Fletcher verification. */ static void *read_object_nocheck_internal(u64 bno, u32 size, struct object *obj, bool noheader) { struct apfs_obj_phys *raw; raw = apfs_mmap(NULL, size, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if (raw == MAP_FAILED) system_error(); if (noheader) { struct apfs_obj_phys zeroes = {0}; if (memcmp(raw, &zeroes, sizeof(*raw)) != 0) report("No-header object", "has a header."); return raw; } /* This one check is always needed */ if (!obj_verify_csum_with_size(raw, size)) { report("Object header", "bad checksum in block 0x%llx.", (unsigned long long)bno); } obj->oid = le64_to_cpu(raw->o_oid); obj->xid = le64_to_cpu(raw->o_xid); obj->block_nr = bno; obj->type = le32_to_cpu(raw->o_type) & APFS_OBJECT_TYPE_MASK; obj->flags = le32_to_cpu(raw->o_type) & APFS_OBJECT_TYPE_FLAGS_MASK; obj->subtype = le32_to_cpu(raw->o_subtype); obj->size = size; return raw; } void *read_object_nocheck(u64 bno, u32 size, struct object *obj) { return read_object_nocheck_internal(bno, size, obj, false /* noheader */); } /** * parse_object_flags - Check consistency of object flags * @flags: the flags * @encrypted: is the object encrypted? * * Returns the storage type flags to be checked by the caller. */ u32 parse_object_flags(u32 flags, bool encrypted) { if ((flags & APFS_OBJECT_TYPE_FLAGS_DEFINED_MASK) != flags) report("Object header", "undefined flag in use."); if (flags & APFS_OBJ_NONPERSISTENT) report("Object header", "nonpersistent flag is set."); if (flags & APFS_OBJ_NOHEADER) report("Object header", "noheader flag is set."); /* * So-called encrypted objects don't actually appear to be encrypted at * all, no idea what this is about. */ if ((bool)(flags & APFS_OBJ_ENCRYPTED) != encrypted) report("Object header", "wrong encryption flag."); return flags & APFS_OBJ_STORAGETYPE_MASK; } /** * read_object_internal - Read an object header from disk and run some checks * @oid: object id * @omap_table: hash table for the object map (NULL if no translation is needed) * @obj: object struct to receive the results * @noheader: does this object lack a header? * * Returns a pointer to the raw data of the object in memory, after checking * the consistency of some of its fields. */ static void *read_object_internal(u64 oid, struct htable_entry **omap_table, struct object *obj, bool noheader) { struct apfs_obj_phys *raw; struct omap_record *omap_rec = NULL; u64 bno; u64 xid; u32 storage_type; assert(omap_table || !noheader); if (omap_table) { omap_rec = get_latest_omap_record(oid, sb->s_xid, omap_table); if (!omap_rec || !omap_rec->bno) report("Object map", "record missing for id 0x%llx.", (unsigned long long)oid); if ((bool)(omap_rec->flags & APFS_OMAP_VAL_NOHEADER) != noheader) report("Object map", "wrong setting for noheader flag."); if (!ongoing_query) { /* Query code will revisit already parsed nodes */ if (vsb && vsb->v_in_snapshot) { if (omap_rec->seen_for_snap) report("Object map record", "oid used twice for same snapshot."); omap_rec->seen_for_snap = true; } else { if (omap_rec->seen_for_latest) report("Object map record", "oid used twice in latest checkpoint."); omap_rec->seen_for_latest = true; } } bno = omap_rec->bno; } else { bno = oid; } if (noheader) { raw = read_object_nocheck_internal(bno, sb->s_blocksize, obj, noheader); obj->oid = oid; obj->block_nr = bno; obj->xid = omap_rec->xid; } else { raw = read_object_nocheck(bno, sb->s_blocksize, obj); } if (!ongoing_query) { /* Query code will revisit already parsed nodes */ if ((obj->type == APFS_OBJECT_TYPE_SPACEMAN_CIB) || (obj->type == APFS_OBJECT_TYPE_SPACEMAN_CAB)) { ip_bmap_mark_as_used(bno, 1 /* length */); } else if (omap_table) { /* Virtual objects may be shared between snapshots */ if (!omap_rec->seen) { container_bmap_mark_as_used(bno, 1 /* length */); /* The volume super itself doesn't count here */ if (vsb && obj->type != APFS_OBJECT_TYPE_FS) ++vsb->v_block_count; } omap_rec->seen = true; } else { container_bmap_mark_as_used(bno, 1 /* length */); /* Volume superblocks in snapshots don't count either */ if (vsb && obj->type != APFS_OBJECT_TYPE_FS) ++vsb->v_block_count; } } if (oid != obj->oid) report("Object header", "wrong object id in block 0x%llx.", (unsigned long long)bno); /* * The reference claims that these oids are also reserved for physical * objects, but some official images don't respect this. */ if (!(obj->flags & APFS_OBJ_PHYSICAL) && oid < APFS_OID_RESERVED_COUNT) report("Object header", "reserved object id in block 0x%llx (0x%llx) (type: 0x%x).", (unsigned long long)bno, (unsigned long long)oid, obj->flags); if (omap_table && oid >= sb->s_next_oid) report("Object header", "unassigned object id in block 0x%llx.", (unsigned long long)bno); xid = obj->xid; if (!xid) report("Object header", "null transaction id in block 0x%llx.", (unsigned long long)bno); if (sb->s_xid < xid) { /* * When a snapshot is deleted, the following one is given its * physical extents; so its extent reference tree gets altered * under the current transaction. Sealing a volume also seems * to make some changes to existing snapshot objects, but I * haven't looked into it in depth so far (TODO). */ if (!vsb->v_in_snapshot || (!apfs_volume_is_sealed() && obj->subtype != APFS_OBJECT_TYPE_BLOCKREFTREE)) report("Object header", "bad transaction id in block 0x%llx.", (unsigned long long)bno); } if (vsb && vsb->v_first_xid > xid) report_weird("Transaction id in block is older than volume"); if (omap_table && xid != omap_rec->xid) report("Object header", "transaction id in omap key doesn't match block 0x%llx.", (unsigned long long)bno); storage_type = parse_object_flags(obj->flags, vsb && vsb->v_encrypted && obj->subtype == APFS_OBJECT_TYPE_FSTREE); /* Ephemeral objects are handled by read_ephemeral_object() */ if (omap_table && storage_type != APFS_OBJ_VIRTUAL) report("Object header", "wrong flag for virtual object."); if (!omap_table && storage_type != APFS_OBJ_PHYSICAL) report("Object header", "wrong flag for physical object."); return raw; } void *read_object(u64 oid, struct htable_entry **omap_table, struct object *obj) { return read_object_internal(oid, omap_table, obj, false /* noheader */); } void *read_object_noheader(u64 oid, struct htable_entry **omap_table, struct object *obj) { return read_object_internal(oid, omap_table, obj, true /* noheader */); } /** * free_cpoint_map - Free a map structure after performing some final checks * @entry: the entry to free */ static void free_cpoint_map(struct htable_entry *entry) { struct cpoint_map *map = (struct cpoint_map *)entry; if (!map->m_seen) report("Checkpoint map", "no object for mapping."); if (map->m_oid < APFS_OID_RESERVED_COUNT) report("Checkpoint map", "reserved object id."); if (map->m_oid >= sb->s_next_oid) report("Checkpoint map", "unassigned object id."); free(entry); } /** * free_cpoint_map_table - Free the checkpoint map table and all its entries * @table: table to free */ void free_cpoint_map_table(struct htable_entry **table) { free_htable(table, free_cpoint_map); } /** * get_cpoint_map - Find or create a map structure in the checkpoint map table * @oid: ephemeral object id * * Returns the checkpoint mapping structure, after creating it if necessary. */ struct cpoint_map *get_cpoint_map(u64 oid) { struct htable_entry *entry; entry = get_htable_entry(oid, sizeof(struct cpoint_map), sb->s_cpoint_map_table); return (struct cpoint_map *)entry; } /** * read_ephemeral_object - Read an ephemeral object header from disk * @oid: object id * @obj: object struct to receive the results * * Returns a pointer to the raw data of the object in memory, after checking * the consistency of some of its fields. */ void *read_ephemeral_object(u64 oid, struct object *obj) { struct apfs_obj_phys *raw; struct cpoint_map *map; u32 storage_type; void *block = NULL; int i, data_idx; assert(sb->s_cpoint_map_table); assert(sb->s_xid); map = get_cpoint_map(oid); if (!map->m_paddr) report("Ephemeral object", "missing checkpoint mapping."); if (map->m_seen) report("Checkpoint map", "an ephemeral object id was reused."); map->m_seen = true; raw = malloc(map->m_size); if (!raw) system_error(); data_idx = map->m_paddr - sb->s_data_base; for (i = 0; i < map->m_size >> sb->s_blocksize_bits; ++i) { block = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, (sb->s_data_base + data_idx) * sb->s_blocksize); if (block == MAP_FAILED) system_error(); memcpy((char *)raw + (i << sb->s_blocksize_bits), block, sb->s_blocksize); munmap(block, sb->s_blocksize); block = NULL; /* Ephemeral objects can wrap around */ if (++data_idx == sb->s_data_blocks) data_idx = 0; } if (!obj_verify_csum_with_size(raw, map->m_size)) report("Object header", "bad checksum in block 0x%llx.", (unsigned long long)map->m_paddr); obj->oid = le64_to_cpu(raw->o_oid); obj->xid = le64_to_cpu(raw->o_xid); obj->block_nr = map->m_paddr; obj->type = le32_to_cpu(raw->o_type) & APFS_OBJECT_TYPE_MASK; obj->flags = le32_to_cpu(raw->o_type) & APFS_OBJECT_TYPE_FLAGS_MASK; obj->subtype = le32_to_cpu(raw->o_subtype); obj->size = map->m_size; if ((obj->type | obj->flags) != map->m_type) report("Ephemeral object", "type field doesn't match mapping."); if (obj->subtype != map->m_subtype) report("Ephemeral object", "subtype doesn't match mapping."); if (obj->oid != oid) report("Ephemeral object", "wrong object id."); if (obj->xid != sb->s_xid) report("Ephemeral object", "not part of latest transaction."); storage_type = parse_object_flags(obj->flags, false); if (storage_type != APFS_OBJ_EPHEMERAL) report("Object header", "wrong flag for ephemeral object."); return raw; } apfsprogs-0.2.0/apfsck/object.h000066400000000000000000000023561471277137200164240ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _OBJECT_H #define _OBJECT_H #include #include "htable.h" struct apfs_obj_phys; struct super_block; struct node; /* * In-memory representation of an APFS object */ struct object { u64 block_nr; u64 oid; /* Often the same as the block number */ u64 xid; u32 type; u32 subtype; u32 flags; /* * Size of the object in bytes. It's usually a single block, the only * exception I've found so far are the spacemans of containers that are * too big to have all cib addresses in one block, but too small to fill * a whole cab with cib addresses. */ u32 size; }; extern int obj_verify_csum(struct apfs_obj_phys *obj); extern void *read_object_nocheck(u64 bno, u32 size, struct object *obj); extern u32 parse_object_flags(u32 flags, bool encrypted); extern void *read_object(u64 oid, struct htable_entry **omap_table, struct object *obj); extern void *read_object_noheader(u64 oid, struct htable_entry **omap_table, struct object *obj); extern void free_cpoint_map_table(struct htable_entry **table); extern struct cpoint_map *get_cpoint_map(u64 oid); extern void *read_ephemeral_object(u64 oid, struct object *obj); #endif /* _OBJECT_H */ apfsprogs-0.2.0/apfsck/snapshot.c000066400000000000000000000167601471277137200170140ustar00rootroot00000000000000/* * Copyright (C) 2022 Ernesto A. Fernández */ #include #include #include #include #include #include "apfsck.h" #include "htable.h" #include "key.h" #include "snapshot.h" #include "super.h" /** * free_snap - Free a snapshot structure after some final checks * @entry: the entry to free */ static void free_snap(struct htable_entry *entry) { struct snapshot *snap = (struct snapshot *)entry; if (!snap->sn_name_seen || !snap->sn_meta_seen) report("Snapshot", "missing metadata entry."); if (!snap->sn_omap_seen) report("Snapshot", "missing omap entry."); free(entry); } /** * free_snap_table - Free the snapshot hash table and all its entries * @table: table to free */ void free_snap_table(struct htable_entry **table) { free_htable(table, free_snap); } /** * get_snapshot - Find or create a snapshot structure in the snapshot hash table * @xid: transaction id for the snapshot * * Returns the snapshot structure, after creating it if necessary. */ struct snapshot *get_snapshot(u64 xid) { struct htable_entry *entry; entry = get_htable_entry(xid, sizeof(struct snapshot), vsb->v_snap_table); return (struct snapshot *)entry; } /** * parse_snap_name_record - Parse and check a snapshot name record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ static void parse_snap_name_record(struct apfs_snap_name_key *key, struct apfs_snap_name_val *val, int len) { struct snapshot *snap = NULL; if (len != sizeof(*val)) report("Snapshot name record", "wrong length for value."); snap = get_snapshot(le64_to_cpu(val->snap_xid)); if (snap->sn_name_seen) report("Snapshot tree", "snap with two name records."); snap->sn_name_seen = true; if (!snap->sn_meta_seen || !snap->sn_meta_name) report("Snapshot tree", "missing a metadata record."); if (strcmp((char *)key->name, snap->sn_meta_name) != 0) report("Snapshot tree", "inconsistent names for snapshot."); } /** * check_snapshot - Check a whole snapshot * @xid: transaction for the snapshot * @vol_bno: block number for the snapshot volume superblock * @extentref_bno: block number for the snapshot's extentref tree * @inum: inode number for the snapshot */ static void check_snapshot(u64 xid, u64 vol_bno, u64 extentref_bno, u64 inum) { struct volume_superblock *latest_vsb = vsb; u64 latest_xid = sb->s_xid; struct listed_cnid *cnid = NULL; vsb = NULL; sb->s_xid = xid; vsb = alloc_volume_super(true); /* The list of extref trees is shared by all transactions */ vsb->v_snap_extrefs = latest_vsb->v_snap_extrefs; vsb->v_snap_count = latest_vsb->v_snap_count; vsb->v_raw = read_object(vol_bno, NULL, &vsb->v_obj); read_volume_super(latest_vsb->v_index, vsb, &vsb->v_obj); /* * Each snapshot is given an "inode number" and, unlike dstream ids, * they don't seem to overlap with real inode numbers. This lines are * to check that the number is not reused inside the snapshot. */ cnid = get_listed_cnid(inum); cnid_set_state_flag(cnid, CNID_IN_INODE); if (vsb->v_extref_oid != 0) report("Snapshot volume superblock", "has extentref tree."); vsb->v_extref_oid = extentref_bno; if (vsb->v_omap_oid != 0) report("Snapshot volume superblock", "has object map."); vsb->v_omap = latest_vsb->v_omap; vsb->v_omap_table = latest_vsb->v_omap_table; omap_htable_clear_seen_for_snap(latest_vsb->v_omap_table); vsb->v_snap_max_xid = latest_vsb->v_snap_max_xid; vsb->v_extent_table = latest_vsb->v_extent_table; if (vsb->v_snap_meta_oid != 0) report("Snapshot volume superblock", "has snapshot tree."); check_volume_super(); /* Go back to the latest transaction */ sb->s_xid = latest_xid; latest_vsb->v_snap_extrefs = vsb->v_snap_extrefs; latest_vsb->v_block_count += vsb->v_block_count; vsb = latest_vsb; /* TODO: don't leak */ /* * Repeat this now to check that the number is not reused inside the * next snapshot either, or for the current transaction. */ cnid = get_listed_cnid(inum); cnid_set_state_flag(cnid, CNID_IN_INODE); } /** * parse_snap_metadata_record - Parse and check a snapshot metadata record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ static void parse_snap_metadata_record(struct apfs_snap_metadata_key *key, struct apfs_snap_metadata_val *val, int len) { struct snapshot *snap = NULL; u64 snap_xid; int namelen; if (len < sizeof(*val) + 1) report("Snapshot metadata record", "value is too small."); if (*((char *)val + len - 1) != 0) report("Snapshot metadata record", "name lacks NULL-termination."); namelen = le16_to_cpu(val->name_len); if (strlen((char *)val->name) + 1 != namelen) report("Snapshot metadata record", "wrong name length."); if (len != sizeof(*val) + namelen) report("Snapshot metadata record", "size of value doesn't match name length."); snap_xid = cat_cnid(&key->hdr); snap = get_snapshot(snap_xid); if (snap->sn_meta_seen) report("Snapshot tree", "snap with two metadata records."); snap->sn_meta_seen = true; snap->sn_meta_name = calloc(1, namelen); if (!snap->sn_meta_name) system_error(); strcpy(snap->sn_meta_name, (char *)val->name); if (le32_to_cpu(val->extentref_tree_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE)) report("Snapshot metadata", "wrong type for extentref tree."); if (val->flags) report_unknown("Snapshot flags"); if (!val->inum) report("Snapshot metadata", "no inode number."); check_snapshot(snap_xid, le64_to_cpu(val->sblock_oid), le64_to_cpu(val->extentref_tree_oid), le64_to_cpu(val->inum)); ++vsb->v_snap_count; } /** * parse_snap_record - Parse and check a snapshot tree record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_snap_record(void *key, void *val, int len) { switch (cat_type(key)) { case APFS_TYPE_SNAP_METADATA: return parse_snap_metadata_record(key, val, len); case APFS_TYPE_SNAP_NAME: return parse_snap_name_record(key, val, len); default: report(NULL, "Bug!"); } } /** * parse_omap_snap_record - Parse and check an omap snapshot tree record value * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_omap_snap_record(__le64 *key, struct apfs_omap_snapshot *val, int len) { struct snapshot *snapshot = NULL; u64 snap_xid; /* * These keys and values must be aligned to eight bytes. * TODO: add the same check to the free queue? */ if ((u64)key & 7 || (u64)val & 7) report("Omap snapshot record", "bad alignment for key or value."); if (len != sizeof(*val)) report("Omap snapshot record", "value is too small."); snap_xid = le64_to_cpu(*key); if (snap_xid == 0) report("Omap snapshot record", "xid is zero."); if (snap_xid > sb->s_xid) report("Omap snapshot record", "xid is in the future."); if (snap_xid >= vsb->v_snap_max_xid) vsb->v_snap_max_xid = snap_xid; snapshot = get_snapshot(snap_xid); snapshot->sn_omap_seen = true; if (val->oms_flags) report_unknown("Deleted or reverted snapshot"); if (val->oms_pad) report("Omap snapshot record", "padding should be zeroes."); if (val->oms_oid) report("Omap snapshot record", "oid should be zero."); } apfsprogs-0.2.0/apfsck/snapshot.h000066400000000000000000000015271471277137200170140ustar00rootroot00000000000000/* * Copyright (C) 2022 Ernesto A. Fernández */ #ifndef _SNAPSHOT_H #define _SNAPSHOT_H #include #include "htable.h" /* * Snapshot data in memory */ struct snapshot { struct htable_entry sn_htable; /* Hash table entry header */ bool sn_name_seen; /* Has the snap's name been seen? */ bool sn_meta_seen; /* Has the snap's metadata been seen? */ bool sn_omap_seen; /* Is the snap in the omap snap tree? */ char *sn_meta_name; /* Name reported by the metadata rec */ }; #define sn_xid sn_htable.h_id /* Transaction id */ extern void free_snap_table(struct htable_entry **table); extern struct snapshot *get_snapshot(u64 xid); extern void parse_snap_record(void *key, void *val, int len); extern void parse_omap_snap_record(__le64 *key, struct apfs_omap_snapshot *val, int len); #endif /* _SNAPSHOT_H */ apfsprogs-0.2.0/apfsck/spaceman.c000066400000000000000000001060241471277137200167350ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "key.h" #include "object.h" #include "spaceman.h" #include "super.h" /** * block_in_ip - Does this block belong to the internal pool? * @bno: block number to check */ static inline bool block_in_ip(u64 bno) { struct spaceman *sm = &sb->s_spaceman; u64 start = sm->sm_ip_base; u64 end = start + sm->sm_ip_block_count; return bno >= start && bno < end; } /** * range_in_ip - Is this range included in the internal pool? * @paddr: first block of the range * @length: length of the range */ static bool range_in_ip(u64 paddr, u64 length) { u64 last = paddr + length - 1; bool first_in_ip = block_in_ip(paddr); bool last_in_ip = block_in_ip(last); if ((first_in_ip && !last_in_ip) || (!first_in_ip && last_in_ip)) report("Free queue record", "internal pool is overrun."); return first_in_ip; } /** * bmap_mark_as_used - Set a range to ones in a bitmap * @bitmap: the bitmap * @paddr: first block number * @length: block count * * Checks that an address range is still zeroed in the given bitmap, and then * switches those bits. */ static void bmap_mark_as_used(u64 *bitmap, u64 paddr, u64 length) { u64 *byte; u64 flag; u64 i; for (i = paddr; i < paddr + length; ++i) { byte = bitmap + i / 64; flag = 1ULL << i % 64; if (*byte & flag) report(NULL /* context */, "A block is used twice."); *byte |= flag; } } /** * ip_bmap_mark_as_used - Mark a range as used in the ip allocation bitmap * @paddr: first block number * @length: block count * * Checks that the given address range is still marked as free in the internal * pool's allocation bitmap, and then switches those bits. */ void ip_bmap_mark_as_used(u64 paddr, u64 length) { if (!range_in_ip(paddr, length)) report(NULL /* context */, "Out-of-range ip block number."); paddr -= sb->s_spaceman.sm_ip_base; bmap_mark_as_used(sb->s_ip_bitmap, paddr, length); } /** * container_bmap_mark_as_used - Mark a range as used in the allocation bitmap * @paddr: first block number * @length: block count * * Checks that the given address range is still marked as free in the * container's allocation bitmap, and then switches those bits. */ void container_bmap_mark_as_used(u64 paddr, u64 length) { u64 tier2_blkno, max_dev_blkcnt; bool tier2; void *bitmap = NULL; tier2_blkno = APFS_FUSION_TIER2_DEVICE_BYTE_ADDR >> sb->s_blocksize_bits; tier2 = paddr >= tier2_blkno; if (tier2) paddr -= tier2_blkno; max_dev_blkcnt = tier2 ? sb->s_max_tier2_blkcnt : sb->s_max_main_blkcnt; bitmap = tier2 ? sb->s_tier2_bitmap : sb->s_main_bitmap; /* Avoid out-of-bounds writes to the allocation bitmap */ if (paddr + length > max_dev_blkcnt || paddr + length < paddr) report(NULL /* context */, "Out-of-range block number."); bmap_mark_as_used(bitmap, paddr, length); } /** * parse_spaceman_chunk_counts - Parse spaceman fields for chunk-related counts * @raw: pointer to the raw spaceman structure * * Checks the counts of blocks per chunk, chunks per cib, and cibs per cab, and * reads them into the in-memory container superblock. */ static void parse_spaceman_chunk_counts(struct apfs_spaceman_phys *raw) { struct spaceman *sm = &sb->s_spaceman; int chunk_info_size = sizeof(struct apfs_chunk_info); int cib_size = sizeof(struct apfs_chunk_info_block); int cab_size = sizeof(struct apfs_cib_addr_block); sm->sm_blocks_per_chunk = le32_to_cpu(raw->sm_blocks_per_chunk); if (sm->sm_blocks_per_chunk != 8 * sb->s_blocksize) /* One bitmap block for each chunk */ report("Space manager", "wrong count of blocks per chunk."); sm->sm_chunks_per_cib = (sb->s_blocksize - cib_size) / chunk_info_size; if (le32_to_cpu(raw->sm_chunks_per_cib) != sm->sm_chunks_per_cib) report("Space manager", "wrong count of chunks per cib."); sm->sm_cibs_per_cab = (sb->s_blocksize - cab_size) / sizeof(__le64); if (le32_to_cpu(raw->sm_cibs_per_cab) != sm->sm_cibs_per_cab) report("Space manager", "wrong count of cibs per cab."); } /** * read_chunk_bitmap - Read a chunk's bitmap into memory * @addr: first block number for the chunk * @bmap: block number for the chunk's bitmap, or zero if the chunk is all free * @dev: device that owns this chunk * * Returns a pointer to the chunk's bitmap, read into its proper position * within the in-memory bitmap for the container. */ static void *read_chunk_bitmap(u64 addr, u64 bmap, struct spaceman_device *dev) { struct spaceman *sm = &sb->s_spaceman; ssize_t read_bytes; void *buf, *ret; size_t count; off_t offset; u32 chunk_number; assert(dev->sm_bitmap); /* Prevent out-of-bounds writes to sm->sm_bitmap */ if (addr & (sm->sm_blocks_per_chunk - 1)) report("Chunk-info", "chunk address isn't multiple of size."); chunk_number = addr / sm->sm_blocks_per_chunk; if (addr >= sb->s_block_count) report("Chunk-info", "chunk address is out of bounds."); ret = buf = dev->sm_bitmap + chunk_number * sb->s_blocksize; if (!bmap) /* The whole chunk is free, so leave this block as zero */ return ret; count = sb->s_blocksize; offset = bmap * sb->s_blocksize; do { read_bytes = apfs_pread(buf, count, offset); if (read_bytes < 0) system_error(); buf += read_bytes; count -= read_bytes; offset += read_bytes; } while (read_bytes > 0); /* Mark the bitmap block as used in the actual allocation bitmap */ ip_bmap_mark_as_used(bmap, 1 /* length */); return ret; } /** * count_chunk_free - Count the free blocks in a chunk * @bmap: pointer to the chunk's bitmap * @blks: number of blocks in the chunk */ static int count_chunk_free(void *bmap, u32 blks) { unsigned long long *curr, *end; int free = blks; end = bmap + sb->s_blocksize; for (curr = bmap; curr < end; ++curr) free -= __builtin_popcountll(*curr); return free; } /** * parse_chunk_info - Parse and check a chunk info structure * @chunk: pointer to the raw chunk info structure * @is_last: is this the last chunk of the device? * @start: expected first block number for the chunk * @xid: on return, the transaction id of the chunk * @dev: device that owns this chunk * * Returns the first block number for the next chunk. */ static u64 parse_chunk_info(struct apfs_chunk_info *chunk, bool is_last, u64 start, u64 *xid, struct spaceman_device *dev) { struct spaceman *sm = &sb->s_spaceman; u32 block_count; void *bitmap; u32 free_count; block_count = le32_to_cpu(chunk->ci_block_count); if (!block_count) report("Chunk-info", "has no blocks."); if (block_count > sm->sm_blocks_per_chunk) report("Chunk-info", "too many blocks."); if (!is_last && block_count != sm->sm_blocks_per_chunk) report("Chunk-info", "too few blocks."); dev->sm_blocks += block_count; if (le64_to_cpu(chunk->ci_addr) != start) report("Chunk-info block", "chunks are not consecutive."); bitmap = read_chunk_bitmap(start, le64_to_cpu(chunk->ci_bitmap_addr), dev); free_count = le32_to_cpu(chunk->ci_free_count); if (free_count != count_chunk_free(bitmap, block_count)) report("Chunk-info", "wrong count of free blocks."); dev->sm_free += free_count; *xid = le64_to_cpu(chunk->ci_xid); if (!*xid) report("Chunk-info", "bad transaction id."); return start + block_count; } /** * parse_chunk_info_block - Parse and check a chunk-info block * @bno: block number of the chunk-info block * @index: index of the chunk-info block * @start: expected first block number for the first chunk * @xid_p: on return, the transaction id of the cib (ignored if NULL) * @dev: device that owns this cib * * Returns the first block number for the first chunk of the next cib. */ static u64 parse_chunk_info_block(u64 bno, u32 index, u64 start, u64 *xid_p, struct spaceman_device *dev) { struct spaceman *sm = &sb->s_spaceman; struct object obj; struct apfs_chunk_info_block *cib; u32 chunk_count; bool last_cib = index == dev->sm_cib_count - 1; u64 max_chunk_xid = 0; int i; cib = read_object(bno, NULL, &obj); if (obj.type != APFS_OBJECT_TYPE_SPACEMAN_CIB) report("Chunk-info block", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Chunk-info block", "wrong object subtype."); if (obj.xid > sm->sm_xid) /* Cib address is stored in the spaceman */ report("Chunk-info block", "xid is more recent than spaceman."); if (le32_to_cpu(cib->cib_index) != index) report("Chunk-info block", "wrong index."); chunk_count = le32_to_cpu(cib->cib_chunk_info_count); if (!chunk_count) report("Chunk-info block", "has no chunks."); if (chunk_count > sm->sm_chunks_per_cib) report("Chunk-info block", "too many chunks."); if (!last_cib && chunk_count != sm->sm_chunks_per_cib) report("Chunk-info block", "too few chunks."); dev->sm_chunks += chunk_count; for (i = 0; i < chunk_count; ++i) { bool last_block = false; u64 chunk_xid; if (last_cib && i == chunk_count - 1) last_block = true; start = parse_chunk_info(&cib->cib_chunk_info[i], last_block, start, &chunk_xid, dev); if (chunk_xid > obj.xid) report("Chunk-info", "xid is too recent."); if (chunk_xid > max_chunk_xid) max_chunk_xid = chunk_xid; } if (obj.xid != max_chunk_xid) /* Cib only changes if a chunk changes */ report("Chunk-info block", "xid is too recent."); if (xid_p) *xid_p = obj.xid; munmap(cib, obj.size); return start; } /** * parse_cib_addr_block - Parse and check a chunk-info address block * @bno: block number of the chunk-info address block * @index: index of the chunk-info address block * @start: expected first block number for the first chunk * @dev: device that owns this cab * * Returns the first block number for the first chunk of the next cab. */ static u64 parse_cib_addr_block(u64 bno, u32 index, u64 start, struct spaceman_device *dev) { struct spaceman *sm = &sb->s_spaceman; struct object obj; struct apfs_cib_addr_block *cab = NULL; u32 cib_count; bool last_cab = index == dev->sm_cab_count - 1; u64 max_cib_xid = 0; int i; cab = read_object(bno, NULL, &obj); if (obj.type != APFS_OBJECT_TYPE_SPACEMAN_CAB) report("Cib address block", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Cib address block", "wrong object subtype."); if (obj.xid > sm->sm_xid) /* Cab address is stored in the spaceman */ report("Cib address block", "xid is more recent than spaceman."); if (le32_to_cpu(cab->cab_index) != index) report("Cib address block", "wrong index."); cib_count = le32_to_cpu(cab->cab_cib_count); if (!cib_count) report("Cib address block", "has no cibs."); if (cib_count > sm->sm_cibs_per_cab) report("Cib address block", "too many cibs."); if (!last_cab && cib_count != sm->sm_cibs_per_cab) report("Cib address block", "too few cibs."); dev->sm_cibs += cib_count; for (i = 0; i < cib_count; ++i) { u64 cib_xid; start = parse_chunk_info_block(le64_to_cpu(cab->cab_cib_addr[i]), sm->sm_cibs_per_cab * index + i, start, &cib_xid, dev); if (cib_xid > obj.xid) report("Chunk-info block", "xid is too recent."); if (cib_xid > max_cib_xid) max_cib_xid = cib_xid; } if (obj.xid != max_cib_xid) /* Cab only changes if a cib changes */ report("Cib address block", "xid is too recent."); munmap(cab, obj.size); return start; } /** * spaceman_val_from_off - Get the value stored on a given spaceman offset * @raw: pointer to the raw space manager * @offset: offset of the value in @raw * * This is not in the official documentation and I didn't figure it out myself. * Credit should go to Joachim Metz: . * * TODO: check that no values found by this function overlap with each other. */ static u64 spaceman_val_from_off(struct apfs_spaceman_phys *raw, u32 offset) { struct spaceman *sm = &sb->s_spaceman; char *value_p = (char *)raw + offset; assert(sm->sm_struct_size); if (offset & 0x7) report("Spaceman", "offset is not aligned to 8 bytes."); if (offset < sm->sm_struct_size) report("Spaceman", "offset overlaps with structure."); if (offset >= sm->sm_obj_size || offset + sizeof(u64) > sm->sm_obj_size) report("Spaceman", "offset is out of bounds."); return *((u64 *)value_p); } /** * spaceman_16_from_off - Get the 16 bits stored on a given spaceman offset * @raw: pointer to the raw space manager * @offset: offset of the value in @raw * * TODO: check that no values found by this function overlap with each other. */ static u16 spaceman_16_from_off(struct apfs_spaceman_phys *raw, u32 offset) { struct spaceman *sm = &sb->s_spaceman; char *value_p = (char *)raw + offset; assert(sm->sm_struct_size); if (offset & 0x1) report("Spaceman", "offset is not aligned to 2 bytes."); if (offset < sm->sm_struct_size) report("Spaceman", "offset overlaps with structure."); if (offset >= sm->sm_obj_size || offset + sizeof(u16) > sm->sm_obj_size) report("Spaceman", "offset is out of bounds."); return *((u16 *)value_p); } /** * spaceman_16_array_from_off - Get a pointer to the array on a spaceman offset * @raw: pointer to the raw space manager * @offset: offset of the array of 16-bit entries in @raw * @len: number of entries in the array * * TODO: check that no values found by this function overlap with each other, * and also with spaceman_val_from_off()/spaceman_16_from_off(). */ static __le16 *spaceman_16_array_from_off(struct apfs_spaceman_phys *raw, u32 offset, u32 len) { struct spaceman *sm = &sb->s_spaceman; __le16 *array_p = (void *)raw + offset; assert(sm->sm_struct_size); if (offset & 0x1) report("Spaceman", "offset is not aligned to 2 bytes."); if (offset < sm->sm_struct_size) report("Spaceman", "offset overlaps with structure."); if (len > UINT32_MAX / 10 || offset > UINT32_MAX / 10) report("Spaceman", "length or offset of array are not sane."); if (offset >= sm->sm_obj_size || offset + sizeof(__le16) * len > sm->sm_obj_size) report("Spaceman", "offset is out of bounds."); return array_p; } /** * parse_spaceman_device - Parse and check a spaceman's device struct * @raw: pointer to the raw space manager * @which: device to check */ static void parse_spaceman_device(struct apfs_spaceman_phys *raw, enum smdev which) { struct spaceman *sm = &sb->s_spaceman; struct spaceman_device *dev = &sm->sm_dev[which]; struct apfs_spaceman_device *rawdev = &raw->sm_dev[which]; u64 max_blkcnt; u32 cab_count; u32 addr_off; u64 start = 0; int i; dev->sm_cab_count = le32_to_cpu(rawdev->sm_cab_count); dev->sm_cib_count = le32_to_cpu(rawdev->sm_cib_count); dev->sm_chunk_count = le64_to_cpu(rawdev->sm_chunk_count); dev->sm_block_count = le64_to_cpu(rawdev->sm_block_count); dev->sm_free_count = le64_to_cpu(rawdev->sm_free_count); sm->sm_total_chunk_count += dev->sm_chunk_count; sm->sm_total_cib_count += dev->sm_cib_count; sm->sm_total_cab_count += dev->sm_cab_count; sm->sm_total_free_count += dev->sm_free_count; max_blkcnt = which == APFS_SD_MAIN ? sb->s_max_main_blkcnt : sb->s_max_tier2_blkcnt; if (dev->sm_block_count > max_blkcnt) report("Spaceman device", "block count too big for device."); if (dev->sm_chunk_count != DIV_ROUND_UP(dev->sm_block_count, sm->sm_blocks_per_chunk)) report("Spaceman device", "wrong count of chunks."); if (dev->sm_cib_count != DIV_ROUND_UP(dev->sm_chunk_count, sm->sm_chunks_per_cib)) report("Spaceman device", "wrong count of chunk-info blocks."); cab_count = DIV_ROUND_UP(dev->sm_cib_count, sm->sm_cibs_per_cab); /* CABs are not used unless at least one can be filled */ if (cab_count == 1) cab_count = 0; if (dev->sm_cab_count != cab_count) report("Spaceman device", "wrong count of chunk-info address blocks."); /* All bitmaps will need to be read into memory */ dev->sm_bitmap = calloc(dev->sm_chunk_count, sb->s_blocksize); if (!dev->sm_bitmap) system_error(); /* * TODO: is it mandatory that sm_addr_off for the tier2 device comes * right after the end of the same array for the main device? */ addr_off = le32_to_cpu(rawdev->sm_addr_offset); if (!dev->sm_cab_count) { /* If CABs are not used, the spaceman just lists the CIBs */ for (i = 0; i < dev->sm_cib_count; ++i) { u64 bno = spaceman_val_from_off(raw, addr_off + i * sizeof(u64)); start = parse_chunk_info_block(bno, i, start, NULL /* xid_p */, dev); } } else { for (i = 0; i < dev->sm_cab_count; ++i) { u64 bno = spaceman_val_from_off(raw, addr_off + i * sizeof(u64)); start = parse_cib_addr_block(bno, i, start, dev); } if (dev->sm_cib_count != dev->sm_cibs) report("Spaceman device", "bad total number of cibs."); } if (dev->sm_chunk_count != dev->sm_chunks) report("Spaceman device", "bad total number of chunks."); if (dev->sm_block_count != dev->sm_blocks) report("Spaceman device", "bad total number of blocks."); if (dev->sm_free_count != dev->sm_free) report("Spaceman device", "bad total number of free blocks."); if (rawdev->sm_reserved || rawdev->sm_reserved2) report("Spaceman device", "non-zero padding."); } struct alloc_zone { struct alloc_zone *next; /* Next entry in linked list */ u16 id; /* Zone id */ u64 start; /* Start of zone */ u64 end; /* End of zone */ }; static struct alloc_zone *alloc_zone_list = NULL; static void check_alloc_zone_sanity(u64 start, u64 end) { if (start & (sb->s_blocksize - 1)) report("Allocation zone", "start isn't multiple of block size."); if (end & (sb->s_blocksize - 1)) report("Allocation zone", "end isn't multiple of block size."); if (start >= end) report("Allocation zone", "invalid range."); } /* Puts alloc zones in a list to check for overlap */ static void check_new_alloc_zone(u16 id, u64 start, u64 end) { struct alloc_zone **zone_p = NULL; struct alloc_zone *zone = NULL; struct alloc_zone *new = NULL; check_alloc_zone_sanity(start, end); zone_p = &alloc_zone_list; zone = *zone_p; while (zone) { if (zone->id == id) report("Allocation zones", "repeated id."); if (start < zone->end && end > zone->start) report("Allocations zones", "overlapping ranges."); zone_p = &zone->next; zone = *zone_p; } new = calloc(1, sizeof(*new)); if (!new) system_error(); new->id = id; new->start = start; new->end = end; *zone_p = new; } static void free_checked_alloc_zones(void) { struct alloc_zone *curr = alloc_zone_list; alloc_zone_list = NULL; while (curr) { struct alloc_zone *next = NULL; next = curr->next; curr->next = NULL; free(curr); curr = next; } } /* If old zones are reported, just check that the index is valid */ static void check_prev_alloc_zones(struct apfs_spaceman_allocation_zone_info_phys *az) { struct apfs_spaceman_allocation_zone_boundaries *azb = NULL; u16 prev_index; int j; prev_index = le16_to_cpu(az->saz_previous_boundary_index); if (prev_index > APFS_SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES) report("Allocation zones", "out-of-range previous index."); for (j = 0; j < APFS_SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES; ++j) { azb = &az->saz_previous_boundaries[j]; if (prev_index == 0) { /* No previous zones should be reported */ if (azb->saz_zone_start || azb->saz_zone_end) report("Previous allocation zones", "missing index."); continue; } if (!azb->saz_zone_start && !azb->saz_zone_end) { /* No zone reported in this slot */ if (j == prev_index - 1 && !azb->saz_zone_start) report("Allocation zones", "latest is missing."); continue; } check_alloc_zone_sanity(le64_to_cpu(azb->saz_zone_start), le64_to_cpu(azb->saz_zone_end)); } } /** * check_spaceman_datazone - Check the spaceman allocation zones * @dz: pointer to the raw datazone structure * * Allocation zones are undocumented, so we can't do much more than report them * as unsupported if they are in use. */ static void check_spaceman_datazone(struct apfs_spaceman_datazone_info_phys *dz) { int i, dev; for (dev = 0; dev < APFS_SD_COUNT; ++dev) { for (i = 0; i < APFS_SM_DATAZONE_ALLOCZONE_COUNT; ++i) { struct apfs_spaceman_allocation_zone_info_phys *az = NULL; struct apfs_spaceman_allocation_zone_boundaries *azb = NULL; az = &dz->sdz_allocation_zones[dev][i]; azb = &az->saz_current_boundaries; if (az->saz_zone_id) { if (dev != APFS_SD_MAIN) report_unknown("Fusion drive"); check_new_alloc_zone(le16_to_cpu(az->saz_zone_id), le64_to_cpu(azb->saz_zone_start), le64_to_cpu(azb->saz_zone_end)); } else if (azb->saz_zone_start || azb->saz_zone_end) { report("Allocation zone", "has no id."); } if (az->saz_reserved) report("Datazone", "reserved field in use."); check_prev_alloc_zones(az); } free_checked_alloc_zones(); } } /** * check_spaceman_tier2_fq_is_null - Check that a tier 2 free queue is unset * @sfq_raw: raw free queue to check */ static void check_spaceman_free_queue_is_null(struct apfs_spaceman_free_queue *sfq_raw) { if (sfq_raw->sfq_count || sfq_raw->sfq_tree_oid || sfq_raw->sfq_oldest_xid || sfq_raw->sfq_tree_node_limit) report("Tier 2 free queue", "should not be set."); } /** * check_spaceman_free_queue_dev - Check the free queue for a given device * @sfq_raw: raw free queue to check * @which: device for the queue */ static void check_spaceman_free_queue_dev(struct apfs_spaceman_free_queue *sfq_raw, enum smdev which) { struct spaceman *sm = &sb->s_spaceman; struct spaceman_device *dev = NULL; struct free_queue *fq = NULL; int idx; if (which == APFS_SD_TIER2 && !apfs_is_fusion_drive()) return check_spaceman_free_queue_is_null(sfq_raw); dev = &sm->sm_dev[which]; idx = which == APFS_SD_MAIN ? APFS_SFQ_MAIN : APFS_SFQ_TIER2; dev->sm_dev_fq = parse_free_queue_btree(le64_to_cpu(sfq_raw->sfq_tree_oid), idx); fq = dev->sm_dev_fq; if (le64_to_cpu(sfq_raw->sfq_count) != fq->sfq_count) report("Spaceman free queue", "wrong block count."); if (le64_to_cpu(sfq_raw->sfq_oldest_xid) != fq->sfq_oldest_xid) report("Spaceman free queue", "oldest xid is wrong."); if (le16_to_cpu(sfq_raw->sfq_tree_node_limit) < fq->sfq_btree.node_count) report("Spaceman free queue", "node count above limit."); if (le16_to_cpu(sfq_raw->sfq_tree_node_limit) != main_fq_node_limit(dev->sm_blocks)) report("Spaceman free queue", "wrong node limit."); } /** * check_spaceman_free_queues - Check the spaceman free queues * @sfq: pointer to the raw free queue array */ static void check_spaceman_free_queues(struct apfs_spaceman_free_queue *sfq) { struct spaceman *sm = &sb->s_spaceman; int i; for (i = 0; i < APFS_SFQ_COUNT; ++i) { if (sfq[i].sfq_pad16 || sfq[i].sfq_pad32) report("Spaceman free queue", "non-zero padding."); if (sfq[i].sfq_reserved) report("Spaceman free queue", "reserved field in use."); } sm->sm_ip_fq = parse_free_queue_btree( le64_to_cpu(sfq[APFS_SFQ_IP].sfq_tree_oid), APFS_SFQ_IP); if (le64_to_cpu(sfq[APFS_SFQ_IP].sfq_count) != sm->sm_ip_fq->sfq_count) report("Spaceman free queue", "wrong block count."); if (le64_to_cpu(sfq[APFS_SFQ_IP].sfq_oldest_xid) != sm->sm_ip_fq->sfq_oldest_xid) report("Spaceman free queue", "oldest xid is wrong."); if (le16_to_cpu(sfq[APFS_SFQ_IP].sfq_tree_node_limit) < sm->sm_ip_fq->sfq_btree.node_count) report("Spaceman free queue", "node count above limit."); if (le16_to_cpu(sfq[APFS_SFQ_IP].sfq_tree_node_limit) != ip_fq_node_limit(sm->sm_total_chunk_count)) report("Spaceman free queue", "wrong node limit."); check_spaceman_free_queue_dev(&sfq[APFS_SFQ_MAIN], APFS_SD_MAIN); check_spaceman_free_queue_dev(&sfq[APFS_SFQ_TIER2], APFS_SD_TIER2); } /** * compare_container_bitmaps_dev - Verify a device's allocation bitmap * @which: device to check */ static void compare_container_bitmaps_dev(enum smdev which) { struct spaceman *sm = &sb->s_spaceman; struct spaceman_device *dev = NULL; u64 *reported_bmap = NULL, *real_bmap = NULL; unsigned long long bmap_size; dev = &sm->sm_dev[which]; if (dev->sm_chunk_count == 0) /* No tier 2 device */ return; reported_bmap = dev->sm_bitmap; real_bmap = which == APFS_SD_MAIN ? sb->s_main_bitmap : sb->s_tier2_bitmap; bmap_size = sb->s_blocksize * dev->sm_chunk_count; if (memcmp(reported_bmap, real_bmap, bmap_size) != 0) report("Space manager", "bad allocation bitmap."); } /** * check_ip_free_next - Check the free_next field for the internal pool * @free_next: free_next list to check * @free_head: first block in the linked list of free blocks * @free_tail: last block in the linked list of free blocks * @bmap_count: total number of ip bitmaps * * Returns the number of used entries. */ static int check_ip_free_next(__le16 *free_next, u16 free_head, u16 free_tail, u32 bmap_count) { u16 curr, next; int free_count = 0; /* * Entries for free ip bitmap blocks are a linked list, where each one * gives the index of the next one. The rest of the blocks are used; * they aren't part of the list, so their entries are set to the * invalid index 0xFFFF. */ curr = free_tail; next = free_head; do { if (curr >= bmap_count || next >= bmap_count) report("Internal pool", "free bitmaps are out-of-bounds."); if (++free_count >= bmap_count) report("Internal pool", "free bitmap list loops."); curr = next; next = le16_to_cpu(free_next[curr]); } while (curr != free_tail); /* * The tail itself is also set to the invalid index because the linked * list ends there. Its block is free, though. */ if (le16_to_cpu(free_next[free_tail]) != APFS_SPACEMAN_IP_BM_INDEX_INVALID) report("Free ip bitmaps list", "free tail is not used."); return bmap_count - free_count; } /** * read_ip_bitmap_block - Read a single internal pool bitmap block into memory * @bmap_base: first block of the bitmap ring * @bmap_len: length of the bitmap ring * @bmap_off: offset of the block to read in the ring * @bmap: on return, the whole ip bitmap */ static void read_ip_bitmap_block(u64 bmap_base, u32 bmap_len, u16 bmap_off, char *bmap) { char *curr_blk = NULL; u64 bno = bmap_base + bmap_off % bmap_len; curr_blk = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, bno * sb->s_blocksize); if (curr_blk == MAP_FAILED) system_error(); memcpy(bmap, curr_blk, sb->s_blocksize); munmap(curr_blk, sb->s_blocksize); curr_blk = NULL; } /** * parse_ip_bitmap_list - Check consistency of the internal pool bitmap list * @raw: pointer to the raw space manager * @bmap: on return, set the whole ip bitmap here */ static void parse_ip_bitmap_list(struct apfs_spaceman_phys *raw, char *bmap) { u64 bmap_base = le64_to_cpu(raw->sm_ip_bm_base); u16 bmap_off; u32 bmap_length = le32_to_cpu(raw->sm_ip_bm_block_count); u32 bm_size_in_blocks = le32_to_cpu(raw->sm_ip_bm_size_in_blocks); u16 free_head, free_tail, used_count; __le16 *free_next = NULL; u16 *used_bitmaps = NULL; u32 i, j; /* * The bitmap area is a ring structure that keeps both the currently * valid ip bitmaps and some older versions. I don't know the reason * for this. */ if (bmap_length > APFS_SPACEMAN_IP_BM_BLOCK_COUNT_MAX) report("Internal pool", "bitmap list is too long."); if (bmap_length != 16 * bm_size_in_blocks) report("Space manager", "ip doesn't have 16 bitmap copies."); free_head = le16_to_cpu(raw->sm_ip_bm_free_head); free_tail = le16_to_cpu(raw->sm_ip_bm_free_tail); free_next = spaceman_16_array_from_off(raw, le32_to_cpu(raw->sm_ip_bm_free_next_offset), bmap_length); used_count = check_ip_free_next((__le16 *)free_next, free_head, free_tail, bmap_length); if (used_count != bm_size_in_blocks) report("Internal pool", "incorrect count of used blocks."); container_bmap_mark_as_used(bmap_base, bmap_length); used_bitmaps = calloc(used_count + 1, sizeof(*used_bitmaps)); if (!used_bitmaps) system_error(); used_bitmaps[0] = free_tail; for (i = 0; i < bm_size_in_blocks; ++i) { bmap_off = spaceman_16_from_off(raw, le32_to_cpu(raw->sm_ip_bitmap_offset) + i * sizeof(bmap_off)); if (bmap_off >= bmap_length) report("Internal pool", "bitmap block is out-of-bounds."); if (le16_to_cpu(free_next[bmap_off]) != APFS_SPACEMAN_IP_BM_INDEX_INVALID) report("Internal pool", "used bitmap marked as free."); read_ip_bitmap_block(bmap_base, bmap_length, bmap_off, bmap + i * sb->s_blocksize); /* * Make sure that the same bitmap blocks aren't being reused * for different ip chunks. Remember that the first used bitmap * is the one for free_tail, set outside this loop. */ for (j = 0; j < i + 1; ++j) { if (used_bitmaps[j] == bmap_off) report("Internal pool", "same bitmap used twice."); } used_bitmaps[i + 1] = bmap_off; } free(used_bitmaps); used_bitmaps = NULL; } /** * check_ip_bitmap_blocks - Check that the bitmap blocks are properly zeroed * @raw: pointer to the raw space manager * * For most internal pool bitmap blocks this is the only check needed; the * current one also needs to be compared against the actual allocation bitmap. */ static void check_ip_bitmap_blocks(struct apfs_spaceman_phys *raw) { u64 bmap_base = le64_to_cpu(raw->sm_ip_bm_base); u32 bmap_length = le32_to_cpu(raw->sm_ip_bm_block_count); u64 pool_blocks = le64_to_cpu(raw->sm_ip_block_count); int i; /* * These zeroed-tail checks don't really make sense with multiblock ip * bitmaps, because we can't know for sure which ones were tails and * which ones were used in full. */ if (le32_to_cpu(raw->sm_ip_bm_size_in_blocks) != 1) return; for (i = 0; i < bmap_length; ++i) { char *bmap; int edge, j; bmap = apfs_mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, (bmap_base + i) * sb->s_blocksize); if (bmap == MAP_FAILED) system_error(); /* * The edge is the last byte inside the allocation bitmap; * everything that comes afterwards must be zeroed. */ edge = pool_blocks / 8; for (j = pool_blocks % 8; j < 8; ++j) { u8 flag = 1 << j; if (bmap[edge] & flag) report("Internal pool", "non-zeroed bitmap."); } for (j = edge + 1; j < sb->s_blocksize; ++j) { if (bmap[j]) report("Internal pool", "non-zeroed bitmap."); } munmap(bmap, sb->s_blocksize); } } /** * check_internal_pool - Check the internal pool of blocks * @raw: pointer to the raw space manager */ static void check_internal_pool(struct apfs_spaceman_phys *raw) { struct spaceman *sm = &sb->s_spaceman; char *pool_bmap; u64 pool_base = le64_to_cpu(raw->sm_ip_base); u64 pool_blocks = le64_to_cpu(raw->sm_ip_block_count); u64 ip_chunk_count = le32_to_cpu(raw->sm_ip_bm_size_in_blocks); u64 i; if ((sm->sm_total_chunk_count + sm->sm_total_cib_count + sm->sm_total_cab_count) * 3 != pool_blocks) report("Space manager", "wrong size of internal pool."); pool_bmap = calloc(ip_chunk_count, sb->s_blocksize); if (!pool_bmap) system_error(); parse_ip_bitmap_list(raw, pool_bmap); if (memcmp(pool_bmap, sb->s_ip_bitmap, ip_chunk_count * sb->s_blocksize)) report("Space manager", "bad ip allocation bitmap."); container_bmap_mark_as_used(pool_base, pool_blocks); free(pool_bmap); pool_bmap = NULL; if (le32_to_cpu(raw->sm_ip_bm_tx_multiplier) != APFS_SPACEMAN_IP_BM_TX_MULTIPLIER) report("Space manager", "bad tx multiplier for internal pool."); for (i = 0; i < ip_chunk_count; ++i) { u64 xid; xid = spaceman_val_from_off(raw, le32_to_cpu(raw->sm_ip_bm_xid_offset) + i * sizeof(xid)); if (xid > sb->s_xid) report("Internal pool", "bad transaction id."); } check_ip_bitmap_blocks(raw); } /** * check_spaceman - Check the space manager structures for a container * @oid: ephemeral object id for the spaceman structure */ void check_spaceman(u64 oid) { struct spaceman *sm = &sb->s_spaceman; struct object obj; struct apfs_spaceman_phys *raw; u64 ip_chunk_count; u32 flags; raw = read_ephemeral_object(oid, &obj); if (obj.type != APFS_OBJECT_TYPE_SPACEMAN) report("Space manager", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Space manager", "wrong object subtype."); sm->sm_xid = obj.xid; sm->sm_obj_size = obj.size; sm->sm_ip_base = le64_to_cpu(raw->sm_ip_base); sm->sm_ip_block_count = le64_to_cpu(raw->sm_ip_block_count); ip_chunk_count = DIV_ROUND_UP(sm->sm_ip_block_count, 8 * sb->s_blocksize); if (ip_chunk_count != le32_to_cpu(raw->sm_ip_bm_size_in_blocks)) report("Space manager", "bad ip bm size."); sb->s_ip_bitmap = calloc(ip_chunk_count, sb->s_blocksize); if (!sb->s_ip_bitmap) system_error(); flags = le32_to_cpu(raw->sm_flags); if ((flags & APFS_SM_FLAGS_VALID_MASK) != flags) report("Space manager", "invalid flag in use."); if (flags & APFS_SM_FLAG_VERSIONED) { sm->sm_struct_size = le32_to_cpu(raw->sm_struct_size); if (sm->sm_struct_size != sizeof(*raw)) report("Space manager", "wrong reported struct size."); check_spaceman_datazone(&raw->sm_datazone); } else { /* Some fields are missing in the non-versioned structure */ sm->sm_struct_size = sizeof(*raw) - sizeof(raw->sm_datazone) - sizeof(raw->sm_struct_size) - sizeof(raw->sm_version); } if (le32_to_cpu(raw->sm_block_size) != sb->s_blocksize) report("Space manager", "wrong block size."); parse_spaceman_chunk_counts(raw); parse_spaceman_device(raw, APFS_SD_MAIN); parse_spaceman_device(raw, APFS_SD_TIER2); if (sb->s_block_count != sm->sm_dev[APFS_SD_MAIN].sm_block_count + sm->sm_dev[APFS_SD_TIER2].sm_block_count) report("Spaceman devices", "wrong block count."); check_spaceman_free_queues(raw->sm_fq); check_internal_pool(raw); free(sb->s_ip_bitmap); if (le64_to_cpu(raw->sm_fs_reserve_block_count) != sm->sm_reserve_block_num) report("Space manager", "wrong block reservation total."); if (le64_to_cpu(raw->sm_fs_reserve_alloc_count) != sm->sm_reserve_alloc_num) report("Space manager", "wrong reserve block allocation total."); if (sm->sm_reserve_block_num - sm->sm_reserve_alloc_num > sm->sm_total_free_count) report("Space manager", "block reservation not respected."); compare_container_bitmaps_dev(APFS_SD_MAIN); compare_container_bitmaps_dev(APFS_SD_TIER2); free(raw); } /** * parse_free_queue_record - Parse a free queue record and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * @btree: the free queue btree structure * * Internal consistency of @key must be checked before calling this function. */ void parse_free_queue_record(struct apfs_spaceman_free_queue_key *key, void *val, int len, struct btree *btree) { struct free_queue *sfq = (struct free_queue *)btree; u64 paddr, length, xid; bool inside_ip; if (!len) { length = 1; /* Ghost records are for one block long extents */ } else if (len == 8) { __le64 *val64 = (__le64 *)val; length = le64_to_cpu(*val64); if (!length) report("Free queue record", "length is zero."); if (length == 1) report("Free queue record", "value is unnecessary."); } else { report("Free queue record", "wrong size of value."); } sfq->sfq_count += length; paddr = le64_to_cpu(key->sfqk_paddr); inside_ip = range_in_ip(paddr, length); if (sfq->sfq_index == APFS_SFQ_IP && !inside_ip) report("Free queue record", "range should be inside the IP."); if (sfq->sfq_index != APFS_SFQ_IP && inside_ip) report("Free queue record", "range should be outside the IP."); xid = le64_to_cpu(key->sfqk_xid); if (xid > sb->s_xid) report("Free queue record", "bad transaction id."); if (!sfq->sfq_oldest_xid || xid < sfq->sfq_oldest_xid) sfq->sfq_oldest_xid = xid; /* * These blocks are free, but still not marked as such. The point * seems to be the preservation of recent checkpoints. */ if (inside_ip) ip_bmap_mark_as_used(paddr, length); else container_bmap_mark_as_used(paddr, length); } apfsprogs-0.2.0/apfsck/spaceman.h000066400000000000000000000044001471277137200167350ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _SPACEMAN_H #define _SPACEMAN_H #include #include "btree.h" #include "object.h" struct apfs_spaceman_free_queue_key; struct spaceman_device { struct free_queue *sm_dev_fq; /* Free queue for the device */ /* Device info read from the on-disk structures */ void *sm_bitmap; /* Allocation bitmap */ u32 sm_cib_count; u32 sm_cab_count; u64 sm_chunk_count; u64 sm_block_count; u64 sm_free_count; /* Device info measured by the fsck */ u64 sm_chunks; /* Number of chunks */ u64 sm_cibs; /* Number of chunk-info blocks */ u64 sm_blocks; /* Number of blocks */ u64 sm_free; /* Number of free blocks */ }; /* Space manager data in memory */ struct spaceman { struct free_queue *sm_ip_fq; /* Free queue for internal pool */ int sm_struct_size; /* Size of the spaceman structure on disk */ u32 sm_obj_size; /* Size of the spaceman object */ struct spaceman_device sm_dev[APFS_SD_COUNT]; /* Spaceman info read from the on-disk structures */ u64 sm_xid; u32 sm_blocks_per_chunk; u32 sm_chunks_per_cib; u32 sm_cibs_per_cab; u64 sm_ip_base; u64 sm_ip_block_count; u32 sm_total_cib_count; u32 sm_total_cab_count; u64 sm_total_chunk_count; u64 sm_total_free_count; /* Spaceman info measured by the fsck */ u64 sm_reserve_block_num; /* Blocks that are reserved for volumes */ u64 sm_reserve_alloc_num; /* Blocks already alloced by those volumes */ }; /* * Free queue data in memory. This is a subclass of struct btree; a free queue * btree can simply be cast to a free_queue structure. */ struct free_queue { /* This must always remain the first field */ struct btree sfq_btree; /* B-tree structure for the free queue */ int sfq_index; /* Position in the free queue array */ /* Free queue stats as measured by the fsck */ u64 sfq_count; /* Total count of free blocks in the queue */ u64 sfq_oldest_xid; /* First transaction id in the queue */ }; extern void container_bmap_mark_as_used(u64 paddr, u64 length); extern void ip_bmap_mark_as_used(u64 paddr, u64 length); extern void check_spaceman(u64 oid); extern void parse_free_queue_record(struct apfs_spaceman_free_queue_key *key, void *val, int len, struct btree *btree); #endif /* _SPACEMAN_H */ apfsprogs-0.2.0/apfsck/super.c000066400000000000000000001572161471277137200163150ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "crypto.h" #include "dir.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "object.h" #include "snapshot.h" #include "spaceman.h" #include "super.h" struct super_block *sb; struct volume_superblock *vsb; /** * is_power_of_two - Check if a number is a power of two * @n: the number to check */ static bool is_power_of_2(unsigned int n) { return (n != 0 && ((n & (n - 1)) == 0)); } /** * blksize_bits - Find the corresponding bit shift for a blocksize * @size: the blocksize */ static inline unsigned int blksize_bits(unsigned int size) { unsigned int bits = 8; if (size < 4096) report("Container superblock", "block size is too small."); if (!is_power_of_2(size)) report("Container superblock", "blocksize isn't power of two."); do { bits++; size >>= 1; } while (size > 256); return bits; } /** * read_super_copy - Read the copy of the container superblock in block 0 * * Sets sb->s_blocksize and returns a pointer to the raw superblock in memory. */ static struct apfs_nx_superblock *read_super_copy(void) { struct apfs_nx_superblock *msb_raw; int bsize_tmp; /* * For now assume a small blocksize, we only need it so that we can * read the actual blocksize from disk. */ bsize_tmp = APFS_NX_DEFAULT_BLOCK_SIZE; msb_raw = mmap(NULL, bsize_tmp, PROT_READ, MAP_PRIVATE, fd_main, APFS_NX_BLOCK_NUM * bsize_tmp); if (msb_raw == MAP_FAILED) system_error(); if (le32_to_cpu(msb_raw->nx_magic) != APFS_NX_MAGIC) report(NULL, "Not an apfs container - are you checking the right partition?"); sb->s_blocksize = le32_to_cpu(msb_raw->nx_block_size); sb->s_blocksize_bits = blksize_bits(sb->s_blocksize); if (sb->s_blocksize != bsize_tmp) { munmap(msb_raw, bsize_tmp); msb_raw = mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, fd_main, APFS_NX_BLOCK_NUM * sb->s_blocksize); if (msb_raw == MAP_FAILED) system_error(); } if (!obj_verify_csum(&msb_raw->nx_o)) report("Block zero", "bad checksum."); if (le64_to_cpu(msb_raw->nx_o.o_oid) != APFS_OID_NX_SUPERBLOCK) report("Block zero", "bad object id."); return msb_raw; } /** * read_latest_super - Read the latest checkpoint superblock * @base: base of the checkpoint descriptor area * @blocks: block count for the checkpoint descriptor area */ static struct apfs_nx_superblock *read_latest_super(u64 base, u32 blocks) { struct apfs_nx_superblock *latest = NULL; u64 xid = 0; u64 bno; assert(sb->s_blocksize); for (bno = base; bno < base + blocks; ++bno) { struct apfs_nx_superblock *current; current = mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, fd_main, bno * sb->s_blocksize); if (current == MAP_FAILED) system_error(); if (le32_to_cpu(current->nx_magic) != APFS_NX_MAGIC) continue; /* Not a superblock */ if (le64_to_cpu(current->nx_o.o_xid) <= xid) continue; /* Old */ if (!obj_verify_csum(¤t->nx_o)) continue; /* Corrupted */ xid = le64_to_cpu(current->nx_o.o_xid); latest = current; } if (!latest) report("Checkpoint descriptor area", "no valid superblock."); return latest; } /** * fusion_super_check - Check the superblock copy on the tier 2 device * @mainsb: superblock copy in block zero of the main device */ static void fusion_super_compare(struct apfs_nx_superblock *mainsb) { struct apfs_nx_superblock *tier2sb = NULL; size_t to_read; off_t offset; ssize_t ret; if (!apfs_is_fusion_drive()) return; tier2sb = calloc(1, sb->s_blocksize); if (!tier2sb) system_error(); to_read = sb->s_blocksize; offset = 0; do { ret = pread(fd_tier2, (void *)tier2sb + offset, to_read, offset); if (ret < 0) system_error(); to_read -= ret; offset += ret; } while (ret > 0); if (to_read > 0) report("Fusion drive", "tier 2 is too small."); if (tier2sb->nx_o.o_xid != mainsb->nx_o.o_xid) { report_crash("Block zero of tier 2 device"); return; } if (!obj_verify_csum(&tier2sb->nx_o)) report("Block zero of tier 2 device", "bad checksum."); /* * The reference seems to have this backwards: the main device has the * bit set to zero, and the tier 2 set to 1. It's also not clear to me * what the meant by "the highest bit". */ if (sb->s_fusion_uuid[15] & 0x01) report("Fusion driver", "wrong top bit for main device uuid."); if (!(tier2sb->nx_fusion_uuid[15] & 0x01)) report("Fusion driver", "wrong top bit for tier 2 device uuid."); /* * Both superblocks should be the same outside of that one bit (and * the checksum, of course). */ tier2sb->nx_fusion_uuid[15] &= ~0x01; tier2sb->nx_o.o_cksum = mainsb->nx_o.o_cksum; if (memcmp(mainsb, tier2sb, sb->s_blocksize)) report("Block zero", "fields don't match the checkpoint."); free(tier2sb); tier2sb = NULL; } /** * main_super_compare - Compare two copies of the container superblock * @desc: the superblock from the latest checkpoint * @copy: the superblock copy in block zero */ static void main_super_compare(struct apfs_nx_superblock *desc, struct apfs_nx_superblock *copy) { char *desc_bytes = (char *)desc; char *copy_bytes = (char *)copy; if (copy->nx_o.o_xid != desc->nx_o.o_xid) { report_crash("Block zero"); return; } /* * The nx_counters array doesn't always match, and it seems that the * same is true of the flags for some reason. Naturally, this means * the checksum won't match either. */ if (memcmp(desc_bytes + 0x08, copy_bytes + 0x08, 0x3D8 - 0x08) || memcmp(desc_bytes + 0x4D8, copy_bytes + 0x4D8, 0x4F0 - 0x4D8) || memcmp(desc_bytes + 0x4F8, copy_bytes + 0x4F8, 4096 - 0x4F8)) report("Block zero", "fields don't match the checkpoint."); } /** * get_device_size - Get the block count for a given device or image * @device_fd: file descriptor for the device * @blocksize: the filesystem blocksize */ static u64 get_device_size(int device_fd, unsigned int blocksize) { struct stat buf; u64 size; if (fstat(device_fd, &buf)) system_error(); if ((buf.st_mode & S_IFMT) == S_IFREG) return buf.st_size / blocksize; if (ioctl(device_fd, BLKGETSIZE64, &size)) system_error(); return size / blocksize; } static u64 get_main_device_size(unsigned int blocksize) { return get_device_size(fd_main, blocksize); } static u64 get_tier2_device_size(unsigned int blocksize) { u64 size; if (fd_tier2 == -1) return 0; size = get_device_size(fd_tier2, blocksize); /* * I might later check the size to decide if the device exists, so * make sure this is always sane. */ if (size == 0) report("Fusion drive", "tier 2 has size zero."); return size; } /** * get_max_volumes - Calculate the maximum number of volumes for the container * @size: the container size, in bytes */ static u32 get_max_volumes(u64 size) { u32 max_vols; /* Divide by 512 MiB and round up, as the reference requires */ max_vols = DIV_ROUND_UP(size, 512 * 1024 * 1024); if (max_vols > APFS_NX_MAX_FILE_SYSTEMS) max_vols = APFS_NX_MAX_FILE_SYSTEMS; return max_vols; } /** * check_main_flags - Check consistency of container flags * @flags: the flags */ static void check_main_flags(u64 flags) { if ((flags & APFS_NX_FLAGS_VALID_MASK) != flags) report("Container superblock", "invalid flag in use."); if (flags & (APFS_NX_RESERVED_1 | APFS_NX_RESERVED_2)) report("Container superblock", "reserved flag in use."); if (flags & APFS_NX_CRYPTO_SW) report_unknown("Software encryption"); } /** * check_optional_main_features - Check the optional features of the container * @flags: the optional feature flags */ static void check_optional_main_features(u64 flags) { if ((flags & APFS_NX_SUPPORTED_FEATURES_MASK) != flags) report("Container superblock", "unknown optional feature."); if (flags & APFS_NX_FEATURE_DEFRAG) report_unknown("Defragmentation"); if (flags & APFS_NX_FEATURE_LCFD) { /* TODO: what is this flag exactly? */ if (!apfs_is_fusion_drive()) report("Container superblock", "LCFD flag set on non-fusion drive."); } } /** * check_rocompat_main_features - Check the ro compatible features of container * @flags: the read-only compatible feature flags */ static void check_rocompat_main_features(u64 flags) { if ((flags & APFS_NX_SUPPORTED_ROCOMPAT_MASK) != flags) report("Container superblock", "unknown ro-compat feature."); } /** * check_incompat_main_features - Check the incompatible features of a container * @flags: the incompatible feature flags */ static void check_incompat_main_features(u64 flags) { if ((flags & APFS_NX_SUPPORTED_INCOMPAT_MASK) != flags) report("Container superblock", "unknown incompatible feature."); if (flags & APFS_NX_INCOMPAT_VERSION1) report_unknown("APFS version 1"); if (!(flags & APFS_NX_INCOMPAT_VERSION2)) report_unknown("APFS versions other than 2"); if ((bool)(flags & APFS_NX_INCOMPAT_FUSION) != (bool)(fd_tier2 != -1)) report("Container superblock", "bad setting for fusion flag."); } /** * check_efi_information - Check the EFI info from the container superblock * @oid: the physical object id for the EFI driver record */ static void check_efi_information(u64 oid) { struct apfs_nx_efi_jumpstart *efi; struct object obj; long long num_extents; u64 block_count = 0; u32 file_length; int i; if (!oid) /* Not all containers can be booted from, of course */ return; efi = read_object(oid, NULL /* omap_table */, &obj); if (obj.type != APFS_OBJECT_TYPE_EFI_JUMPSTART) report("EFI info", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("EFI info", "wrong object subtype."); if (le32_to_cpu(efi->nej_magic) != APFS_NX_EFI_JUMPSTART_MAGIC) report("EFI info", "wrong magic."); if (le32_to_cpu(efi->nej_version) != APFS_NX_EFI_JUMPSTART_VERSION) report("EFI info", "wrong version."); for (i = 0; i < 16; ++i) if (efi->nej_reserved[i]) report("EFI info", "reserved field in use."); num_extents = le32_to_cpu(efi->nej_num_extents); if (sizeof(*efi) + num_extents * sizeof(efi->nej_rec_extents[0]) > sb->s_blocksize) report("EFI info", "number of extents cannot fit."); for (i = 0; i < num_extents; ++i) { struct apfs_prange *ext = &efi->nej_rec_extents[i]; u64 ext_blocks = le64_to_cpu(ext->pr_block_count); u64 ext_bno = le64_to_cpu(ext->pr_start_paddr); if (!ext_blocks) report("EFI info", "empty extent."); container_bmap_mark_as_used(ext_bno, ext_blocks); block_count += ext_blocks; } file_length = le32_to_cpu(efi->nej_efi_file_len); if (!file_length) report("EFI info", "driver is empty."); if (file_length > block_count * sb->s_blocksize) report("EFI info", "driver doesn't fit in extents."); if (file_length <= (block_count - 1) * sb->s_blocksize) report("EFI info", "wasted space in driver extents."); munmap(efi, obj.size); } /** * check_ephemeral_information - Check the container's array of ephemeral info * @info: pointer to the nx_ephemeral_info array on the container superblock */ static void check_ephemeral_information(__le64 *info) { u64 min_block_count; u64 container_size; int i; assert(sb->s_block_count); container_size = sb->s_block_count * sb->s_blocksize; if (container_size < 128 * 1024 * 1024) min_block_count = main_fq_node_limit(sb->s_block_count); else min_block_count = APFS_NX_EPH_MIN_BLOCK_COUNT; if (le64_to_cpu(info[0]) != ((min_block_count << 32) | (APFS_NX_MAX_FILE_SYSTEM_EPH_STRUCTS << 16) | APFS_NX_EPH_INFO_VERSION_1)) report("Container superblock", "bad first entry in ephemeral info."); /* Only the first entry in the info array is documented */ for (i = 1; i < APFS_NX_EPH_INFO_COUNT; ++i) if (info[i]) report_unknown("Ephemeral info array"); } /** * software_strlen - Calculate the length of a software info string * @str: the string * * Also checks that the string has a proper null-termination. */ static int software_strlen(u8 *str) { int length = strnlen((char *)str, APFS_MODIFIED_NAMELEN); if (length == APFS_MODIFIED_NAMELEN) report("Volume software id", "no NULL-termination."); return length; } /** * check_software_info - Check the fields reporting implementation info * @formatted_by: information about the software that created the volume * @modified_by: information about the software that modified the volume */ static void check_software_information(struct apfs_modified_by *formatted_by, struct apfs_modified_by *modified_by) { struct apfs_modified_by *end_mod_by; int length; bool mods_over; u64 xid; mods_over = false; end_mod_by = modified_by + APFS_MAX_HIST; xid = sb->s_xid + 1; /* Last possible xid */ vsb->v_first_xid = le64_to_cpu(formatted_by->last_xid); vsb->v_last_xid = vsb->v_first_xid; for (; modified_by != end_mod_by; ++modified_by) { length = software_strlen(modified_by->id); if (!length && (modified_by->timestamp || modified_by->last_xid)) report("Volume modification info", "entry without id."); if (mods_over) { if (length) report("Volume modification info", "empty entry should end the list."); continue; } if (!length) { mods_over = true; continue; } /* The first entry must be the most recent */ if (xid <= le64_to_cpu(modified_by->last_xid)) report("Volume modification info", "entries are not in order."); xid = le64_to_cpu(modified_by->last_xid); if (xid > vsb->v_last_xid) vsb->v_last_xid = xid; } length = software_strlen(formatted_by->id); if (!length) report("Volume superblock", "creation information is missing."); if (xid <= vsb->v_first_xid) report("Volume creation info", "transaction is too recent."); } /** * check_volume_flags - Check consistency of volume flags * @flags: the flags */ static void check_volume_flags(u64 flags) { u64 incomp = le64_to_cpu(vsb->v_raw->apfs_incompatible_features); if ((flags & APFS_FS_FLAGS_VALID_MASK) != flags) report("Volume superblock", "invalid flag in use."); if (flags & APFS_FS_RESERVED_4) report("Volume superblock", "reserved flag in use."); if (!(flags & APFS_FS_UNENCRYPTED)) vsb->v_encrypted = true; else if (flags & (APFS_FS_EFFACEABLE | APFS_FS_ONEKEY)) report("Volume superblock", "inconsistent crypto flags."); if (flags & (APFS_FS_SPILLEDOVER | APFS_FS_RUN_SPILLOVER_CLEANER)) report_unknown("Fusion drive spillover"); if (flags & APFS_FS_ALWAYS_CHECK_EXTENTREF) report_unknown("Forced extent reference checks"); if (flags & APFS_FS_PREVIOUSLY_SEALED) report_unknown("Previously sealed volume"); if (flags & APFS_FS_PFK) report_unknown("PFK volume flag"); if (flags & APFS_FS_UNKNOWN_200) report_unknown("0x200 volume flag"); if ((bool)(flags & APFS_FS_PFK) != (bool)(incomp & APFS_INCOMPAT_PFK)) report("Volume superblock", "inconsistent PFK bits"); if ((bool)(flags & APFS_FS_PREVIOUSLY_SEALED) && (bool)(incomp & APFS_INCOMPAT_SEALED_VOLUME)) report("Volume superblock", "both sealed and unsealed"); /* Got this check from the official fsck, no idea what it's about */ if (!(bool)(flags & APFS_FS_SPILLEDOVER) && (bool)(incomp & APFS_INCOMPAT_SECONDARY_FSROOT)) report("Volume superblock", "secondary fsroot wihout spillover"); } /** * check_optional_vol_features - Check the optional features of a volume * @flags: the optional feature flags */ static void check_optional_vol_features(u64 flags) { if ((flags & APFS_SUPPORTED_FEATURES_MASK) != flags) report("Volume superblock", "unknown optional feature."); if (flags & APFS_FEATURE_DEFRAG_PRERELEASE) report("Volume superblock", "prerelease defrag enabled."); /* TODO: should be easy to support, but I need an image for testing */ if (!(flags & APFS_FEATURE_HARDLINK_MAP_RECORDS)) report_unknown("Volume without sibling map records"); } /** * check_rocompat_vol_features - Check the ro compatible features of a volume * @flags: the read-only compatible feature flags */ static void check_rocompat_vol_features(u64 flags) { if ((flags & APFS_SUPPORTED_ROCOMPAT_MASK) != flags) report("Volume superblock", "unknown ro compatible feature."); } /** * check_incompat_vol_features - Check the incompatible features of a volume * @flags: the incompatible feature flags */ static void check_incompat_vol_features(u64 flags) { if ((flags & APFS_SUPPORTED_INCOMPAT_MASK) != flags) report("Volume superblock", "unknown incompatible feature."); if (flags & APFS_INCOMPAT_PFK) report_unknown("PFK incompatible volume feature"); if (flags & APFS_INCOMPAT_DATALESS_SNAPS) report_unknown("Dataless snapshots"); if (flags & APFS_INCOMPAT_ENC_ROLLED) report_unknown("Change of encryption keys"); if (flags & APFS_INCOMPAT_INCOMPLETE_RESTORE) report_unknown("Incomplete restore"); if (flags & APFS_INCOMPAT_SECONDARY_FSROOT) report_unknown("Secondary fsroot"); if ((bool)(flags & APFS_INCOMPAT_CASE_INSENSITIVE) && (bool)(flags & APFS_INCOMPAT_NORMALIZATION_INSENSITIVE)) report("Volume superblock", "redundant flag for case sensitivity."); } /** * role_is_valid - Does the give number match a possible volume role? * @role: the role number to check */ static bool role_is_valid(u16 role) { switch (role) { case APFS_VOL_ROLE_NONE: case APFS_VOL_ROLE_SYSTEM: case APFS_VOL_ROLE_USER: case APFS_VOL_ROLE_RECOVERY: case APFS_VOL_ROLE_VM: case APFS_VOL_ROLE_PREBOOT: case APFS_VOL_ROLE_INSTALLER: case APFS_VOL_ROLE_DATA: case APFS_VOL_ROLE_BASEBAND: case APFS_VOL_ROLE_UPDATE: case APFS_VOL_ROLE_XART: case APFS_VOL_ROLE_HARDWARE: case APFS_VOL_ROLE_BACKUP: case APFS_VOL_ROLE_RESERVED_7: case APFS_VOL_ROLE_RESERVED_8: case APFS_VOL_ROLE_ENTERPRISE: case APFS_VOL_ROLE_RESERVED_10: case APFS_VOL_ROLE_PRELOGIN: return true; default: return false; } } /** * check_volume_role - Check that a volume's role flags are valid * @role: the volume role */ static void check_volume_role(u16 role) { if (!role_is_valid(role)) report("Volume superblock", "invalid role in use."); if (role == APFS_VOL_ROLE_RESERVED_7 || role == APFS_VOL_ROLE_RESERVED_8 || role == APFS_VOL_ROLE_RESERVED_10) report("Volume superblock", "reserved role in use."); } static bool meta_crypto_is_empty(struct apfs_wrapped_meta_crypto_state *wmcs) { if (wmcs->major_version || wmcs->minor_version || wmcs->cpflags) return false; if (wmcs->persistent_class || wmcs->key_os_version) return false; if (wmcs->key_revision || wmcs->unused) return false; return true; } /** * check_meta_crypto - Check a volume's meta_crypto field * @wmcs: the structure to check */ static void check_meta_crypto(struct apfs_wrapped_meta_crypto_state *wmcs) { /* This seems to contradict the reference, but it happens sometimes */ if (meta_crypto_is_empty(wmcs)) return; if (le16_to_cpu(wmcs->major_version) != APFS_WMCS_MAJOR_VERSION) report("Volume meta_crypto", "wrong major version."); if (le16_to_cpu(wmcs->minor_version) != APFS_WMCS_MINOR_VERSION) report("Volume meta_crypto", "wrong minor version."); if (wmcs->cpflags) report("Volume meta_crypto", "unknown flag."); if (le32_to_cpu(wmcs->persistent_class) != APFS_PROTECTION_CLASS_F) report_unknown("Encrypted metadata"); if (le16_to_cpu(wmcs->key_revision) != 1) /* Key has been changed */ report_unknown("Encrypted metadata"); if (wmcs->unused) report("Volume meta_crypto", "reserved field in use."); } /** * get_volume_group - Find or create the volume group struct with the given uuid */ static struct volume_group *get_volume_group(char uuid[16]) { struct volume_group *vg = sb->s_volume_group; /* This shouldn't happen according to the reference, but it does */ if (uuid_is_null(uuid)) report_weird("Volume group uuid"); if (vg) { if (memcmp(vg->vg_id, uuid, 16) != 0) report_unknown("Two volume groups"); return vg; } vg = calloc(1, sizeof(*vg)); if (!vg) system_error(); memcpy(vg->vg_id, uuid, 16); sb->s_volume_group = vg; return vg; } /** * parse_volume_group_info - Parse the current volume's metadata for group info */ static void parse_volume_group_info(void) { struct volume_group *vg = NULL; char *vg_uuid = vsb->v_raw->apfs_volume_group_id; if (apfs_volume_is_in_group()) { vg = get_volume_group(vg_uuid); if (vsb->v_in_snapshot) return; if (apfs_is_data_volume_in_group()) { if (vg->vg_data_seen) report("Volume group", "two data volumes."); vg->vg_data_seen = true; } else if (apfs_is_system_volume_in_group()) { if (vg->vg_system_seen) report("Volume group", "two system volumes."); vg->vg_system_seen = true; } else { report("Volume group", "volume is neither data nor system."); } } else { if (!uuid_is_null(vg_uuid)) report("Volume group", "member has no feature flag."); } } static void parse_cloneinfo_epoch(struct volume_superblock *vsb) { struct apfs_superblock *raw = vsb->v_raw; u64 id_epoch, xid; /* * These "cloneinfo" fields are a way to determine if this volume was * modified by an older, buggy implementation that may have corrupted * the INODE_WAS_EVER_CLONED flags. */ id_epoch = le64_to_cpu(raw->apfs_cloneinfo_id_epoch); xid = le64_to_cpu(raw->apfs_cloneinfo_xid); if (id_epoch) { /* * This is the only epoch I have encountered so far. It would * appear to imply that the first inode created may have a * corrupted flag, but that was probably not the intention. In * fact, I've even seen a volume with this epoch that never had * any user inodes at all. */ if (id_epoch != APFS_MIN_USER_INO_NUM) report_unknown("Cloneinfo id epoch"); } if (xid) { /* I've never seen this either */ if (xid != vsb->v_last_xid) report_unknown("Out of date cloneinfo xid"); } /* * The reference says that no xid implies no epoch, but that doesn't * seem to be true for unmodified volumes. */ if (id_epoch && !xid) { if (vsb->v_first_xid != vsb->v_last_xid) report("Volume superblock", "cloneinfo epoch with no xid."); } } /** * integrity_meta_256_from_off - Get a pointer to the 256 bits on an im offset * @raw: pointer to the raw integrity metadata * @offset: offset of the 256-bit value in @raw */ static char *integrity_meta_256_from_off(struct apfs_integrity_meta_phys *raw, u32 offset) { char *value_p = (char *)raw + offset; int sz_256 = 256 / 8; if (offset & 0x7) report("Integrity metadata", "offset is not aligned to 8 bytes."); if (offset < sizeof(*raw)) report("Integrity metadata", "offset overlaps with structure."); if (offset >= sb->s_blocksize || offset + sz_256 > sb->s_blocksize) report("Integrity metadata", "offset is out of bounds."); return value_p; } static void parse_integrity_meta(u64 oid) { struct apfs_integrity_meta_phys *meta = NULL; struct object obj; u32 flags, hash_type; char *hash = NULL; int i; assert(vsb->v_omap_table); meta = read_object(oid, vsb->v_omap_table, &obj); if (obj.type != APFS_OBJECT_TYPE_INTEGRITY_META) report("Integrity metadata", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Integrity metadata", "wrong object subtype."); if (le32_to_cpu(meta->im_version) == APFS_INTEGRITY_META_VERSION_INVALID) report("Integrity metadata", "invalid version."); if (le32_to_cpu(meta->im_version) > APFS_INTEGRITY_META_VERSION_HIGHEST) report("Integrity metadata", "undocumented new version."); flags = le32_to_cpu(meta->im_flags); if (flags & (~((u32)APFS_SEAL_BROKEN))) report("Integrity metadata", "undocumented flags."); if (flags & APFS_SEAL_BROKEN || meta->im_broken_xid) report_unknown("Broken seal volume"); hash_type = le32_to_cpu(meta->im_hash_type); if (hash_type < APFS_HASH_MIN || hash_type > APFS_HASH_MAX) report("Integrity metadata", "undocumented hash type."); if (hash_type != APFS_HASH_SHA256) report_unknown("Unusual hash for sealed volume"); hash = integrity_meta_256_from_off(meta, le32_to_cpu(meta->im_root_hash_offset)); memcpy(vsb->v_hash, hash, 256 / 8); hash = NULL; for (i = 0; i < 9; ++i) { if (meta->im_reserved[i]) report("Integrity metadata", "reserved field is in use."); } munmap(meta, obj.size); } /** * check_doc_id_index_flags - Check consistency of document id index flags * @flags: the flags */ static void check_doc_id_index_flags(u32 flags) { if ((flags & APFS_DOC_ID_VALID_FLAGS) != flags) report("Document id index", "invalid flag in use."); if (flags) report_unknown("Document id index flags"); } /** * read_volume_super - Read the volume superblock and run some checks * @vol: volume number * @vsb: volume superblock struct to receive the results * @obj: volume superblock object */ void read_volume_super(int vol, struct volume_superblock *vsb, struct object *obj) { char *vol_name = NULL; struct spaceman *sm = &sb->s_spaceman; u64 alloc_count, reserve_blkcnt, quota_blkcnt; if (vsb->v_obj.type != APFS_OBJECT_TYPE_FS) report("Volume superblock", "wrong object type."); if (vsb->v_obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Volume superblock", "wrong object subtype."); vsb->v_index = le32_to_cpu(vsb->v_raw->apfs_fs_index); if (vsb->v_index != vol) report("Volume superblock", "wrong reported volume number."); if (le32_to_cpu(vsb->v_raw->apfs_magic) != APFS_MAGIC) report("Volume superblock", "wrong magic."); check_optional_vol_features(le64_to_cpu(vsb->v_raw->apfs_features)); check_rocompat_vol_features(le64_to_cpu( vsb->v_raw->apfs_readonly_compatible_features)); check_incompat_vol_features(le64_to_cpu( vsb->v_raw->apfs_incompatible_features)); alloc_count = le64_to_cpu(vsb->v_raw->apfs_fs_alloc_count); reserve_blkcnt = le64_to_cpu(vsb->v_raw->apfs_fs_reserve_block_count); quota_blkcnt = le64_to_cpu(vsb->v_raw->apfs_fs_quota_block_count); if (reserve_blkcnt) { sm->sm_reserve_block_num += reserve_blkcnt; if (alloc_count > reserve_blkcnt) sm->sm_reserve_alloc_num += reserve_blkcnt; else sm->sm_reserve_alloc_num += alloc_count; } if (quota_blkcnt) { if (alloc_count > quota_blkcnt) report("Volume superblock", "exceeded allocation quota."); /* * These are actually equal in the few cases I've observed, but * I highly doubt that's a general rule. */ if (reserve_blkcnt > quota_blkcnt) report("Volume superblock", "block reserves exceed quota."); } check_meta_crypto(&vsb->v_raw->apfs_meta_crypto); vsb->v_next_obj_id = le64_to_cpu(vsb->v_raw->apfs_next_obj_id); if (vsb->v_next_obj_id < APFS_MIN_USER_INO_NUM) report("Volume superblock", "next catalog id is invalid."); vsb->v_next_doc_id = le32_to_cpu(vsb->v_raw->apfs_next_doc_id); if (vsb->v_next_doc_id < APFS_MIN_DOC_ID) report("Volume superblock", "next document id is invalid."); vol_name = (char *)vsb->v_raw->apfs_volname; if (!*vol_name) report("Volume superblock", "label is missing."); if (strnlen(vol_name, APFS_VOLNAME_LEN) == APFS_VOLNAME_LEN) report("Volume superblock", "name lacks NULL-termination."); check_volume_flags(le64_to_cpu(vsb->v_raw->apfs_fs_flags)); check_software_information(&vsb->v_raw->apfs_formatted_by, &vsb->v_raw->apfs_modified_by[0]); check_volume_role(le16_to_cpu(vsb->v_raw->apfs_role)); /* * The documentation suggests that other tree types could be possible, * but I don't understand how that would work. */ if (le32_to_cpu(vsb->v_raw->apfs_root_tree_type) != (APFS_OBJ_VIRTUAL | APFS_OBJECT_TYPE_BTREE)) report("Volume superblock", "wrong type for catalog tree."); if (le32_to_cpu(vsb->v_raw->apfs_extentref_tree_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE)) report("Volume superblock", "wrong type for extentref tree."); if (le32_to_cpu(vsb->v_raw->apfs_snap_meta_tree_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE)) report("Volume superblock", "wrong type for snapshot tree."); /* The official fsck performs this one check on these two fields */ if (le64_to_cpu(vsb->v_raw->apfs_total_blocks_freed) > le64_to_cpu(vsb->v_raw->apfs_total_blocks_alloced)) report("Volume superblock", "more blocks freed than ever alloced."); if (le16_to_cpu(vsb->v_raw->reserved) != 0) report("Volume superblock", "reserved field is in use."); if (le64_to_cpu(vsb->v_raw->apfs_root_to_xid) != 0) report_unknown("Root from snapshot"); if (le64_to_cpu(vsb->v_raw->apfs_er_state_oid) != 0) report_unknown("Encryption or decryption in progress"); if (le64_to_cpu(vsb->v_raw->apfs_revert_to_xid) != 0) report_unknown("Revert to a snapshot"); if (le64_to_cpu(vsb->v_raw->apfs_revert_to_sblock_oid) != 0) report_unknown("Revert to a volume superblock"); parse_cloneinfo_epoch(vsb); if (apfs_volume_is_sealed()) { /* The reference seems to be wrong about the role */ if (apfs_volume_role() && apfs_volume_role() != APFS_VOL_ROLE_SYSTEM) report("Sealed volume", "wrong role."); if (le32_to_cpu(vsb->v_raw->apfs_fext_tree_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE)) report("Sealed volume", "invalid value of fext tree type."); if (!vsb->v_raw->apfs_fext_tree_oid) report("Sealed volume", "missing fext tree."); if (!vsb->v_raw->apfs_integrity_meta_oid) report("Sealed volume", "missing integrity metadata."); } else { if (vsb->v_raw->apfs_fext_tree_oid || vsb->v_raw->apfs_integrity_meta_oid) report("Volume superblock", "no sealed feature flag."); if (vsb->v_raw->apfs_fext_tree_type && le32_to_cpu(vsb->v_raw->apfs_fext_tree_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE)) report("Volume superblock", "invalid value of fext tree type."); } if (vsb->v_raw->reserved_type && le32_to_cpu(vsb->v_raw->reserved_type) != (APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE)) report("Volume superblock", "invalid value of reserved type."); if (vsb->v_raw->reserved_oid) report("Volume superblock", "reserved oid is set."); parse_volume_group_info(); check_doc_id_index_flags(le32_to_cpu(vsb->v_raw->apfs_doc_id_index_flags)); vsb->v_extref_oid = le64_to_cpu(vsb->v_raw->apfs_extentref_tree_oid); vsb->v_omap_oid = le64_to_cpu(vsb->v_raw->apfs_omap_oid); vsb->v_snap_meta_oid = le64_to_cpu(vsb->v_raw->apfs_snap_meta_tree_oid); vsb->v_fext_tree_oid = le64_to_cpu(vsb->v_raw->apfs_fext_tree_oid); vsb->v_integrity_oid = le64_to_cpu(vsb->v_raw->apfs_integrity_meta_oid); } /** * map_volume_super - Find the volume superblock and map it into memory * @vol: volume number * @vsb: volume superblock struct to receive the results * * Returns the in-memory location of the volume superblock, or NULL if there * is no volume with this number. */ static struct apfs_superblock *map_volume_super(int vol, struct volume_superblock *vsb) { struct apfs_nx_superblock *msb_raw = sb->s_raw; u64 vol_id; vol_id = le64_to_cpu(msb_raw->nx_fs_oid[vol]); if (vol_id == 0) return NULL; if (vol > sb->s_max_vols) report("Container superblock", "too many volumes."); vsb->v_raw = read_object(vol_id, sb->s_omap_table, &vsb->v_obj); read_volume_super(vol, vsb, &vsb->v_obj); return vsb->v_raw; } static struct object *parse_reaper(u64 oid); /** * check_volume_group - Check that a volume group (if any) was complete * @vg: the volume group (may be NULL) */ static void check_volume_group(struct volume_group *vg) { if (!vg) return; if (!vg->vg_system_seen) report("Volume group", "system volume is missing."); if (!vg->vg_data_seen) report_weird("Volume group with no data"); } /** * alloc_volume_super - Allocate an in-memory volume superblock struct * @snap: is this a snapshot? */ struct volume_superblock *alloc_volume_super(bool snap) { struct volume_superblock *ret = NULL; ret = calloc(1, sizeof(*ret)); if (!ret) system_error(); ret->v_in_snapshot = snap; if (!snap) { ret->v_omap_table = alloc_htable(); ret->v_snap_table = alloc_htable(); ret->v_extent_table = alloc_htable(); } ret->v_cnid_table = alloc_htable(); ret->v_dstream_table = alloc_htable(); ret->v_inode_table = alloc_htable(); ret->v_dirstat_table = alloc_htable(); ret->v_crypto_table = alloc_htable(); return ret; } static void check_snap_meta_ext(u64 oid) { struct apfs_snap_meta_ext *sme = NULL; struct object obj; /* Older implementations of apfs just don't set this */ if (!oid) return; if (vsb->v_snap_max_xid == 0) report("Volume superblock", "has extended snap meta but no snapshots."); sme = read_object(oid, vsb->v_omap_table, &obj); if (obj.type != APFS_OBJECT_TYPE_SNAP_META_EXT) report("Extended snapshot metadata", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Extended snapshot metadata", "wrong object subtype."); if (le32_to_cpu(sme->sme_version) != 1) report("Extended snapshot metadata", "wrong version."); if (sme->sme_flags) report("Extended snapshot metadata", "undocumented flags."); if (!sme->sme_snap_xid) report("Extended snapshot metadata", "null transaction id."); /* * The current transaction has the same content as the latest snapshot, * but this may be impossible to check if that snapshot got deleted. */ if (vsb->v_in_snapshot && le64_to_cpu(sme->sme_snap_xid) != sb->s_xid) report("Extended snapshot metadata", "wrong transaction id."); munmap(sme, obj.size); } /** * check_volume_super - Parse and check the whole current volume superblock */ void check_volume_super(void) { struct apfs_superblock *vsb_raw = vsb->v_raw; if (!vsb->v_in_snapshot) { vsb->v_omap = parse_omap_btree(vsb->v_omap_oid); vsb->v_snap_meta = parse_snap_meta_btree(vsb->v_snap_meta_oid); } /* A virtual object, so it must be parsed after the omap */ if (apfs_volume_is_sealed()) parse_integrity_meta(vsb->v_integrity_oid); /* The first tree is for the latest xid, the others are for snapshots */ if (!vsb->v_in_snapshot) { vsb->v_extent_ref = parse_extentref_btree(vsb->v_extref_oid); } else { /* We want the most recent snapshots first */ struct listed_btree *new = NULL; new = calloc(1, sizeof(*new)); if (!new) system_error(); new->btree = parse_extentref_btree(vsb->v_extref_oid); new->next = vsb->v_snap_extrefs; vsb->v_snap_extrefs = new; } if (apfs_volume_is_sealed()) vsb->v_fext = parse_fext_btree(vsb->v_fext_tree_oid); vsb->v_cat = parse_cat_btree(le64_to_cpu(vsb_raw->apfs_root_tree_oid), vsb->v_omap_table); check_snap_meta_ext(le64_to_cpu(vsb_raw->apfs_snap_meta_ext_oid)); if (!vsb->v_in_snapshot) { free_snap_table(vsb->v_snap_table); vsb->v_snap_table = NULL; } free_inode_table(vsb->v_inode_table); vsb->v_inode_table = NULL; free_dstream_table(vsb->v_dstream_table); vsb->v_dstream_table = NULL; free_cnid_table(vsb->v_cnid_table); vsb->v_cnid_table = NULL; if (!vsb->v_in_snapshot) { free_extent_table(vsb->v_extent_table); vsb->v_extent_table = NULL; free_omap_table(vsb->v_omap_table); vsb->v_omap_table = NULL; } else { check_and_reset_extent_table(vsb->v_extent_table); } free_dirstat_table(vsb->v_dirstat_table); vsb->v_dirstat_table = NULL; free_crypto_table(vsb->v_crypto_table); vsb->v_crypto_table = NULL; if (!vsb->v_has_root) report("Catalog", "the root directory is missing."); if (!vsb->v_has_priv) report("Catalog", "the private directory is missing."); if (le64_to_cpu(vsb_raw->apfs_num_files) != vsb->v_file_count) { /* Sometimes this is off by one. TODO: why? */ report_weird("File count in volume superblock"); } if (le64_to_cpu(vsb_raw->apfs_num_directories) != vsb->v_dir_count) report("Volume superblock", "bad directory count."); if (le64_to_cpu(vsb_raw->apfs_num_symlinks) != vsb->v_symlink_count) report("Volume superblock", "bad symlink count."); if (le64_to_cpu(vsb_raw->apfs_num_other_fsobjects) != vsb->v_special_count) report("Volume superblock", "bad special file count."); /* * Older snapshots may have been deleted after this one was created, so * there is no way to check apfs_num_snapshots exactly. * * TODO: check that each snapshot has more snaps than the previous one? */ if (vsb->v_in_snapshot) { if (le64_to_cpu(vsb_raw->apfs_num_snapshots) < vsb->v_snap_count) report("Volume superblock", "bad snapshot count."); } else { if (le64_to_cpu(vsb_raw->apfs_num_snapshots) != vsb->v_snap_count) report("Volume superblock", "bad snapshot count."); } /* * The original omap for a snapshot is not preserved, so there is no way * to know the real value of v_block_count back then. */ if (!vsb->v_in_snapshot) { if (le64_to_cpu(vsb_raw->apfs_fs_alloc_count) != vsb->v_block_count) report("Volume superblock", "bad block count."); } } static struct object *parse_fusion_wbc_state(u64 oid); static void check_fusion_wbc(u64 bno, u64 blkcnt); /** * check_container - Check the whole container for a given checkpoint * @sb: checkpoint superblock */ static void check_container(struct super_block *sb) { int vol; bool reaper_vol_seen = false; sb->s_omap_table = alloc_htable(); /* Check for corruption in the container object map... */ sb->s_omap = parse_omap_btree(le64_to_cpu(sb->s_raw->nx_omap_oid)); /* ...and in the reaper */ sb->s_reaper = parse_reaper(le64_to_cpu(sb->s_raw->nx_reaper_oid)); /* Check the fusion structures now */ sb->s_fusion_mt = parse_fusion_middle_tree(le64_to_cpu(sb->s_raw->nx_fusion_mt_oid)); sb->s_fusion_wbc = parse_fusion_wbc_state(le64_to_cpu(sb->s_raw->nx_fusion_wbc_oid)); check_fusion_wbc(le64_to_cpu(sb->s_raw->nx_fusion_wbc.pr_start_paddr), le64_to_cpu(sb->s_raw->nx_fusion_wbc.pr_block_count)); for (vol = 0; vol < APFS_NX_MAX_FILE_SYSTEMS; ++vol) { struct apfs_superblock *vsb_raw; vsb = alloc_volume_super(false); vsb_raw = map_volume_super(vol, vsb); if (!vsb_raw) { /* * Containers typically have all of their volumes at the * beginning of the array, but I've encountered images * where this isn't true. I guess it makes sense if * volumes can be deleted? */ free(vsb); continue; } if (vsb->v_obj.oid == sb->s_reaper_fs_id) reaper_vol_seen = true; check_volume_super(); sb->s_volumes[vol] = vsb; vsb = NULL; } /* For now we just check that the reaper's volume exists */ if (sb->s_reaper_fs_id && !reaper_vol_seen) report("Reaper", "volume id is invalid."); free_omap_table(sb->s_omap_table); sb->s_omap_table = NULL; check_spaceman(le64_to_cpu(sb->s_raw->nx_spaceman_oid)); check_volume_group(sb->s_volume_group); free(sb->s_volume_group); sb->s_volume_group = NULL; } /** * parse_main_super - Parse a container superblock and run generic checks * @sb: checkpoint superblock struct to receive the results */ static void parse_main_super(struct super_block *sb) { u64 max_main_chunk_count, max_tier2_chunk_count; assert(sb->s_raw); /* This field was already set from the checkpoint mappings */ assert(sb->s_xid); if (sb->s_xid != le64_to_cpu(sb->s_raw->nx_o.o_xid)) report("Container superblock", "inconsistent xid."); sb->s_blocksize = le32_to_cpu(sb->s_raw->nx_block_size); if (sb->s_blocksize != APFS_NX_DEFAULT_BLOCK_SIZE) report_unknown("Block size other than 4096"); sb->s_block_count = le64_to_cpu(sb->s_raw->nx_block_count); if (!sb->s_block_count) report("Container superblock", "reports no block count."); sb->s_max_main_blkcnt = get_main_device_size(sb->s_blocksize); sb->s_max_tier2_blkcnt = get_tier2_device_size(sb->s_blocksize); if (sb->s_block_count > sb->s_max_main_blkcnt + sb->s_max_tier2_blkcnt) report("Container superblock", "too many blocks for device."); /* * A chunk is the disk section covered by a single block in the * allocation bitmap. */ max_main_chunk_count = DIV_ROUND_UP(sb->s_max_main_blkcnt, 8 * sb->s_blocksize); sb->s_main_bitmap = calloc(max_main_chunk_count, sb->s_blocksize); if (!sb->s_main_bitmap) system_error(); ((char *)sb->s_main_bitmap)[0] = 0x01; /* Block zero is always used */ if (sb->s_max_tier2_blkcnt) { max_tier2_chunk_count = DIV_ROUND_UP(sb->s_max_tier2_blkcnt, 8 * sb->s_blocksize); sb->s_tier2_bitmap = calloc(max_tier2_chunk_count, sb->s_blocksize); if (!sb->s_tier2_bitmap) system_error(); ((char *)sb->s_tier2_bitmap)[0] = 0x01; /* Block zero is always used */ } sb->s_max_vols = get_max_volumes(sb->s_block_count * sb->s_blocksize); if (sb->s_max_vols != le32_to_cpu(sb->s_raw->nx_max_file_systems)) report("Container superblock", "bad maximum volume number."); check_main_flags(le64_to_cpu(sb->s_raw->nx_flags)); check_optional_main_features(le64_to_cpu(sb->s_raw->nx_features)); check_rocompat_main_features(le64_to_cpu( sb->s_raw->nx_readonly_compatible_features)); check_incompat_main_features(le64_to_cpu( sb->s_raw->nx_incompatible_features)); if (le32_to_cpu(sb->s_raw->nx_xp_desc_blocks) >> 31 || le32_to_cpu(sb->s_raw->nx_xp_data_blocks) >> 31 || le64_to_cpu(sb->s_raw->nx_xp_desc_base) >> 63 || le64_to_cpu(sb->s_raw->nx_xp_data_base) >> 63) report("Container superblock", "has checkpoint tree."); sb->s_data_base = le64_to_cpu(sb->s_raw->nx_xp_data_base); sb->s_data_blocks = le32_to_cpu(sb->s_raw->nx_xp_data_blocks); sb->s_data_index = le32_to_cpu(sb->s_raw->nx_xp_data_index); sb->s_data_len = le32_to_cpu(sb->s_raw->nx_xp_data_len); if (sb->s_data_index >= sb->s_data_blocks) report("Container superblock", "out of range checkpoint data."); if (sb->s_data_len > sb->s_data_blocks) report("Container superblock", "reports too many blocks of checkpoint data."); if ((sb->s_data_index + sb->s_data_len) % sb->s_data_blocks != le32_to_cpu(sb->s_raw->nx_xp_data_next)) report("Container superblock", "wrong length for checkpoint data."); if (sb->s_raw->nx_test_type || sb->s_raw->nx_test_oid) report("Container superblock", "test field is set."); if (sb->s_raw->nx_blocked_out_prange.pr_block_count) report_unknown("Partition resizing"); check_efi_information(le64_to_cpu(sb->s_raw->nx_efi_jumpstart)); check_ephemeral_information(&sb->s_raw->nx_ephemeral_info[0]); if (uuid_is_null(sb->s_raw->nx_fusion_uuid) != !apfs_is_fusion_drive()) report("Container superblock", "incorrect fusion uuid."); memcpy(sb->s_fusion_uuid, sb->s_raw->nx_fusion_uuid, sizeof(sb->s_fusion_uuid)); /* Containers with no encryption may still have a value here, why? */ check_keybag(le64_to_cpu(sb->s_raw->nx_keylocker.pr_start_paddr), le64_to_cpu(sb->s_raw->nx_keylocker.pr_block_count)); /* TODO: actually check all this stuff */ container_bmap_mark_as_used(le64_to_cpu(sb->s_raw->nx_mkb_locker.pr_start_paddr), le64_to_cpu(sb->s_raw->nx_mkb_locker.pr_block_count)); sb->s_next_oid = le64_to_cpu(sb->s_raw->nx_next_oid); if (sb->s_xid + 1 != le64_to_cpu(sb->s_raw->nx_next_xid)) report("Container superblock", "next transaction id is wrong."); } struct checkpoint_info { u64 desc_base; u64 data_base; u32 desc_blocks; u32 data_blocks; u32 desc_next; u32 data_next; u32 desc_index; u32 data_index; u32 desc_len; u32 data_len; }; /** * parse_cpoint_map - Parse and verify a checkpoint mapping * @raw: the raw checkpoint mapping * @cp_info: information about the checkpoint areas * @idx: expected index for the mapping * * Returns the expected index for the next mapping. */ static u32 parse_cpoint_map(struct apfs_checkpoint_mapping *raw, struct checkpoint_info *cp_info, u32 idx) { struct cpoint_map *map; u32 start_off, blkcnt; map = get_cpoint_map(le64_to_cpu(raw->cpm_oid)); if (map->m_paddr) report("Checkpoint maps", "two mappings for the same oid."); if (!raw->cpm_paddr) report("Checkpoint map", "invalid physical address."); map->m_paddr = le64_to_cpu(raw->cpm_paddr); if (map->m_paddr != cp_info->data_base + idx) report("Chekpoint map", "out of order or with holes."); map->m_type = le32_to_cpu(raw->cpm_type); map->m_subtype = le32_to_cpu(raw->cpm_subtype); map->m_size = le32_to_cpu(raw->cpm_size); if (map->m_size & (sb->s_blocksize - 1)) report("Checkpoint map", "size isn't multiple of block size."); blkcnt = map->m_size >> sb->s_blocksize_bits; if ((map->m_type & APFS_OBJECT_TYPE_MASK) != APFS_OBJECT_TYPE_SPACEMAN) { if (map->m_size != sb->s_blocksize) report_unknown("Large non-spaceman ephemeral objects"); } if (raw->cpm_pad) report("Checkpoint map", "non-zero padding."); if (raw->cpm_fs_oid) report_unknown("Ephemeral object belonging to a volume"); start_off = (cp_info->data_blocks + idx - cp_info->data_index) % cp_info->data_blocks; if (start_off >= cp_info->data_len || start_off + blkcnt > cp_info->data_len) report("Checkpoint map", "object index outside valid range."); idx += blkcnt; return idx % cp_info->data_blocks; } /** * parse_cpoint_map_blocks - Parse and verify a checkpoint's mapping blocks * @cp_info: information about the checkpoint areas * @index: index of the first mapping block for the checkpoint * * Returns the number of checkpoint-mapping blocks, and sets @index to the * index of their checkpoint superblock. */ static u32 parse_cpoint_map_blocks(struct checkpoint_info *cp_info, u32 *index) { struct object obj; struct apfs_checkpoint_map_phys *raw; u32 blk_count = 0; u32 cpm_count; u32 obj_idx; /* * The current superblock hasn't been parsed yet, so this xid would be * from the previous checkpoint. */ assert(!sb->s_xid); assert(!sb->s_cpoint_map_table); sb->s_cpoint_map_table = alloc_htable(); obj_idx = cp_info->data_index; while (1) { u64 bno = cp_info->desc_base + *index; u32 flags; int i; raw = read_object_nocheck(bno, sb->s_blocksize, &obj); if (obj.oid != bno) report("Checkpoint map", "wrong object id."); if (parse_object_flags(obj.flags, false) != APFS_OBJ_PHYSICAL) report("Checkpoint map", "wrong storage type."); if (obj.type != APFS_OBJECT_TYPE_CHECKPOINT_MAP) report("Checkpoint map", "wrong object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Checkpoint map", "wrong object subtype."); /* Checkpoint mappings belong to the checkpoint transaction */ if (sb->s_xid && obj.xid != sb->s_xid) report("Checkpoint map", "inconsistent xid."); if (!obj.xid) report("Checkpoint map", "invalid xid."); sb->s_xid = obj.xid; cpm_count = le32_to_cpu(raw->cpm_count); if (sizeof(*raw) + cpm_count * sizeof(raw->cpm_map[0]) > sb->s_blocksize) report("Checkpoint maps", "won't fit in block."); for (i = 0; i < cpm_count; ++i) obj_idx = parse_cpoint_map(&raw->cpm_map[i], cp_info, obj_idx); flags = le32_to_cpu(raw->cpm_flags); munmap(raw, obj.size); blk_count++; *index = (*index + 1) % cp_info->desc_blocks; if ((flags & APFS_CHECKPOINT_MAP_LAST) != flags) report("Checkpoint map", "invalid flag in use."); if (flags & APFS_CHECKPOINT_MAP_LAST) break; if (blk_count == cp_info->desc_blocks) report("Checkpoint", "no mapping block marked last."); } if (obj_idx != cp_info->data_next) report("Checkpoint maps", "overlap or have holes."); return blk_count; } /** * preread_checkpoint_info - Read the superblock fields that are needed early on * @nx_sb_copy: superblock backup on block zero * @info: on return, information about the checkpoint areas */ static void preread_checkpoint_info(struct apfs_nx_superblock *nx_sb_copy, struct checkpoint_info *info) { struct apfs_nx_superblock *msb_raw_latest = NULL; /* We want to mount the latest valid checkpoint among the descriptors */ info->desc_base = le64_to_cpu(nx_sb_copy->nx_xp_desc_base); if (info->desc_base >> 63 != 0) { /* The highest bit is set when checkpoints are not contiguous */ report("Block zero", "checkpoint descriptor tree not yet supported."); } info->desc_blocks = le32_to_cpu(nx_sb_copy->nx_xp_desc_blocks); if (info->desc_blocks > 10000) /* Arbitrary loop limit, is it enough? */ report("Block zero", "too many checkpoint descriptors?"); info->data_base = le64_to_cpu(nx_sb_copy->nx_xp_data_base); info->data_blocks = le32_to_cpu(nx_sb_copy->nx_xp_data_blocks); /* Find the valid range, as reported by the latest descriptor */ msb_raw_latest = read_latest_super(info->desc_base, info->desc_blocks); info->desc_next = le32_to_cpu(msb_raw_latest->nx_xp_desc_next); info->desc_index = le32_to_cpu(msb_raw_latest->nx_xp_desc_index); if (info->desc_next >= info->desc_blocks || info->desc_index >= info->desc_blocks) report("Checkpoint superblock", "out of range checkpoint descriptors."); info->data_next = le32_to_cpu(msb_raw_latest->nx_xp_data_next); info->data_index = le32_to_cpu(msb_raw_latest->nx_xp_data_index); if (info->data_next >= info->data_blocks || info->data_index >= info->data_blocks) report("Checkpoint superblock", "out of range checkpoint data."); info->desc_len = le32_to_cpu(msb_raw_latest->nx_xp_desc_len); info->data_len = le32_to_cpu(msb_raw_latest->nx_xp_data_len); munmap(msb_raw_latest, sb->s_blocksize); msb_raw_latest = NULL; } /** * parse_filesystem - Parse the whole filesystem looking for corruption */ void parse_filesystem(void) { struct apfs_nx_superblock *msb_raw_copy; struct checkpoint_info cp_info = {0}; long long valid_blocks; u32 index; sb = calloc(1, sizeof(*sb)); if (!sb) system_error(); /* Read the superblock from the last clean unmount */ msb_raw_copy = read_super_copy(); /* * We'll read the superblock in full only after going through the * checkpoint mapping blocks, so we need to know a few fields related * to the checkpoints ahead of time. */ preread_checkpoint_info(msb_raw_copy, &cp_info); /* * Now go through the valid checkpoints one by one, though it seems * that cleanly unmounted filesystems only preserve the last one. */ index = cp_info.desc_index; valid_blocks = (cp_info.desc_blocks + cp_info.desc_next - cp_info.desc_index) % cp_info.desc_blocks; while (valid_blocks > 0) { struct object obj; struct apfs_nx_superblock *raw; u64 bno; u32 map_blocks; /* Some fields from the previous checkpoint need to be unset */ if (sb->s_raw) munmap(sb->s_raw, sb->s_blocksize); sb->s_raw = NULL; sb->s_xid = 0; free(sb->s_main_bitmap); sb->s_main_bitmap = NULL; free(sb->s_tier2_bitmap); sb->s_tier2_bitmap = NULL; /* The checkpoint-mapping blocks come before the superblock */ map_blocks = parse_cpoint_map_blocks(&cp_info, &index); valid_blocks -= map_blocks; bno = cp_info.desc_base + index; raw = read_object_nocheck(bno, sb->s_blocksize, &obj); if (parse_object_flags(obj.flags, false) != APFS_OBJ_EPHEMERAL) report("Checkpoint superblock", "bad storage type."); if (obj.type != APFS_OBJECT_TYPE_NX_SUPERBLOCK) report("Checkpoint superblock", "bad object type."); if (obj.subtype != APFS_OBJECT_TYPE_INVALID) report("Checkpoint superblock", "bad object subtype."); if (le32_to_cpu(raw->nx_magic) != APFS_NX_MAGIC) report("Checkpoint superblock", "wrong magic."); if (le32_to_cpu(raw->nx_xp_desc_len) != map_blocks + 1) report("Checkpoint superblock", "wrong checkpoint descriptor block count."); sb->s_raw = raw; parse_main_super(sb); /* Do this now, after parse_main_super() allocated the bitmap */ container_bmap_mark_as_used(cp_info.desc_base, cp_info.desc_blocks); container_bmap_mark_as_used(sb->s_data_base, sb->s_data_blocks); check_container(sb); free_cpoint_map_table(sb->s_cpoint_map_table); sb->s_cpoint_map_table = NULL; /* One more block for the checkpoint superblock itself */ index = (index + 1) % cp_info.desc_blocks; valid_blocks--; } if (valid_blocks != 0) report("Block zero", "bad index for checkpoint descriptors."); if (!sb->s_raw) report("Checkpoint descriptor area", "no valid superblocks."); main_super_compare(sb->s_raw, msb_raw_copy); fusion_super_compare(msb_raw_copy); munmap(msb_raw_copy, sb->s_blocksize); } /** * parse_reaper - Parse the reaper and check for corruption * @oid: object id for the reaper * * Returns a pointer to the object struct for the reaper. */ static struct object *parse_reaper(u64 oid) { struct apfs_nx_reaper_phys *raw; struct object *reaper; u32 flags, buffer_size; int i; reaper = calloc(1, sizeof(*reaper)); if (!reaper) system_error(); raw = read_ephemeral_object(oid, reaper); if (reaper->type != APFS_OBJECT_TYPE_NX_REAPER) report("Reaper", "wrong object type."); if (reaper->subtype != APFS_OBJECT_TYPE_INVALID) report("Reaper", "wrong object subtype."); buffer_size = le32_to_cpu(raw->nr_state_buffer_size); if (buffer_size != sb->s_blocksize - sizeof(*raw)) report("Reaper", "wrong state buffer size."); /* Docs on the reaper are very incomplete, so let's hope it's empty */ if (raw->nr_head) { struct apfs_nx_reap_list_phys *list_raw = NULL; struct object list = {0}; sb->s_reaper_fs_id = le64_to_cpu(raw->nr_fs_oid); if (le64_to_cpu(raw->nr_next_reap_id) <= le64_to_cpu(raw->nr_completed_id)) report("Reaper", "next read id before completed."); if (raw->nr_tail != raw->nr_head) report_unknown("Nonempty reaper"); if (le64_to_cpu(raw->nr_head) - le64_to_cpu(raw->nr_tail) + 1 != le32_to_cpu(raw->nr_rlcount)) report("Reaper", "wrong reap list count."); list_raw = read_ephemeral_object(le64_to_cpu(raw->nr_head), &list); if (list.type != APFS_OBJECT_TYPE_NX_REAP_LIST) report("Reaper list", "wrong object type."); if (list.subtype != APFS_OBJECT_TYPE_INVALID) report("Reaper list", "wrong object subtype."); if (list_raw->nrl_max != cpu_to_le32((sb->s_blocksize - sizeof(*list_raw)) / sizeof(struct apfs_nx_reap_list_entry))) report("Reaper list", "wrong maximum entry count."); if (list_raw->nrl_next || list_raw->nrl_flags || list_raw->nrl_count) report_unknown("Nonempty reaper list"); if (list_raw->nrl_first != cpu_to_le32(-1) || list_raw->nrl_last != cpu_to_le32(-1)) report_unknown("Nonempty reaper list"); /* TODO: nrl_free? */ free(list_raw); } else { if (raw->nr_completed_id || raw->nr_head || raw->nr_rlcount || raw->nr_type) report("Reaper", "should be empty."); if (raw->nr_size || raw->nr_oid || raw->nr_xid || raw->nr_nrle_flags) report("Reaper", "should be empty."); if (le64_to_cpu(raw->nr_next_reap_id) != 1) report("Reaper", "should be empty."); for (i = 0; i < buffer_size; ++i) { if (raw->nr_state_buffer[i]) report("Reaper", "should be empty."); } if (raw->nr_fs_oid) report("Reaper", "is empty but belongs to a volume."); } flags = le32_to_cpu(raw->nr_flags); if ((flags & APFS_NR_FLAGS_VALID_MASK) != flags) report("Reaper", "invalid flag in use."); if (!(flags & APFS_NR_BHM_FLAG)) report("Reaper", "reserved flag must always be set."); if (flags & APFS_NR_CONTINUE) report_unknown("Object being reaped"); free(raw); return reaper; } /** * parse_fusion_wbc_list - Parse and check the whole list of wbc entries * @head_oid: first list block oid * @tail_oid: last list block oid * @version: fusion wbc version */ static void parse_fusion_wbc_list(u64 head_oid, u64 tail_oid, u64 version) { if (head_oid || tail_oid) report_unknown("Nonempty fusion wb cache"); } /** * parse_fusion_wbc_state - Parse the fusion wbc state and check for corruption * @oid: object id for the fusion write-back cache state * * Returns a pointer to the object struct for the wbc. */ static struct object *parse_fusion_wbc_state(u64 oid) { struct apfs_fusion_wbc_phys *wbc = NULL; struct object *obj = NULL; if (apfs_is_fusion_drive() != (bool)oid) report("Fusion wb cache", "oid incorrectly set/unset."); if (!oid) return NULL; obj = calloc(1, sizeof(*obj)); if (!obj) system_error(); wbc = read_ephemeral_object(oid, obj); if (obj->type != APFS_OBJECT_TYPE_NX_FUSION_WBC) report("Fusion wb cache", "wrong object type."); if (obj->subtype != APFS_OBJECT_TYPE_INVALID) report("Fusion wb cache", "wrong object subtype."); if (le64_to_cpu(wbc->fwp_version) != 0x70) report_unknown("Unknown version of fusion wb cache"); if (wbc->fwp_reserved) report("Fusion wb cache", "reserved field in use."); parse_fusion_wbc_list(le64_to_cpu(wbc->fwp_listHeadOid), le64_to_cpu(wbc->fwp_listTailOid), le64_to_cpu(wbc->fwp_version)); if (wbc->fwp_stableHeadOffset || wbc->fwp_stableTailOffset || wbc->fwp_listBlocksCount || wbc->fwp_usedByRC) report_unknown("Nonempty fusion wb cache"); if (wbc->fwp_rcStash.pr_start_paddr || wbc->fwp_rcStash.pr_block_count) report_unknown("Nonempty fusion wb cache"); free(wbc); return obj; } /** * check_fusion_wbc - Check the address range for the wb cache * @bno: first block of the wb cache * @blkcnt: block count of the cache */ static void check_fusion_wbc(u64 bno, u64 blkcnt) { if (!apfs_is_fusion_drive()) { if (bno || blkcnt) report("Fusion wb cache", "should not exist."); return; } if (!bno || !blkcnt) report("Fusion wb cache", "should exist."); container_bmap_mark_as_used(bno, blkcnt); } apfsprogs-0.2.0/apfsck/super.h000066400000000000000000000156371471277137200163220ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _SUPER_H #define _SUPER_H #include #include #include "htable.h" #include "object.h" #include "spaceman.h" struct volume_group { char vg_id[16]; bool vg_system_seen; bool vg_data_seen; }; /** * An entry in a linked list of b-trees */ struct listed_btree { struct btree *btree; struct listed_btree *next; }; struct volume_superblock { struct apfs_superblock *v_raw; struct btree *v_omap; struct btree *v_cat; struct btree *v_extent_ref; struct listed_btree *v_snap_extrefs; /* Snapshots have their own */ struct btree *v_snap_meta; struct btree *v_snapshots; struct btree *v_fext; struct htable_entry **v_omap_table; /* Hash table of omap records */ struct htable_entry **v_inode_table; /* Hash table of all inodes */ struct htable_entry **v_dstream_table; /* Hash table of all dstreams */ struct htable_entry **v_cnid_table; /* Hash table of all cnids */ struct htable_entry **v_extent_table; /* Hash table of all extents */ struct htable_entry **v_snap_table; /* Hash table of all snapshots */ struct htable_entry **v_dirstat_table; /* Hash table of all dir stats */ struct htable_entry **v_crypto_table; /* Hash table of all crypto states */ bool v_in_snapshot; /* Is this a snapshot volume? */ /* Volume stats as measured by the fsck */ u64 v_file_count; /* Number of files */ u64 v_dir_count; /* Number of directories */ u64 v_symlink_count; /* Number of symlinks */ u64 v_special_count; /* Number of other filesystem objects */ u64 v_block_count; /* Number of blocks currently allocated */ u64 v_snap_count; /* Number of snapshots */ u64 v_snap_max_xid; /* Maximum xid among snapshots */ bool v_has_root; /* Is there a root directory? */ bool v_has_priv; /* Is there a private directory? */ /* Volume information read from the on-disk structure */ u64 v_extref_oid; /* Object id for the extent reference tree */ u64 v_omap_oid; /* Object id for object map tree */ u64 v_snap_meta_oid; /* Object id for the snapshot metadata tree */ u64 v_fext_tree_oid; /* Object id for the fext tree */ u64 v_integrity_oid; /* Object id for the integrity metadata */ u64 v_first_xid; /* Transaction that created the volume */ u64 v_last_xid; /* Transaction that last modified the volume */ u64 v_next_obj_id; /* Next cnid to be assigned */ u32 v_next_doc_id; /* Next document identifier to be assigned */ u32 v_index; /* Index in the container's volume array */ bool v_encrypted; /* Is the volume encrypted? */ u8 v_hash[32]; /* For a sealed volume, the root SHA-256 */ struct object v_obj; /* Object holding the volume sb */ }; /* Superblock data in memory */ struct super_block { struct apfs_nx_superblock *s_raw; void *s_main_bitmap; /* Allocation bitmap for the main device */ void *s_tier2_bitmap; /* Allocation bitmap for the tier 2 device */ void *s_ip_bitmap; /* Allocation bitmap for the internal pool */ char s_fusion_uuid[16]; /* Fusion drive UUID set on the main device */ struct btree *s_fusion_mt; /* Fusion middle-tree */ struct object *s_fusion_wbc; /* Fusion write-back cache */ struct btree *s_omap; struct object *s_reaper; unsigned long s_blocksize; unsigned char s_blocksize_bits; u64 s_block_count; /* Number of blocks in the container */ u64 s_max_main_blkcnt; /* Maximum number of blocks in the main device */ u64 s_max_tier2_blkcnt; /* Maximum number of blocks in the tier 2 device */ u64 s_xid; /* Transaction id for the superblock */ u64 s_next_oid; /* Next virtual object id to be used */ u32 s_max_vols; /* Maximum number of volumes allowed */ u64 s_data_base; /* Base address of the checkpoint data area */ u32 s_data_blocks; /* Number of blocks in the checkpoint data area */ u32 s_data_index; /* Index of first valid block in checkpoint data */ u32 s_data_len; /* Number of valid blocks in checkpoint data area */ u64 s_reaper_fs_id; /* Volume id reported by the reaper */ /* Hash table of ephemeral object mappings for the checkpoint */ struct htable_entry **s_cpoint_map_table; /* Hash table of virtual object mappings for the container */ struct htable_entry **s_omap_table; struct spaceman s_spaceman; /* Information about the space manager */ /* Information about the one volume group in the container, if any */ struct volume_group *s_volume_group; /* This is excessive in most cases. TODO: switch to a linked list? */ struct volume_superblock *s_volumes[APFS_NX_MAX_FILE_SYSTEMS]; }; /* * Checkpoint mapping data in memory */ struct cpoint_map { struct htable_entry m_htable; /* Hash table entry header */ bool m_seen; /* Has this ephemeral oid been seen in use? */ u32 m_type; /* Type of the object */ u32 m_subtype; /* Subtype of the object */ u64 m_paddr; /* Physical address of the object */ u32 m_size; /* Size of the object in bytes */ }; #define m_oid m_htable.h_id /* Ephemeral object id */ static inline bool apfs_is_case_insensitive(void) { extern struct volume_superblock *vsb; return (vsb->v_raw->apfs_incompatible_features & cpu_to_le64(APFS_INCOMPAT_CASE_INSENSITIVE)) != 0; } static inline bool apfs_is_normalization_insensitive(void) { extern struct volume_superblock *vsb; u64 flags = le64_to_cpu(vsb->v_raw->apfs_incompatible_features); if (apfs_is_case_insensitive()) return true; if (flags & APFS_INCOMPAT_NORMALIZATION_INSENSITIVE) return true; return false; } static inline bool apfs_volume_is_sealed(void) { u64 flags = le64_to_cpu(vsb->v_raw->apfs_incompatible_features); return flags & APFS_INCOMPAT_SEALED_VOLUME; } static inline bool apfs_volume_has_extent_prealloc_flag(void) { u64 flags = le64_to_cpu(vsb->v_raw->apfs_incompatible_features); return flags & APFS_INCOMPAT_EXTENT_PREALLOC_FLAG; } static inline bool apfs_volume_is_in_group(void) { u64 features = le64_to_cpu(vsb->v_raw->apfs_features); return features & APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE; } static inline u16 apfs_volume_role(void) { return le16_to_cpu(vsb->v_raw->apfs_role); } static inline bool apfs_is_data_volume_in_group(void) { u16 role = apfs_volume_role(); return apfs_volume_is_in_group() && role == APFS_VOL_ROLE_DATA; } static inline bool apfs_is_system_volume_in_group(void) { u16 role = apfs_volume_role(); return apfs_volume_is_in_group() && role == APFS_VOL_ROLE_SYSTEM; } static inline bool apfs_is_fusion_drive(void) { u64 flags = le64_to_cpu(sb->s_raw->nx_incompatible_features); return flags & APFS_NX_INCOMPAT_FUSION; } /** * uuid_is_null - Check if all bytes of a uuid are zero * @uuid: the uuid to check * * TODO: reuse this for other uuid checks */ static inline bool uuid_is_null(char uuid[16]) { int i; for (i = 0; i < 16; ++i) { if (uuid[i]) return false; } return true; } extern void parse_filesystem(void); extern struct volume_superblock *alloc_volume_super(bool snap); extern void read_volume_super(int vol, struct volume_superblock *vsb, struct object *obj); extern void check_volume_super(void); #endif /* _SUPER_H */ apfsprogs-0.2.0/apfsck/xattr.c000066400000000000000000000162271471277137200163150ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include "apfsck.h" #include "compress.h" #include "extents.h" #include "inode.h" #include "key.h" #include "super.h" #include "xattr.h" /** * check_xattr_flags - Check basic consistency of xattr flags * @flags: the flags */ static void check_xattr_flags(u16 flags) { if ((flags & APFS_XATTR_VALID_FLAGS) != flags) report("Xattr record", "invalid flags in use."); if (flags & APFS_XATTR_RESERVED_8) report("Xattr record", "reserved flag in use."); if (flags & APFS_XATTR_UNKNOWN_10) report_unknown("0x10 xattr flag"); if ((bool)(flags & APFS_XATTR_DATA_STREAM) == (bool)(flags & APFS_XATTR_DATA_EMBEDDED)) report("Xattr record", "must be either embedded or dstream."); } /** * parse_xattr_dstream - Parse a xattr dstream struct and check for corruption * @xstream: the dstream structure * * Returns a pointer to the in-memory dstream structure. */ static struct dstream *parse_xattr_dstream(struct apfs_xattr_dstream *xstream) { struct dstream *dstream; struct apfs_dstream *dstream_raw = &xstream->dstream; u64 id = le64_to_cpu(xstream->xattr_obj_id); u64 size, alloced_size; u64 crypid; size = le64_to_cpu(dstream_raw->size); alloced_size = le64_to_cpu(dstream_raw->alloced_size); crypid = le64_to_cpu(dstream_raw->default_crypto_id); if (crypid && crypid != APFS_CRYPTO_SW_ID) ++get_crypto_state(crypid)->c_references; dstream = get_dstream(id); if (dstream->d_references) { /* A dstream structure for this id has already been seen */ if (dstream->d_obj_type != APFS_TYPE_XATTR) report("Xattr dstream", "shared by inode and xattr."); if (dstream->d_size != size) report("Xattr dstream", "inconsistent size for stream."); if (dstream->d_alloced_size != alloced_size) report("Xattr dstream", "inconsistent allocated size for stream."); } else { dstream->d_obj_type = APFS_TYPE_XATTR; dstream->d_size = size; dstream->d_alloced_size = alloced_size; } dstream->d_references++; return dstream; } /** * parse_decmpfs - Parse the contents of a compression xattr * @inode: inode for the file * @xdata: contents of the xattr * @len: length of @xdata */ static void parse_decmpfs(struct inode *inode, u8 *xdata, int len) { struct compress *compress = inode->i_compress; struct apfs_compress_hdr *hdr = NULL; u32 algo; if (!compress) report("Inode", "is not compressed but has decmpfs xattr."); if (len < sizeof(*hdr)) report("Decmpfs xattr", "too small for the header."); hdr = (struct apfs_compress_hdr *)xdata; if (hdr->signature) report_unknown("Compression signature"); algo = le32_to_cpu(hdr->algo); compress->size = le64_to_cpu(hdr->size); compress->decmpfs = malloc(len); if (!compress->decmpfs) system_error(); memcpy(compress->decmpfs, xdata, len); compress->decmpfs_len = len; switch (algo) { case APFS_COMPRESS_PLAIN_ATTR: case APFS_COMPRESS_ZLIB_ATTR: if (compress->rsrc_dstream) report("Compressed inode", "should not have a resource fork."); if (apfs_volume_is_sealed()) { compress->rsrc_dstream = get_dstream(inode->i_ino); compress->rsrc_dstream->d_inline = true; } break; case APFS_COMPRESS_PLAIN_RSRC: case APFS_COMPRESS_ZLIB_RSRC: case APFS_COMPRESS_LZBITMAP_RSRC: if (len != sizeof(*hdr)) report("Decmpfs xattr", "too big for non-inline."); if (!compress->rsrc_dstream) report("Inode", "should have resource fork for compression."); break; default: report_unknown("Compression algorithm"); } } /** * parse_xattr_record - Parse a xattr record value and check for corruption * @key: pointer to the raw key * @val: pointer to the raw value * @len: length of the raw value * * Internal consistency of @key must be checked before calling this function. */ void parse_xattr_record(struct apfs_xattr_key *key, struct apfs_xattr_val *val, int len) { struct inode *inode; u16 flags; u64 content_len; u64 dstream_id = 0; if (len < sizeof(*val)) report("Xattr record", "value is too small."); len -= sizeof(*val); flags = le16_to_cpu(val->flags); check_xattr_flags(flags); inode = get_inode(cat_cnid(&key->hdr)); if (!inode->i_seen) report("Catalog", "xattr record with no inode."); if (flags & APFS_XATTR_DATA_STREAM) { struct dstream *dstream; struct apfs_xattr_dstream *dstream_raw; if (len != sizeof(*dstream_raw)) report("Xattr record", "bad length for dstream structure."); if (len != le16_to_cpu(val->xdata_len)) /* Never seems to happen, but the docs don't ban it */ report_weird("Xattr data length for dstream structure"); dstream_raw = (struct apfs_xattr_dstream *)val->xdata; dstream = parse_xattr_dstream(dstream_raw); dstream->d_owner = inode->i_ino; dstream->d_xattr = true; if (strcmp((char *)key->name, APFS_XATTR_NAME_RSRC_FORK) == 0 && inode->i_compress) inode->i_compress->rsrc_dstream = dstream; content_len = dstream->d_size; dstream_id = dstream->d_id; } else { if (len != le16_to_cpu(val->xdata_len)) report("Xattr record", "bad length for embedded data."); if (len > APFS_XATTR_MAX_EMBEDDED_SIZE) report("Xattr record", "embedded data is too long."); content_len = len; } if (!strcmp((char *)key->name, APFS_XATTR_NAME_SYMLINK)) { if (!(flags & APFS_XATTR_FILE_SYSTEM_OWNED)) report("Symlink target xattr", "not owned by system."); if (inode->i_xattr_bmap & XATTR_BMAP_SYMLINK) report("Catalog", "two targets for same symlink."); inode->i_xattr_bmap |= XATTR_BMAP_SYMLINK; } else if (!strcmp((char *)key->name, APFS_XATTR_NAME_RSRC_FORK)) { if (flags & APFS_XATTR_FILE_SYSTEM_OWNED) report("Resource fork xattr", "owned by system."); if (inode->i_xattr_bmap & XATTR_BMAP_RSRC_FORK) report("Catalog", "two resource forks for same inode."); inode->i_xattr_bmap |= XATTR_BMAP_RSRC_FORK; } else if (!strcmp((char *)key->name, APFS_XATTR_NAME_SECURITY)) { if (flags & APFS_XATTR_FILE_SYSTEM_OWNED) report("Security xattr", "owned by system."); if (inode->i_xattr_bmap & XATTR_BMAP_SECURITY) report("Catalog", "two security xattrs for one inode."); inode->i_xattr_bmap |= XATTR_BMAP_SECURITY; } else if (!strcmp((char *)key->name, APFS_XATTR_NAME_FINDER_INFO)) { if (flags & APFS_XATTR_FILE_SYSTEM_OWNED) report("Finder info xattr", "owned by system."); if (inode->i_xattr_bmap & XATTR_BMAP_FINDER_INFO) report("Catalog", "two finder info xattrs for one inode."); inode->i_xattr_bmap |= XATTR_BMAP_FINDER_INFO; if (content_len != 32) report("Finder info xattr", "wrong size"); } else if (!strcmp((char *)key->name, APFS_XATTR_NAME_COMPRESSED)) { if (flags & APFS_XATTR_FILE_SYSTEM_OWNED) report("Decmpfs xattr", "owned by system."); if (inode->i_xattr_bmap & XATTR_BMAP_COMPRESSED) report("Catalog", "inode has two compressed headers."); inode->i_xattr_bmap |= XATTR_BMAP_COMPRESSED; if (flags & APFS_XATTR_DATA_STREAM) { u8 *decmpfs = NULL; decmpfs = malloc(content_len); if (!decmpfs) system_error(); read_whole_dstream(dstream_id, decmpfs, content_len); parse_decmpfs(inode, decmpfs, content_len); free(decmpfs); } else { parse_decmpfs(inode, val->xdata, len); } } } apfsprogs-0.2.0/apfsck/xattr.h000066400000000000000000000004771471277137200163220ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _XATTR_H #define _XATTR_H #include #include "inode.h" struct apfs_xattr_key; extern void parse_xattr_record(struct apfs_xattr_key *key, struct apfs_xattr_val *val, int len); #endif /* _XATTR_H */ apfsprogs-0.2.0/include/000077500000000000000000000000001471277137200151535ustar00rootroot00000000000000apfsprogs-0.2.0/include/apfs/000077500000000000000000000000001471277137200161045ustar00rootroot00000000000000apfsprogs-0.2.0/include/apfs/aes.h000066400000000000000000000003661471277137200170320ustar00rootroot00000000000000#ifndef _AES_H #define _AES_H #include int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); int aes_xts_decrypt(const u8 *key1, const u8 *key2, u64 tweak, const u8 *cipher, int len, u8 *plain); #endif /* _AES_H */ apfsprogs-0.2.0/include/apfs/checksum.h000066400000000000000000000004371471277137200200630ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _CHECKSUM_H #define _CHECKSUM_H #include extern u32 crc32c(u32 crc, const void *buf, int size); extern u64 fletcher64(void *addr, unsigned long len); #endif /* _CHECKSUM_H */ apfsprogs-0.2.0/include/apfs/libzbitmap.h000066400000000000000000000053371471277137200204220ustar00rootroot00000000000000#ifndef _LIBZBITMAP_H #define _LIBZBITMAP_H #include #define ZBM_NOMEM (-1) /* Failed to allocate memory */ #define ZBM_INVAL (-2) /* Compressed input is invalid */ #define ZBM_RANGE (-3) /* The destination buffer is too small */ #define ZBM_OVERFLOW (-4) /* Integer overflow from size or index */ /** * zbm_decompress - Decompress an LZBITMAP buffer * @dest: destination buffer (may be NULL) * @dest_size: size of the destination buffer * @src: source buffer * @src_size: size of the source buffer * @out_len: on return, the length of the decompressed output * * May be called with a NULL destination buffer to retrieve the expected length * of the decompressed data. Returns 0 on success, or a negative error code in * case of failure. */ int zbm_decompress(void *dest, size_t dest_size, const void *src, size_t src_size, size_t *out_len); /** * zbm_compress - Compress a whole buffer with LZBITMAP * @dest: destination buffer (may be NULL) * @dest_size: size of the destination buffer * @src: source buffer * @src_size: size of the source buffer * @out_len: on return, the length of the compressed output * * May be called with a NULL destination buffer to retrieve the maximum possible * length of the compressed output. Returns 0 on success, or a negative error * code in case of failure. * * This function just compresses the whole buffer sequentially, using a single * thread. For speed, the caller should consider using zbm_compress_chunk() * instead. */ int zbm_compress(void *dest, size_t dest_size, const void *src, size_t src_size, size_t *out_len); #define ZBM_MAX_CHUNK_SIZE 0x800A #define ZBM_LAST_CHUNK_SIZE 6 /* * zbm_compress_chunk - Compress a single chunk in a buffer with LZBITMAP * @dest: destination buffer * @dest_size: size of the destination buffer * @src: source buffer * @src_size: size of the source buffer * @index: which chunk from @src to compress * @out_len: on return, the length of the compressed output * * Returns 0 on success, or a negative error code in case of failure. * * To compress a whole buffer, this function must be called repeatedly with the * same value of @src and a sequentially increasing value of @index. The process * concludes when @out_len returns ZBM_LAST_CHUNK_SIZE. The size of @dest must * always be ZBM_MAX_CHUNK_SIZE or more. * * The advantage of this over zbm_compress() is that the caller can compress * multiple chunks in parallel. For an example using pthreads, check the test * code in test/test.c. */ int zbm_compress_chunk(void *dest, size_t dest_size, const void *src, size_t src_size, size_t index, size_t *out_len); #endif /* _LIBZBITMAP_H */ apfsprogs-0.2.0/include/apfs/parameters.h000066400000000000000000000004021471277137200204140ustar00rootroot00000000000000/* * Copyright (C) 2021 Ernesto A. Fernández */ #ifndef _PARAMETERS_H #define _PARAMETERS_H #include extern u16 ip_fq_node_limit(u64 chunks); extern u16 main_fq_node_limit(u64 blocks); #endif /* _PARAMETERS_H */ apfsprogs-0.2.0/include/apfs/raw.h000066400000000000000000001212611471277137200170510ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández * * Definitions for all on-disk data structures. */ #ifndef _RAW_H #define _RAW_H #include /* Object identifiers constants */ #define APFS_OID_NX_SUPERBLOCK 1 #define APFS_OID_INVALID 0ULL #define APFS_OID_RESERVED_COUNT 1024 /* Object type masks */ #define APFS_OBJECT_TYPE_MASK 0x0000ffff #define APFS_OBJECT_TYPE_FLAGS_MASK 0xffff0000 #define APFS_OBJ_STORAGETYPE_MASK 0xc0000000 #define APFS_OBJECT_TYPE_FLAGS_DEFINED_MASK 0xf8000000 /* Object types */ #define APFS_OBJECT_TYPE_NX_SUPERBLOCK 0x00000001 #define APFS_OBJECT_TYPE_BTREE 0x00000002 #define APFS_OBJECT_TYPE_BTREE_NODE 0x00000003 #define APFS_OBJECT_TYPE_SPACEMAN 0x00000005 #define APFS_OBJECT_TYPE_SPACEMAN_CAB 0x00000006 #define APFS_OBJECT_TYPE_SPACEMAN_CIB 0x00000007 #define APFS_OBJECT_TYPE_SPACEMAN_BITMAP 0x00000008 #define APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE 0x00000009 #define APFS_OBJECT_TYPE_EXTENT_LIST_TREE 0x0000000a #define APFS_OBJECT_TYPE_OMAP 0x0000000b #define APFS_OBJECT_TYPE_CHECKPOINT_MAP 0x0000000c #define APFS_OBJECT_TYPE_FS 0x0000000d #define APFS_OBJECT_TYPE_FSTREE 0x0000000e #define APFS_OBJECT_TYPE_BLOCKREFTREE 0x0000000f #define APFS_OBJECT_TYPE_SNAPMETATREE 0x00000010 #define APFS_OBJECT_TYPE_NX_REAPER 0x00000011 #define APFS_OBJECT_TYPE_NX_REAP_LIST 0x00000012 #define APFS_OBJECT_TYPE_OMAP_SNAPSHOT 0x00000013 #define APFS_OBJECT_TYPE_EFI_JUMPSTART 0x00000014 #define APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE 0x00000015 #define APFS_OBJECT_TYPE_NX_FUSION_WBC 0x00000016 #define APFS_OBJECT_TYPE_NX_FUSION_WBC_LIST 0x00000017 #define APFS_OBJECT_TYPE_ER_STATE 0x00000018 #define APFS_OBJECT_TYPE_GBITMAP 0x00000019 #define APFS_OBJECT_TYPE_GBITMAP_TREE 0x0000001a #define APFS_OBJECT_TYPE_GBITMAP_BLOCK 0x0000001b #define APFS_OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c #define APFS_OBJECT_TYPE_SNAP_META_EXT 0x0000001d #define APFS_OBJECT_TYPE_INTEGRITY_META 0x0000001e #define APFS_OBJECT_TYPE_FEXT_TREE 0x0000001f #define APFS_OBJECT_TYPE_RESERVED_20 0x00000020 #define APFS_OBJECT_TYPE_INVALID 0x00000000 #define APFS_OBJECT_TYPE_TEST 0x000000ff /* Object type flags */ #define APFS_OBJ_VIRTUAL 0x00000000 #define APFS_OBJ_EPHEMERAL 0x80000000 #define APFS_OBJ_PHYSICAL 0x40000000 #define APFS_OBJ_NOHEADER 0x20000000 #define APFS_OBJ_ENCRYPTED 0x10000000 #define APFS_OBJ_NONPERSISTENT 0x08000000 #define APFS_MAX_CKSUM_SIZE 8 /* * On-disk representation of an APFS object */ struct apfs_obj_phys { /*00*/ __le64 o_cksum; /* Fletcher checksum */ __le64 o_oid; /* Object-id */ /*10*/ __le64 o_xid; /* Transaction ID */ __le32 o_type; /* Object type */ __le32 o_subtype; /* Object subtype */ } __packed; /* Flags for the object map structure */ #define APFS_OMAP_MANUALLY_MANAGED 0x00000001 #define APFS_OMAP_ENCRYPTING 0x00000002 #define APFS_OMAP_DECRYPTING 0x00000004 #define APFS_OMAP_KEYROLLING 0x00000008 #define APFS_OMAP_CRYPTO_GENERATION 0x00000010 #define APFS_OMAP_FLAGS_VALID_MASK (APFS_OMAP_MANUALLY_MANAGED \ | APFS_OMAP_ENCRYPTING \ | APFS_OMAP_DECRYPTING \ | APFS_OMAP_KEYROLLING \ | APFS_OMAP_CRYPTO_GENERATION) /* * On-disk representation of an object map */ struct apfs_omap_phys { /*00*/ struct apfs_obj_phys om_o; /*20*/ __le32 om_flags; __le32 om_snap_count; __le32 om_tree_type; __le32 om_snapshot_tree_type; /*30*/ __le64 om_tree_oid; __le64 om_snapshot_tree_oid; /*40*/ __le64 om_most_recent_snap; __le64 om_pending_revert_min; __le64 om_pending_revert_max; } __packed; /* Object map value flags */ #define APFS_OMAP_VAL_DELETED 0x00000001 #define APFS_OMAP_VAL_SAVED 0x00000002 #define APFS_OMAP_VAL_ENCRYPTED 0x00000004 #define APFS_OMAP_VAL_NOHEADER 0x00000008 #define APFS_OMAP_VAL_CRYPTO_GENERATION 0x00000010 #define APFS_OMAP_VAL_FLAGS_VALID_MASK (APFS_OMAP_VAL_DELETED \ | APFS_OMAP_VAL_SAVED \ | APFS_OMAP_VAL_ENCRYPTED \ | APFS_OMAP_VAL_NOHEADER \ | APFS_OMAP_VAL_CRYPTO_GENERATION) /* * Structure of a value in an object map B-tree */ struct apfs_omap_val { __le32 ov_flags; __le32 ov_size; __le64 ov_paddr; } __packed; /* * Structure of a value in an omap's snapshot tree */ struct apfs_omap_snapshot { __le32 oms_flags; __le32 oms_pad; __le64 oms_oid; } __packed; /* B-tree node flags */ #define APFS_BTNODE_ROOT 0x0001 #define APFS_BTNODE_LEAF 0x0002 #define APFS_BTNODE_FIXED_KV_SIZE 0x0004 #define APFS_BTNODE_HASHED 0x0008 #define APFS_BTNODE_NOHEADER 0x0010 #define APFS_BTNODE_CHECK_KOFF_INVAL 0x8000 #define APFS_BTNODE_MASK 0x001f /* Valid on-disk flags */ /* B-tree location constants */ #define APFS_BTOFF_INVALID 0xffff /* * Structure storing a location inside a B-tree node */ struct apfs_nloc { __le16 off; __le16 len; } __packed; /* * Structure storing the location of a key/value pair within a B-tree node */ struct apfs_kvloc { struct apfs_nloc k; struct apfs_nloc v; } __packed; /* * Structure storing the location of a key/value pair within a B-tree node * having fixed-size key and value (flag APFS_BTNODE_FIXED_KV_SIZE is present) */ struct apfs_kvoff { __le16 k; __le16 v; } __packed; /* * On-disk representation of a B-tree node */ struct apfs_btree_node_phys { /*00*/ struct apfs_obj_phys btn_o; /*20*/ __le16 btn_flags; __le16 btn_level; __le32 btn_nkeys; /*28*/ struct apfs_nloc btn_table_space; struct apfs_nloc btn_free_space; struct apfs_nloc btn_key_free_list; struct apfs_nloc btn_val_free_list; /*38*/ __le64 btn_data[]; } __packed; /* B-tree info flags */ #define APFS_BTREE_UINT64_KEYS 0x00000001 #define APFS_BTREE_SEQUENTIAL_INSERT 0x00000002 #define APFS_BTREE_ALLOW_GHOSTS 0x00000004 #define APFS_BTREE_EPHEMERAL 0x00000008 #define APFS_BTREE_PHYSICAL 0x00000010 #define APFS_BTREE_NONPERSISTENT 0x00000020 #define APFS_BTREE_KV_NONALIGNED 0x00000040 #define APFS_BTREE_HASHED 0x00000080 #define APFS_BTREE_NOHEADER 0x00000100 #define APFS_BTREE_FLAGS_VALID_MASK (APFS_BTREE_UINT64_KEYS \ | APFS_BTREE_SEQUENTIAL_INSERT \ | APFS_BTREE_ALLOW_GHOSTS \ | APFS_BTREE_EPHEMERAL \ | APFS_BTREE_PHYSICAL \ | APFS_BTREE_NONPERSISTENT \ | APFS_BTREE_KV_NONALIGNED \ | APFS_BTREE_HASHED \ | APFS_BTREE_NOHEADER) /* * Structure used to store information about a B-tree that won't change * over time */ struct apfs_btree_info_fixed { __le32 bt_flags; __le32 bt_node_size; __le32 bt_key_size; __le32 bt_val_size; } __packed; /* * Structure used to store information about a B-tree (located at the end of * a B-tree root node block) */ struct apfs_btree_info { struct apfs_btree_info_fixed bt_fixed; __le32 bt_longest_key; /* Longest key ever stored */ __le32 bt_longest_val; /* Longest value ever stored */ __le64 bt_key_count; __le64 bt_node_count; } __packed; /* * Structure of the value of a directory entry. This is the data in * the catalog nodes for record type APFS_TYPE_DIR_REC. */ struct apfs_drec_val { __le64 file_id; __le64 date_added; __le16 flags; u8 xfields[]; } __packed; /* Physical extent records */ #define APFS_PEXT_LEN_MASK 0x0fffffffffffffffULL #define APFS_PEXT_KIND_MASK 0xf000000000000000ULL #define APFS_PEXT_KIND_SHIFT 60 /* The kind of a physical extent record */ enum { APFS_KIND_ANY = 0, APFS_KIND_NEW = 1, APFS_KIND_UPDATE = 2, APFS_KIND_DEAD = 3, APFS_KIND_UPDATE_REFCNT = 4, APFS_KIND_INVALID = 255 /* This is weird, won't fit in 4 bits */ }; #define APFS_OWNING_OBJ_ID_INVALID (~0ULL) #define APFS_OWNING_OBJ_ID_UNKNOWN (~1ULL) /* * Structure of a physical extent record */ struct apfs_phys_ext_val { __le64 len_and_kind; __le64 owning_obj_id; __le32 refcnt; } __packed; /* File extent records */ #define APFS_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL #define APFS_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL #define APFS_FILE_EXTENT_FLAG_SHIFT 56 #define APFS_FILE_EXTENT_CRYPTO_FLAG 0x01 /* Made-up name */ #define APFS_FILE_EXTENT_PREALLOCATED 0x02 /* Made-up name */ #define APFS_VALID_FILE_EXTENT_FLAGS (APFS_FILE_EXTENT_CRYPTO_FLAG \ | APFS_FILE_EXTENT_PREALLOCATED) /* * Structure of a file extent record */ struct apfs_file_extent_val { __le64 len_and_flags; __le64 phys_block_num; __le64 crypto_id; } __packed; /* * Structure of a data stream record */ struct apfs_dstream_id_val { __le32 refcnt; } __packed; #define APFS_CP_MAX_WRAPPEDKEYSIZE 128 /* * Structure used to store the encryption state for PFKs */ struct apfs_wrapped_crypto_state { __le16 major_version; __le16 minor_version; __le32 cpflags; __le32 persistent_class; __le32 key_os_version; __le16 key_revision; __le16 key_len; u8 persistent_key[0]; } __packed; /* * Structure of a crypto state record */ struct apfs_crypto_state_val { __le32 refcnt; struct apfs_wrapped_crypto_state state; } __packed; /* Inode numbers for special inodes */ #define APFS_INVALID_INO_NUM 0 #define APFS_ROOT_DIR_PARENT 1 /* Root directory parent */ #define APFS_ROOT_DIR_INO_NUM 2 /* Root directory */ #define APFS_PRIV_DIR_INO_NUM 3 /* Private directory */ #define APFS_SNAP_DIR_INO_NUM 6 /* Snapshots metadata */ #define APFS_PURGEABLE_DIR_INO_NUM 7 /* Parent of purgeable files */ /* Smallest inode number available for user content */ #define APFS_MIN_USER_INO_NUM 16 #define APFS_UNIFIED_ID_SPACE_MARK 0x0800000000000000 /* Inode internal flags */ #define APFS_INODE_IS_APFS_PRIVATE 0x00000001 #define APFS_INODE_MAINTAIN_DIR_STATS 0x00000002 #define APFS_INODE_DIR_STATS_ORIGIN 0x00000004 #define APFS_INODE_PROT_CLASS_EXPLICIT 0x00000008 #define APFS_INODE_WAS_CLONED 0x00000010 #define APFS_INODE_FLAG_UNUSED 0x00000020 #define APFS_INODE_HAS_SECURITY_EA 0x00000040 #define APFS_INODE_BEING_TRUNCATED 0x00000080 #define APFS_INODE_HAS_FINDER_INFO 0x00000100 #define APFS_INODE_IS_SPARSE 0x00000200 #define APFS_INODE_WAS_EVER_CLONED 0x00000400 #define APFS_INODE_ACTIVE_FILE_TRIMMED 0x00000800 #define APFS_INODE_PINNED_TO_MAIN 0x00001000 #define APFS_INODE_PINNED_TO_TIER2 0x00002000 #define APFS_INODE_HAS_RSRC_FORK 0x00004000 #define APFS_INODE_NO_RSRC_FORK 0x00008000 #define APFS_INODE_ALLOCATION_SPILLEDOVER 0x00010000 #define APFS_INODE_FAST_PROMOTE 0x00020000 #define APFS_INODE_HAS_UNCOMPRESSED_SIZE 0x00040000 #define APFS_INODE_IS_PURGEABLE 0x00080000 #define APFS_INODE_WANTS_TO_BE_PURGEABLE 0x00100000 #define APFS_INODE_IS_SYNC_ROOT 0x00200000 #define APFS_INODE_SNAPSHOT_COW_EXEMPTION 0x00400000 /* This flag is not documented */ #define APFS_INODE_HAS_PURGEABLE_FLAGS 0x02000000 /* Masks for internal flags */ #define APFS_VALID_INTERNAL_INODE_FLAGS (APFS_INODE_IS_APFS_PRIVATE \ | APFS_INODE_MAINTAIN_DIR_STATS \ | APFS_INODE_DIR_STATS_ORIGIN \ | APFS_INODE_PROT_CLASS_EXPLICIT \ | APFS_INODE_WAS_CLONED \ | APFS_INODE_HAS_SECURITY_EA \ | APFS_INODE_BEING_TRUNCATED \ | APFS_INODE_HAS_FINDER_INFO \ | APFS_INODE_IS_SPARSE \ | APFS_INODE_WAS_EVER_CLONED \ | APFS_INODE_ACTIVE_FILE_TRIMMED \ | APFS_INODE_PINNED_TO_MAIN \ | APFS_INODE_PINNED_TO_TIER2 \ | APFS_INODE_HAS_RSRC_FORK \ | APFS_INODE_NO_RSRC_FORK \ | APFS_INODE_ALLOCATION_SPILLEDOVER \ | APFS_INODE_FAST_PROMOTE \ | APFS_INODE_HAS_UNCOMPRESSED_SIZE \ | APFS_INODE_IS_PURGEABLE \ | APFS_INODE_WANTS_TO_BE_PURGEABLE \ | APFS_INODE_IS_SYNC_ROOT \ | APFS_INODE_SNAPSHOT_COW_EXEMPTION \ | APFS_INODE_HAS_PURGEABLE_FLAGS) #define APFS_INODE_INHERITED_INTERNAL_FLAGS (APFS_INODE_MAINTAIN_DIR_STATS) #define APFS_INDOE_CLONED_INTERNAL_FLAGS (APFS_INODE_HAS_RSRC_FORK \ | APFS_INODE_NO_RSRC_FORK \ | APFS_INODE_HAS_FINDER_INFO) #define APFS_INODE_PINNED_MASK (APFS_INODE_PINNED_TO_MAIN \ | APFS_INODE_PINNED_TO_TIER2) /* BSD flags */ #define APFS_INOBSD_NODUMP 0x00000001 #define APFS_INOBSD_IMMUTABLE 0x00000002 #define APFS_INOBSD_APPEND 0x00000004 #define APFS_INOBSD_COMPRESSED 0x00000020 /* * Structure of an inode as stored as a B-tree value */ struct apfs_inode_val { /*00*/ __le64 parent_id; __le64 private_id; /*10*/ __le64 create_time; __le64 mod_time; __le64 change_time; __le64 access_time; /*30*/ __le64 internal_flags; union { __le32 nchildren; __le32 nlink; }; __le32 default_protection_class; /*40*/ __le32 write_generation_counter; __le32 bsd_flags; __le32 owner; __le32 group; /*50*/ __le16 mode; __le16 pad1; __le64 uncompressed_size; /*5C*/ u8 xfields[]; } __packed; /* Extended field types for dentries */ #define APFS_DREC_EXT_TYPE_SIBLING_ID 1 /* Extended field types for inodes */ #define APFS_INO_EXT_TYPE_SNAP_XID 1 #define APFS_INO_EXT_TYPE_DELTA_TREE_OID 2 #define APFS_INO_EXT_TYPE_DOCUMENT_ID 3 #define APFS_INO_EXT_TYPE_NAME 4 #define APFS_INO_EXT_TYPE_PREV_FSIZE 5 #define APFS_INO_EXT_TYPE_RESERVED_6 6 #define APFS_INO_EXT_TYPE_FINDER_INFO 7 #define APFS_INO_EXT_TYPE_DSTREAM 8 #define APFS_INO_EXT_TYPE_RESERVED_9 9 #define APFS_INO_EXT_TYPE_DIR_STATS_KEY 10 #define APFS_INO_EXT_TYPE_FS_UUID 11 #define APFS_INO_EXT_TYPE_RESERVED_12 12 #define APFS_INO_EXT_TYPE_SPARSE_BYTES 13 #define APFS_INO_EXT_TYPE_RDEV 14 #define APFS_INO_EXT_TYPE_PURGEABLE_FLAGS 15 #define APFS_INO_EXT_TYPE_ORIG_SYNC_ROOT_ID 16 /* Extended field flags */ #define APFS_XF_DATA_DEPENDENT 0x01 #define APFS_XF_DO_NOT_COPY 0x02 #define APFS_XF_RESERVED_4 0x04 #define APFS_XF_CHILDREN_INHERIT 0x08 #define APFS_XF_USER_FIELD 0x10 #define APFS_XF_SYSTEM_FIELD 0x20 #define APFS_XF_RESERVED_40 0x40 #define APFS_XF_RESERVED_80 0x80 /* Constants for extended fields */ #define APFS_MIN_DOC_ID 3 /* Smallest not reserved document id */ /* * Structure used to store the number and size of an xfield collection. The * official reference seems to be wrong about @xf_used_data: it's the size of * the xfield values alone, without the metadata. */ struct apfs_xf_blob { __le16 xf_num_exts; __le16 xf_used_data; u8 xf_data[]; } __packed; /* * Structure used to describe an extended field */ struct apfs_x_field { u8 x_type; u8 x_flags; __le16 x_size; } __packed; /* * Structure used to store information about a data stream */ struct apfs_dstream { __le64 size; __le64 alloced_size; __le64 default_crypto_id; __le64 total_bytes_written; __le64 total_bytes_read; } __packed; /* * Structure used to store directory information */ struct apfs_dir_stats_val { __le64 num_children; __le64 total_size; __le64 chained_key; __le64 gen_count; } __packed; /* * Structure of the value for a sibling link record. These are used to * list the hard links for a given inode. */ struct apfs_sibling_val { __le64 parent_id; __le16 name_len; u8 name[0]; } __packed; /* * Structure of the value for a sibling map record. No idea what these are for. */ struct apfs_sibling_map_val { __le64 file_id; } __packed; /* * Structure of a key in an object map B-tree */ struct apfs_omap_key { __le64 ok_oid; __le64 ok_xid; } __packed; /* * Structure of a key in a free-space queue b-tree */ struct apfs_spaceman_free_queue_key { __le64 sfqk_xid; __le64 sfqk_paddr; } __packed; /* Catalog records types */ enum { APFS_TYPE_ANY = 0, APFS_TYPE_SNAP_METADATA = 1, APFS_TYPE_EXTENT = 2, APFS_TYPE_INODE = 3, APFS_TYPE_XATTR = 4, APFS_TYPE_SIBLING_LINK = 5, APFS_TYPE_DSTREAM_ID = 6, APFS_TYPE_CRYPTO_STATE = 7, APFS_TYPE_FILE_EXTENT = 8, APFS_TYPE_DIR_REC = 9, APFS_TYPE_DIR_STATS = 10, APFS_TYPE_SNAP_NAME = 11, APFS_TYPE_SIBLING_MAP = 12, APFS_TYPE_FILE_INFO = 13, APFS_TYPE_MAX_VALID = 13, APFS_TYPE_MAX = 15, APFS_TYPE_INVALID = 15, }; /* Bit masks for the 'obj_id_and_type' field of a key header */ #define APFS_OBJ_ID_MASK 0x0fffffffffffffffULL #define APFS_OBJ_TYPE_MASK 0xf000000000000000ULL #define APFS_OBJ_TYPE_SHIFT 60 /* Key header for filesystem-object keys */ struct apfs_key_header { __le64 obj_id_and_type; } __packed; /* * Structure of the key for a physical extent record */ struct apfs_phys_ext_key { struct apfs_key_header hdr; } __packed; /* * Structure of the key for an inode record */ struct apfs_inode_key { struct apfs_key_header hdr; } __packed; /* * Structure of the key for a file extent record */ struct apfs_file_extent_key { struct apfs_key_header hdr; __le64 logical_addr; } __packed; /* * Structure of the key for a data stream record */ struct apfs_dstream_id_key { struct apfs_key_header hdr; } __packed; /* * Structure of the key for a crypto state record */ struct apfs_crypto_state_key { struct apfs_key_header hdr; } __packed; /* Bit masks for the 'name_len_and_hash' field of a directory entry */ #define APFS_DREC_LEN_MASK 0x000003ff #define APFS_DREC_HASH_MASK 0xfffffc00 #define APFS_DREC_HASH_SHIFT 10 /* The name length in the catalog key counts the terminating null byte. */ #define APFS_NAME_LEN (APFS_DREC_LEN_MASK - 1) /* Bit masks for the 'type' field of a directory entry */ enum { APFS_DREC_TYPE_MASK = 0x000f, APFS_DREC_RESERVED_10 = 0x0010, /* These flags are not documented */ APFS_DREC_PURGEABLE_2 = 0x0200, APFS_DREC_PURGEABLE_8 = 0x0800, }; #define APFS_DREC_PURGEABLE (APFS_DREC_PURGEABLE_2 | APFS_DREC_PURGEABLE_8) /* * Structure of the key for a directory entry - no hash, used on normalization * sensitive volumes */ struct apfs_drec_key { struct apfs_key_header hdr; __le16 name_len; u8 name[0]; } __packed; /* * Structure of the key for a directory entry, including a precomputed * hash of its name */ struct apfs_drec_hashed_key { struct apfs_key_header hdr; __le32 name_len_and_hash; u8 name[0]; } __packed; /* * Structure of the key for an extended attributes record */ struct apfs_xattr_key { struct apfs_key_header hdr; __le16 name_len; u8 name[0]; } __packed; /* * Structure of the key for a snapshot metadata record */ struct apfs_snap_metadata_key { struct apfs_key_header hdr; } __packed; /* * Structure of the key for a snapshot name record */ struct apfs_snap_name_key { struct apfs_key_header hdr; __le16 name_len; u8 name[0]; } __packed; /* * Structure of the key for a sibling link record */ struct apfs_sibling_link_key { struct apfs_key_header hdr; __le64 sibling_id; } __packed; /* * Structure of the key for a siblink map record */ struct apfs_sibling_map_key { struct apfs_key_header hdr; } __packed; /* * On-disk allocation info for a chunk of blocks */ struct apfs_chunk_info { __le64 ci_xid; __le64 ci_addr; __le32 ci_block_count; __le32 ci_free_count; __le64 ci_bitmap_addr; } __packed; /* Constants for the chunk info block */ #define APFS_CI_COUNT_MASK 0x000FFFFF #define APFS_CI_COUNT_RESERVED_MASK 0xFFF00000 /* * Structure of a block with an array of chunk allocation info structures */ struct apfs_chunk_info_block { struct apfs_obj_phys cib_o; __le32 cib_index; __le32 cib_chunk_info_count; struct apfs_chunk_info cib_chunk_info[]; } __packed; /* * Structure of a block with an array of addresses to chunk information blocks */ struct apfs_cib_addr_block { struct apfs_obj_phys cab_o; __le32 cab_index; __le32 cab_cib_count; __le64 cab_cib_addr[]; } __packed; /* * On-disk structure for a free queue */ struct apfs_spaceman_free_queue { __le64 sfq_count; __le64 sfq_tree_oid; __le64 sfq_oldest_xid; __le16 sfq_tree_node_limit; __le16 sfq_pad16; __le32 sfq_pad32; __le64 sfq_reserved; } __packed; /* Indexes for a free queue array */ enum { APFS_SFQ_IP = 0, APFS_SFQ_MAIN = 1, APFS_SFQ_TIER2 = 2, APFS_SFQ_COUNT = 3 }; /* * On-disk structure for device allocation information */ struct apfs_spaceman_device { __le64 sm_block_count; __le64 sm_chunk_count; __le32 sm_cib_count; __le32 sm_cab_count; __le64 sm_free_count; __le32 sm_addr_offset; __le32 sm_reserved; __le64 sm_reserved2; } __packed; /* Indexes for a device array */ enum smdev { APFS_SD_MAIN = 0, APFS_SD_TIER2 = 1, APFS_SD_COUNT = 2 }; /* * On-disk structure to describe allocation zone boundaries */ struct apfs_spaceman_allocation_zone_boundaries { __le64 saz_zone_start; __le64 saz_zone_end; } __packed; /* Allocation zone constants */ #define APFS_SM_ALLOCZONE_INVALID_END_BOUNDARY 0 #define APFS_SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES 7 struct apfs_spaceman_allocation_zone_info_phys { struct apfs_spaceman_allocation_zone_boundaries saz_current_boundaries; struct apfs_spaceman_allocation_zone_boundaries saz_previous_boundaries[APFS_SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES]; __le16 saz_zone_id; __le16 saz_previous_boundary_index; __le32 saz_reserved; } __packed; /* Datazone constants */ #define APFS_SM_DATAZONE_ALLOCZONE_COUNT 8 struct apfs_spaceman_datazone_info_phys { struct apfs_spaceman_allocation_zone_info_phys sdz_allocation_zones[APFS_SD_COUNT][APFS_SM_DATAZONE_ALLOCZONE_COUNT]; } __packed; /* Internal-pool bitmap constants */ #define APFS_SPACEMAN_IP_BM_TX_MULTIPLIER 16 #define APFS_SPACEMAN_IP_BM_INDEX_INVALID 0xFFFF #define APFS_SPACEMAN_IP_BM_BLOCK_COUNT_MAX 0xFFFE /* Space manager flags */ #define APFS_SM_FLAG_VERSIONED 0x00000001 #define APFS_SM_FLAGS_VALID_MASK APFS_SM_FLAG_VERSIONED /* * On-disk structure for the space manager */ struct apfs_spaceman_phys { struct apfs_obj_phys sm_o; __le32 sm_block_size; __le32 sm_blocks_per_chunk; __le32 sm_chunks_per_cib; __le32 sm_cibs_per_cab; struct apfs_spaceman_device sm_dev[APFS_SD_COUNT]; __le32 sm_flags; __le32 sm_ip_bm_tx_multiplier; __le64 sm_ip_block_count; __le32 sm_ip_bm_size_in_blocks; __le32 sm_ip_bm_block_count; __le64 sm_ip_bm_base; __le64 sm_ip_base; __le64 sm_fs_reserve_block_count; __le64 sm_fs_reserve_alloc_count; struct apfs_spaceman_free_queue sm_fq[APFS_SFQ_COUNT]; __le16 sm_ip_bm_free_head; __le16 sm_ip_bm_free_tail; __le32 sm_ip_bm_xid_offset; __le32 sm_ip_bitmap_offset; __le32 sm_ip_bm_free_next_offset; __le32 sm_version; __le32 sm_struct_size; struct apfs_spaceman_datazone_info_phys sm_datazone; } __packed; /* * Structure used to store a range of physical blocks */ struct apfs_prange { __le64 pr_start_paddr; __le64 pr_block_count; } __packed; /* Reaper flags */ #define APFS_NR_BHM_FLAG 0x00000001 #define APFS_NR_CONTINUE 0x00000002 #define APFS_NR_FLAGS_VALID_MASK (APFS_NR_BHM_FLAG | APFS_NR_CONTINUE) /* * On-disk reaper structure */ struct apfs_nx_reaper_phys { struct apfs_obj_phys nr_o; __le64 nr_next_reap_id; __le64 nr_completed_id; __le64 nr_head; __le64 nr_tail; __le32 nr_flags; __le32 nr_rlcount; __le32 nr_type; __le32 nr_size; __le64 nr_fs_oid; __le64 nr_oid; __le64 nr_xid; __le32 nr_nrle_flags; __le32 nr_state_buffer_size; u8 nr_state_buffer[]; } __packed; struct apfs_nx_reap_list_entry { __le32 nrle_next; __le32 nrle_flags; __le32 nrle_type; __le32 nrle_size; __le64 nrle_fs_oid; __le64 nrle_oid; __le64 nrle_xid; } __packed; struct apfs_nx_reap_list_phys { struct apfs_obj_phys nrl_o; __le64 nrl_next; __le32 nrl_flags; __le32 nrl_max; __le32 nrl_count; __le32 nrl_first; __le32 nrl_last; __le32 nrl_free; struct apfs_nx_reap_list_entry nrl_entries[]; } __packed; /* EFI constants */ #define APFS_NX_EFI_JUMPSTART_MAGIC 0x5244534A #define APFS_NX_EFI_JUMPSTART_VERSION 1 /* * Information about the embedded EFI driver */ struct apfs_nx_efi_jumpstart { struct apfs_obj_phys nej_o; __le32 nej_magic; __le32 nej_version; __le32 nej_efi_file_len; __le32 nej_num_extents; __le64 nej_reserved[16]; struct apfs_prange nej_rec_extents[]; } __packed; /* Main container */ /* Container constants */ #define APFS_NX_MAGIC 0x4253584E #define APFS_NX_BLOCK_NUM 0 #define APFS_NX_MAX_FILE_SYSTEMS 100 #define APFS_NX_EPH_INFO_COUNT 4 #define APFS_NX_EPH_MIN_BLOCK_COUNT 8 #define APFS_NX_MAX_FILE_SYSTEM_EPH_STRUCTS 4 #define APFS_NX_TX_MIN_CHECKPOINT_COUNT 4 #define APFS_NX_EPH_INFO_VERSION_1 1 /* Container flags */ #define APFS_NX_RESERVED_1 0x00000001LL #define APFS_NX_RESERVED_2 0x00000002LL #define APFS_NX_CRYPTO_SW 0x00000004LL #define APFS_NX_FLAGS_VALID_MASK (APFS_NX_RESERVED_1 \ | APFS_NX_RESERVED_2 \ | APFS_NX_CRYPTO_SW) /* Optional container feature flags */ #define APFS_NX_FEATURE_DEFRAG 0x0000000000000001ULL #define APFS_NX_FEATURE_LCFD 0x0000000000000002ULL #define APFS_NX_SUPPORTED_FEATURES_MASK (APFS_NX_FEATURE_DEFRAG | \ APFS_NX_FEATURE_LCFD) /* Read-only compatible container feature flags */ #define APFS_NX_SUPPORTED_ROCOMPAT_MASK (0x0ULL) /* Incompatible container feature flags */ #define APFS_NX_INCOMPAT_VERSION1 0x0000000000000001ULL #define APFS_NX_INCOMPAT_VERSION2 0x0000000000000002ULL #define APFS_NX_INCOMPAT_FUSION 0x0000000000000100ULL #define APFS_NX_SUPPORTED_INCOMPAT_MASK (APFS_NX_INCOMPAT_VERSION2 \ | APFS_NX_INCOMPAT_FUSION) /* Block and container sizes */ #define APFS_NX_MINIMUM_BLOCK_SIZE 4096 #define APFS_NX_DEFAULT_BLOCK_SIZE 4096 #define APFS_NX_MAXIMUM_BLOCK_SIZE 65536 #define APFS_NX_MINIMUM_CONTAINER_SIZE 1048576 /* Indexes into a container superblock's array of counters */ enum { APFS_NX_CNTR_OBJ_CKSUM_SET = 0, APFS_NX_CNTR_OBJ_CKSUM_FAIL = 1, APFS_NX_NUM_COUNTERS = 32 }; /* * On-disk representation of the container superblock */ struct apfs_nx_superblock { /*00*/ struct apfs_obj_phys nx_o; /*20*/ __le32 nx_magic; __le32 nx_block_size; __le64 nx_block_count; /*30*/ __le64 nx_features; __le64 nx_readonly_compatible_features; __le64 nx_incompatible_features; /*48*/ char nx_uuid[16]; /*58*/ __le64 nx_next_oid; __le64 nx_next_xid; /*68*/ __le32 nx_xp_desc_blocks; __le32 nx_xp_data_blocks; /*70*/ __le64 nx_xp_desc_base; __le64 nx_xp_data_base; __le32 nx_xp_desc_next; __le32 nx_xp_data_next; /*88*/ __le32 nx_xp_desc_index; __le32 nx_xp_desc_len; __le32 nx_xp_data_index; __le32 nx_xp_data_len; /*98*/ __le64 nx_spaceman_oid; __le64 nx_omap_oid; __le64 nx_reaper_oid; /*B0*/ __le32 nx_test_type; __le32 nx_max_file_systems; /*B8*/ __le64 nx_fs_oid[APFS_NX_MAX_FILE_SYSTEMS]; /*3D8*/ __le64 nx_counters[APFS_NX_NUM_COUNTERS]; /*4D8*/ struct apfs_prange nx_blocked_out_prange; __le64 nx_evict_mapping_tree_oid; /*4F0*/ __le64 nx_flags; __le64 nx_efi_jumpstart; /*500*/ char nx_fusion_uuid[16]; struct apfs_prange nx_keylocker; /*520*/ __le64 nx_ephemeral_info[APFS_NX_EPH_INFO_COUNT]; /*540*/ __le64 nx_test_oid; __le64 nx_fusion_mt_oid; /*550*/ __le64 nx_fusion_wbc_oid; struct apfs_prange nx_fusion_wbc; __le64 nx_newest_mounted_version; /*570*/ struct apfs_prange nx_mkb_locker; } __packed; /* * A mapping from an ephemeral object id to its physical address */ struct apfs_checkpoint_mapping { __le32 cpm_type; __le32 cpm_subtype; __le32 cpm_size; __le32 cpm_pad; __le64 cpm_fs_oid; __le64 cpm_oid; __le64 cpm_paddr; } __packed; /* Checkpoint flags */ #define APFS_CHECKPOINT_MAP_LAST 0x00000001 /* * A checkpoint-mapping block */ struct apfs_checkpoint_map_phys { struct apfs_obj_phys cpm_o; __le32 cpm_flags; __le32 cpm_count; struct apfs_checkpoint_mapping cpm_map[]; } __packed; /* Volume */ /* Volume constants */ #define APFS_MAGIC 0x42535041 #define APFS_MAX_HIST 8 #define APFS_VOLNAME_LEN 256 /* Volume flags */ #define APFS_FS_UNENCRYPTED 0x00000001LL #define APFS_FS_EFFACEABLE 0x00000002LL #define APFS_FS_RESERVED_4 0x00000004LL #define APFS_FS_ONEKEY 0x00000008LL #define APFS_FS_SPILLEDOVER 0x00000010LL #define APFS_FS_RUN_SPILLOVER_CLEANER 0x00000020LL #define APFS_FS_ALWAYS_CHECK_EXTENTREF 0x00000040LL #define APFS_FS_PREVIOUSLY_SEALED 0x00000080LL /* Made-up name */ #define APFS_FS_PFK 0x00000100LL /* Made-up name */ #define APFS_FS_UNKNOWN_200 0x00000200LL #define APFS_FS_FLAGS_VALID_MASK (APFS_FS_UNENCRYPTED \ | APFS_FS_EFFACEABLE \ | APFS_FS_RESERVED_4 \ | APFS_FS_ONEKEY \ | APFS_FS_SPILLEDOVER \ | APFS_FS_RUN_SPILLOVER_CLEANER \ | APFS_FS_ALWAYS_CHECK_EXTENTREF \ | APFS_FS_PREVIOUSLY_SEALED \ | APFS_FS_PFK \ | APFS_FS_UNKNOWN_200) #define APFS_FS_CRYPTOFLAGS (APFS_FS_UNENCRYPTED \ | APFS_FS_EFFACEABLE \ | APFS_FS_ONEKEY) /* Volume roles */ #define APFS_VOLUME_ENUM_SHIFT 6 #define APFS_VOL_ROLE_NONE 0x0000 #define APFS_VOL_ROLE_SYSTEM 0x0001 #define APFS_VOL_ROLE_USER 0x0002 #define APFS_VOL_ROLE_RECOVERY 0x0004 #define APFS_VOL_ROLE_VM 0x0008 #define APFS_VOL_ROLE_PREBOOT 0x0010 #define APFS_VOL_ROLE_INSTALLER 0x0020 #define APFS_VOL_ROLE_DATA (1 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_BASEBAND (2 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_UPDATE (3 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_XART (4 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_HARDWARE (5 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_BACKUP (6 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_RESERVED_7 (7 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_RESERVED_8 (8 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_ENTERPRISE (9 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_RESERVED_10 (10 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLE_PRELOGIN (11 << APFS_VOLUME_ENUM_SHIFT) #define APFS_VOL_ROLES_VALID_MASK (APFS_VOL_ROLE_SYSTEM \ | APFS_VOL_ROLE_USER \ | APFS_VOL_ROLE_RECOVERY \ | APFS_VOL_ROLE_VM \ | APFS_VOL_ROLE_PREBOOT \ | APFS_VOL_ROLE_INSTALLER \ | APFS_VOL_ROLE_DATA \ | APFS_VOL_ROLE_BASEBAND \ | APFS_VOL_ROLE_UPDATE \ | APFS_VOL_ROLE_XART \ | APFS_VOL_ROLE_HARDWARE \ | APFS_VOL_ROLE_BACKUP \ | APFS_VOL_ROLE_RESERVED_7 \ | APFS_VOL_ROLE_RESERVED_8 \ | APFS_VOL_ROLE_ENTERPRISE \ | APFS_VOL_ROLE_RESERVED_10 \ | APFS_VOL_ROLE_PRELOGIN) /* Optional volume feature flags */ #define APFS_FEATURE_DEFRAG_PRERELEASE 0x00000001LL #define APFS_FEATURE_HARDLINK_MAP_RECORDS 0x00000002LL #define APFS_FEATURE_DEFRAG 0x00000004LL #define APFS_FEATURE_STRICTATIME 0x00000008LL #define APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE 0x00000010LL #define APFS_SUPPORTED_FEATURES_MASK (APFS_FEATURE_DEFRAG \ | APFS_FEATURE_DEFRAG_PRERELEASE \ | APFS_FEATURE_HARDLINK_MAP_RECORDS \ | APFS_FEATURE_STRICTATIME \ | APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE) /* Read-only compatible volume feature flags */ #define APFS_SUPPORTED_ROCOMPAT_MASK (0x0ULL) /* Incompatible volume feature flags */ #define APFS_INCOMPAT_CASE_INSENSITIVE 0x00000001LL #define APFS_INCOMPAT_DATALESS_SNAPS 0x00000002LL #define APFS_INCOMPAT_ENC_ROLLED 0x00000004LL #define APFS_INCOMPAT_NORMALIZATION_INSENSITIVE 0x00000008LL #define APFS_INCOMPAT_INCOMPLETE_RESTORE 0x00000010LL #define APFS_INCOMPAT_SEALED_VOLUME 0x00000020LL #define APFS_INCOMPAT_PFK 0x00000040LL /* Made-up name */ #define APFS_INCOMPAT_EXTENT_PREALLOC_FLAG 0x00000080LL /* Made-up name */ #define APFS_INCOMPAT_SECONDARY_FSROOT 0x00000100LL /* Made-up name */ #define APFS_SUPPORTED_INCOMPAT_MASK (APFS_INCOMPAT_CASE_INSENSITIVE \ | APFS_INCOMPAT_DATALESS_SNAPS \ | APFS_INCOMPAT_ENC_ROLLED \ | APFS_INCOMPAT_NORMALIZATION_INSENSITIVE \ | APFS_INCOMPAT_INCOMPLETE_RESTORE \ | APFS_INCOMPAT_SEALED_VOLUME \ | APFS_INCOMPAT_PFK \ | APFS_INCOMPAT_EXTENT_PREALLOC_FLAG \ | APFS_INCOMPAT_SECONDARY_FSROOT) #define APFS_MODIFIED_NAMELEN 32 /* * Structure containing information about a program that modified the volume */ struct apfs_modified_by { u8 id[APFS_MODIFIED_NAMELEN]; __le64 timestamp; __le64 last_xid; } __packed; /* Version constants for wrapped meta crypto state */ #define APFS_WMCS_MAJOR_VERSION 5 #define APFS_WMCS_MINOR_VERSION 0 /* Protection classes */ #define APFS_PROTECTION_CLASS_DIR_NONE 0 /* Inherits the directory's default */ #define APFS_PROTECTION_CLASS_A 1 #define APFS_PROTECTION_CLASS_B 2 #define APFS_PROTECTION_CLASS_C 3 #define APFS_PROTECTION_CLASS_D 4 /* No protection */ #define APFS_PROTECTION_CLASS_F 6 /* No protection, nonpersistent key */ /* Encryption identifiers */ #define APFS_CRYPTO_SW_ID 4 #define APFS_CRYPTO_RESERVED_5 5 #define APFS_UNASSIGNED_CRYPTO_ID (~0ULL) /* Doc id index flags. I'm making up the names for now. */ #define APFS_DOC_ID_HAS_PREV_TREE 0x00000001 #define APFS_DOC_ID_UNKNOWN_02 0x00000002 #define APFS_DOC_ID_UNKNOWN_04 0x00000004 #define APFS_DOC_ID_UNKNOWN_08 0x00000008 #define APFS_DOC_ID_UNKNOWN_10 0x00000010 #define APFS_DOC_ID_VALID_FLAGS (APFS_DOC_ID_HAS_PREV_TREE \ | APFS_DOC_ID_UNKNOWN_02 \ | APFS_DOC_ID_UNKNOWN_04 \ | APFS_DOC_ID_UNKNOWN_08 \ | APFS_DOC_ID_UNKNOWN_10) /* * Structure used to store the encryption state */ struct apfs_wrapped_meta_crypto_state { __le16 major_version; __le16 minor_version; __le32 cpflags; __le32 persistent_class; __le32 key_os_version; __le16 key_revision; __le16 unused; } __packed; /* * On-disk representation of a volume superblock */ struct apfs_superblock { /*00*/ struct apfs_obj_phys apfs_o; /*20*/ __le32 apfs_magic; __le32 apfs_fs_index; /*28*/ __le64 apfs_features; __le64 apfs_readonly_compatible_features; __le64 apfs_incompatible_features; /*40*/ __le64 apfs_unmount_time; __le64 apfs_fs_reserve_block_count; __le64 apfs_fs_quota_block_count; __le64 apfs_fs_alloc_count; /*60*/ struct apfs_wrapped_meta_crypto_state apfs_meta_crypto; /*74*/ __le32 apfs_root_tree_type; __le32 apfs_extentref_tree_type; __le32 apfs_snap_meta_tree_type; /*80*/ __le64 apfs_omap_oid; __le64 apfs_root_tree_oid; __le64 apfs_extentref_tree_oid; __le64 apfs_snap_meta_tree_oid; /*A0*/ __le64 apfs_revert_to_xid; __le64 apfs_revert_to_sblock_oid; /*B0*/ __le64 apfs_next_obj_id; /*B8*/ __le64 apfs_num_files; __le64 apfs_num_directories; __le64 apfs_num_symlinks; __le64 apfs_num_other_fsobjects; __le64 apfs_num_snapshots; /*E0*/ __le64 apfs_total_blocks_alloced; __le64 apfs_total_blocks_freed; /*F0*/ char apfs_vol_uuid[16]; /*100*/ __le64 apfs_last_mod_time; __le64 apfs_fs_flags; /*110*/ struct apfs_modified_by apfs_formatted_by; /*140*/ struct apfs_modified_by apfs_modified_by[APFS_MAX_HIST]; /*2C0*/ u8 apfs_volname[APFS_VOLNAME_LEN]; /*3C0*/ __le32 apfs_next_doc_id; __le16 apfs_role; __le16 reserved; /*3C8*/ __le64 apfs_root_to_xid; __le64 apfs_er_state_oid; __le64 apfs_cloneinfo_id_epoch; __le64 apfs_cloneinfo_xid; __le64 apfs_snap_meta_ext_oid; /*3F0*/ char apfs_volume_group_id[16]; /*400*/ __le64 apfs_integrity_meta_oid; __le64 apfs_fext_tree_oid; /*410*/ __le32 apfs_fext_tree_type; __le32 reserved_type; __le64 reserved_oid; /*420*/ __le64 apfs_doc_id_index_xid; __le32 apfs_doc_id_index_flags; __le32 apfs_doc_id_tree_type; /*430*/ __le64 apfs_doc_id_tree_oid; /* Made-up name */ __le64 apfs_prev_doc_id_tree_oid; __le64 apfs_doc_id_fixup_cursor; __le64 apfs_sec_root_tree_oid; /*450*/ __le32 apfs_sec_root_tree_type; } __packed; /* Extended attributes constants */ #define APFS_XATTR_MAX_EMBEDDED_SIZE 3804 /* Extended attributes names */ #define APFS_XATTR_NAME_SYMLINK "com.apple.fs.symlink" #define APFS_XATTR_NAME_COMPRESSED "com.apple.decmpfs" #define APFS_XATTR_NAME_RSRC_FORK "com.apple.ResourceFork" #define APFS_XATTR_NAME_SECURITY "com.apple.system.Security" #define APFS_XATTR_NAME_FINDER_INFO "com.apple.FinderInfo" /* Extended attributes flags */ enum { APFS_XATTR_DATA_STREAM = 0x00000001, APFS_XATTR_DATA_EMBEDDED = 0x00000002, APFS_XATTR_FILE_SYSTEM_OWNED = 0x00000004, APFS_XATTR_RESERVED_8 = 0x00000008, APFS_XATTR_UNKNOWN_10 = 0x00000010, }; #define APFS_XATTR_VALID_FLAGS 0x0000001f /* * Structure of the value of an extended attributes record */ struct apfs_xattr_val { __le16 flags; __le16 xdata_len; u8 xdata[0]; } __packed; /* * Structure used to store the data of an extended attributes record */ struct apfs_xattr_dstream { __le64 xattr_obj_id; struct apfs_dstream dstream; } __packed; /* * Structure of the value of a snapshot metadata record */ struct apfs_snap_metadata_val { __le64 extentref_tree_oid; __le64 sblock_oid; __le64 create_time; __le64 change_time; __le64 inum; __le32 extentref_tree_type; __le32 flags; __le16 name_len; u8 name[0]; } __packed; /* * Structure of the value of a snapshot name record */ struct apfs_snap_name_val { __le64 snap_xid; } __packed; /* * Structure of the extended snapshot metadata */ struct apfs_snap_meta_ext { struct apfs_obj_phys sme_o; __le32 sme_version; __le32 sme_flags; __le64 sme_snap_xid; char sme_uuid[16]; __le64 sme_token; } __packed; #define APFS_OBJECT_TYPE_KEYBAG 0x6b657973 /* Spells 'syek' */ #define APFS_VOL_KEYBAG_ENTRY_MAX_SIZE 512 #define APFS_FV_PERSONAL_RECOVERY_KEY_UUID "EBC6C064-0000-11AA-AA11-00306543ECAC" /* Keybag entry types */ enum { KB_TAG_UNKNOWN = 0, KB_TAG_RESERVED_1 = 1, KB_TAG_VOLUME_KEY = 2, KB_TAG_VOLUME_UNLOCK_RECORDS = 3, KB_TAG_VOLUME_PASSPHRASE_HINT = 4, KB_TAG_WRAPPING_M_KEY = 5, KB_TAG_VOLUME_M_KEY = 6, KB_TAG_RESERVED_F8 = 0xF8 }; /* * Structure of a single entry in the keybag */ struct apfs_keybag_entry { char ke_uuid[16]; __le16 ke_tag; __le16 ke_keylen; __le32 padding; u8 ke_keydata[0]; } __packed; #define APFS_KEYBAG_VERSION 2 /* * Structure of the locker in the keybag */ struct apfs_kb_locker { __le16 kl_version; __le16 kl_nkeys; __le32 kl_nbytes; __le64 padding; struct apfs_keybag_entry kl_entries[0]; } __packed; /* * Integrity metadata for a sealed volume */ struct apfs_integrity_meta_phys { struct apfs_obj_phys im_o; __le32 im_version; __le32 im_flags; __le32 im_hash_type; __le32 im_root_hash_offset; __le64 im_broken_xid; __le64 im_reserved[9]; } __packed; /* * Version numbers for the integrity metadata structure */ enum { APFS_INTEGRITY_META_VERSION_INVALID = 0, APFS_INTEGRITY_META_VERSION_1 = 1, APFS_INTEGRITY_META_VERSION_2 = 2, APFS_INTEGRITY_META_VERSION_HIGHEST = APFS_INTEGRITY_META_VERSION_2 }; /* Flags used by integrity metadata */ #define APFS_SEAL_BROKEN (1U << 0) /* * Constants used to identify hash algorithms */ enum { APFS_HASH_INVALID = 0, APFS_HASH_SHA256 = 0x1, APFS_HASH_SHA512_256 = 0x2, APFS_HASH_SHA384 = 0x3, APFS_HASH_SHA512 = 0x4, APFS_HASH_MIN = APFS_HASH_SHA256, APFS_HASH_MAX = APFS_HASH_SHA512, APFS_HASH_DEFAULT = APFS_HASH_SHA256, }; #define APFS_HASH_CCSHA256_SIZE 32 #define APFS_HASH_CCSHA512_256_SIZE 32 #define APFS_HASH_CCSHA384_SIZE 48 #define APFS_HASH_CCSHA512_SIZE 64 #define APFS_HASH_MAX_SIZE 64 /* * Structure of a key in a fext tree */ struct apfs_fext_tree_key { __le64 private_id; __le64 logical_addr; } __packed; /* * Structure of a value in a fext tree */ struct apfs_fext_tree_val { __le64 len_and_flags; __le64 phys_block_num; } __packed; /* * Structure of the key for a file info record */ struct apfs_file_info_key { struct apfs_key_header hdr; __le64 info_and_lba; } __packed; #define APFS_FILE_INFO_LBA_MASK 0x00ffffffffffffffULL #define APFS_FILE_INFO_TYPE_MASK 0xff00000000000000ULL #define APFS_FILE_INFO_TYPE_SHIFT 56 /* * A hash of file data */ struct apfs_file_data_hash_val { __le16 hashed_len; u8 hash_size; u8 hash[0]; } __packed; #define APFS_FILE_INFO_DATA_HASH 1 /* * Structure of the value for a file info record */ struct apfs_file_info_val { union { struct apfs_file_data_hash_val dhash; }; } __packed; #define APFS_BTREE_NODE_HASH_SIZE_MAX 64 /* * Structure of the value of an index record for a hashed catalog tree */ struct apfs_btn_index_node_val { __le64 binv_child_oid; /* * The reference seems to be wrong about the hash size, at least for * SHA-256. TODO: check what happens with other hash functions. */ u8 binv_child_hash[APFS_HASH_CCSHA256_SIZE]; } __packed; /* * Compressed file header */ struct apfs_compress_hdr { __le32 signature; __le32 algo; __le64 size; } __packed; #define APFS_COMPRESS_ZLIB_ATTR 3 #define APFS_COMPRESS_ZLIB_RSRC 4 #define APFS_COMPRESS_PLAIN_ATTR 9 #define APFS_COMPRESS_PLAIN_RSRC 10 #define APFS_COMPRESS_LZBITMAP_RSRC 14 struct apfs_compress_rsrc_hdr { __be32 data_offs; __be32 mgmt_offs; __be32 data_size; __be32 mgmt_size; } __packed; #define APFS_COMPRESS_BLOCK 0x10000 struct apfs_compress_rsrc_data { __le32 unknown; __le32 num; struct apfs_compress_rsrc_block { __le32 offs; __le32 size; } __packed block[0]; } __packed; struct apfs_fusion_wbc_phys { struct apfs_obj_phys fwp_objHdr; __le64 fwp_version; __le64 fwp_listHeadOid; __le64 fwp_listTailOid; __le64 fwp_stableHeadOffset; __le64 fwp_stableTailOffset; __le32 fwp_listBlocksCount; __le32 fwp_reserved; __le64 fwp_usedByRC; struct apfs_prange fwp_rcStash;; } __packed; struct apfs_fusion_wbc_list_entry { __le64 fwle_wbcLba; __le64 fwle_targetLba; __le64 fwle_length; } __packed; struct apfs_fusion_wbc_list_phys { struct apfs_obj_phys fwlp_objHdr; __le64 fwlp_version; __le64 fwlp_tailOffset; __le32 fwlp_indexBegin; __le32 fwlp_indexEnd; __le32 fwlp_indexMax; __le32 fwlp_reserved; struct apfs_fusion_wbc_list_entry fwlp_listEntries[]; } __packed; #define APFS_FUSION_TIER2_DEVICE_BYTE_ADDR 0x4000000000000000ULL /* * Structure of a key in a fusion middle-tree */ struct apfs_fusion_mt_key { __le64 fmk_paddr; } __packed; /* * Structure of a value in a fusion middle-tree */ struct apfs_fusion_mt_val { __le64 fmv_lba; __le32 fmv_length; __le32 fmv_flags; } __packed; /* Flags for the fusion middle-tree */ #define APFS_FUSION_MT_DIRTY (1 << 0) #define APFS_FUSION_MT_TENANT (1 << 1) #define APFS_FUSION_MT_ALLFLAGS (FUSION_MT_DIRTY | FUSION_MT_TENANT) #endif /* _RAW_H */ apfsprogs-0.2.0/include/apfs/sha256.h000066400000000000000000000041521471277137200172670ustar00rootroot00000000000000/*- * Copyright (c) 2001-2003 Allan Saddi * 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. * * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS 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 ALLAN SADDI OR HIS 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. * * $Id: sha256.h 348 2003-02-23 22:12:06Z asaddi $ */ #ifndef _SHA256_H #define _SHA256_H #include #define SHA256_HASH_SIZE 32 /* Hash size in 32-bit words */ #define SHA256_HASH_WORDS 8 struct _SHA256Context { uint64_t totalLength; uint32_t hash[SHA256_HASH_WORDS]; uint32_t bufferLength; union { uint32_t words[16]; uint8_t bytes[64]; } buffer; #ifdef RUNTIME_ENDIAN int littleEndian; #endif /* RUNTIME_ENDIAN */ }; typedef struct _SHA256Context SHA256_CTX; #ifdef __cplusplus extern "C" { #endif void sha256_init (SHA256_CTX *sc); void sha256_update (SHA256_CTX *sc, const void *data, uint32_t len); void sha256_final (SHA256_CTX *sc, uint8_t hash[SHA256_HASH_SIZE]); #ifdef __cplusplus } #endif #endif /* !_SHA256_H */ apfsprogs-0.2.0/include/apfs/types.h000066400000000000000000000030511471277137200174200ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández * * Definitions that make it easier to reuse code from the kernel module. */ #ifndef _TYPES_H #define _TYPES_H #define __CHECK_ENDIAN__ /* Required by linux/types.h when running sparse */ #include #include #include #include #include #define EAGAIN 1 #define ENODATA 2 #define ENOMEM 3 #define EINVAL 4 #define __packed __attribute__((packed)) #ifdef __CHECKER__ #define __force __attribute__((force)) #else /* __CHECKER */ #define __force #endif /* __CHECKER__ */ #define likely(x) __builtin_expect(!!(x), 1) typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #define cpu_to_le16(x) ((__force __le16)(u16)(x)) #define le16_to_cpu(x) ((__force u16)(__le16)(x)) #define cpu_to_le32(x) ((__force __le32)(u32)(x)) #define le32_to_cpu(x) ((__force u32)(__le32)(x)) #define cpu_to_le64(x) ((__force __le64)(u64)(x)) #define le64_to_cpu(x) ((__force u64)(__le64)(x)) static inline uint32_t be32_to_cpu(__be32 in) { uint32_t out = 0; uint8_t *in_p = (uint8_t *)∈ uint8_t *out_p = (uint8_t *)&out; out_p[0] = in_p[3]; out_p[1] = in_p[2]; out_p[2] = in_p[1]; out_p[3] = in_p[0]; return out; } #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define __ROUND_MASK(x, y) ((__typeof__(x))((y)-1)) #define ROUND_UP(x, y) ((((x)-1) | __ROUND_MASK(x, y))+1) #define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) #define NSEC_PER_SEC 1000000000L typedef u32 unicode_t; #endif /* _TYPES_H */ apfsprogs-0.2.0/include/apfs/unicode.h000066400000000000000000000013611471277137200177040ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _UNICODE_H #define _UNICODE_H #include /* * This structure helps normalize_next() to retrieve one normalized * (and case-folded) UTF-32 character at a time from a UTF-8 string. */ struct unicursor { const char *utf8curr; /* Start of UTF-8 to decompose and reorder */ int length; /* Length of normalization until next starter */ int last_pos; /* Offset in substring of last char returned */ u8 last_ccc; /* CCC of the last character returned */ }; extern void init_unicursor(struct unicursor *cursor, const char *utf8str); extern unicode_t normalize_next(struct unicursor *cursor, bool case_fold); #endif /* _UNICODE_H */ apfsprogs-0.2.0/include/apfs/zlib_inflate/000077500000000000000000000000001471277137200205465ustar00rootroot00000000000000apfsprogs-0.2.0/include/apfs/zlib_inflate/inffast.h000066400000000000000000000006231471277137200223520ustar00rootroot00000000000000/* inffast.h -- header to use inffast.c * Copyright (C) 1995-2003 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ void inflate_fast (z_streamp strm, unsigned start); apfsprogs-0.2.0/include/apfs/zlib_inflate/inffixed.h000066400000000000000000000143071471277137200225200ustar00rootroot00000000000000 /* inffixed.h -- table for decoding fixed codes * Generated automatically by makefixed(). */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, {0,9,255} }; static const code distfix[32] = { {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, {22,5,193},{64,5,0} }; apfsprogs-0.2.0/include/apfs/zlib_inflate/inflate.h000066400000000000000000000130241471277137200223410ustar00rootroot00000000000000#ifndef INFLATE_H #define INFLATE_H /* inflate.h -- internal inflate state definition * Copyright (C) 1995-2004 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Possible inflate modes between inflate() calls */ typedef enum { HEAD, /* i: waiting for magic header */ FLAGS, /* i: waiting for method and flags (gzip) */ TIME, /* i: waiting for modification time (gzip) */ OS, /* i: waiting for extra flags and operating system (gzip) */ EXLEN, /* i: waiting for extra length (gzip) */ EXTRA, /* i: waiting for extra bytes (gzip) */ NAME, /* i: waiting for end of file name (gzip) */ COMMENT, /* i: waiting for end of comment (gzip) */ HCRC, /* i: waiting for header crc (gzip) */ DICTID, /* i: waiting for dictionary check value */ DICT, /* waiting for inflateSetDictionary() call */ TYPE, /* i: waiting for type bits, including last-flag bit */ TYPEDO, /* i: same, but skip check to exit inflate on new block */ STORED, /* i: waiting for stored size (length and complement) */ COPY, /* i/o: waiting for input or output to copy stored block */ TABLE, /* i: waiting for dynamic block table lengths */ LENLENS, /* i: waiting for code length code lengths */ CODELENS, /* i: waiting for length/lit and distance code lengths */ LEN, /* i: waiting for length/lit code */ LENEXT, /* i: waiting for length extra bits */ DIST, /* i: waiting for distance code */ DISTEXT, /* i: waiting for distance extra bits */ MATCH, /* o: waiting for output space to copy string */ LIT, /* o: waiting for output space to write literal */ CHECK, /* i: waiting for 32-bit check value */ LENGTH, /* i: waiting for 32-bit length (gzip) */ DONE, /* finished check, done -- remain here until reset */ BAD, /* got a data error -- remain here until reset */ MEM, /* got an inflate() memory error -- remain here until reset */ SYNC /* looking for synchronization bytes to restart inflate() */ } inflate_mode; /* State transitions between above modes - (most modes can go to the BAD or MEM mode -- not shown for clarity) Process header: HEAD -> (gzip) or (zlib) (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME NAME -> COMMENT -> HCRC -> TYPE (zlib) -> DICTID or TYPE DICTID -> DICT -> TYPE Read deflate blocks: TYPE -> STORED or TABLE or LEN or CHECK STORED -> COPY -> TYPE TABLE -> LENLENS -> CODELENS -> LEN Read deflate codes: LEN -> LENEXT or LIT or TYPE LENEXT -> DIST -> DISTEXT -> MATCH -> LEN LIT -> LEN Process trailer: CHECK -> LENGTH -> DONE */ /* state maintained between inflate() calls. Approximately 7K bytes. */ struct inflate_state { inflate_mode mode; /* current inflate mode */ int last; /* true if processing last block */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ int havedict; /* true if dictionary provided */ int flags; /* gzip header method and flags (0 if zlib) */ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ unsigned long check; /* protected copy of check value */ unsigned long total; /* protected copy of output count */ /* gz_headerp head; */ /* where to save gzip header information */ /* sliding window */ unsigned wbits; /* log base 2 of requested window size */ unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned write; /* window write index */ unsigned char *window; /* allocated sliding window, if needed */ /* bit accumulator */ unsigned long hold; /* input bit accumulator */ unsigned bits; /* number of bits in "in" */ /* for string and stored block copying */ unsigned length; /* literal or length of data to copy */ unsigned offset; /* distance back to copy string from */ /* for table and code decoding */ unsigned extra; /* extra bits needed */ /* fixed and dynamic code tables */ code const *lencode; /* starting table for length/literal codes */ code const *distcode; /* starting table for distance codes */ unsigned lenbits; /* index bits for lencode */ unsigned distbits; /* index bits for distcode */ /* dynamic table building */ unsigned ncode; /* number of code length code lengths */ unsigned nlen; /* number of length code lengths */ unsigned ndist; /* number of distance code lengths */ unsigned have; /* number of code lengths in lens[] */ code *next; /* next available space in codes[] */ unsigned short lens[320]; /* temporary storage for code lengths */ unsigned short work[288]; /* work area for code table building */ code codes[ENOUGH]; /* space for code tables */ }; #endif apfsprogs-0.2.0/include/apfs/zlib_inflate/inftrees.h000066400000000000000000000045371471277137200225470ustar00rootroot00000000000000#ifndef INFTREES_H #define INFTREES_H /* inftrees.h -- header to use inftrees.c * Copyright (C) 1995-2005 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* Structure for decoding tables. Each entry provides either the information needed to do the operation requested by the code that indexed that table entry, or it provides a pointer to another table that indexes more bits of the code. op indicates whether the entry is a pointer to another table, a literal, a length or distance, an end-of-block, or an invalid code. For a table pointer, the low four bits of op is the number of index bits of that table. For a length or distance, the low four bits of op is the number of extra bits to get after the code. bits is the number of bits in this code or part of the code to drop off of the bit buffer. val is the actual byte to output in the case of a literal, the base length or distance, or the offset from the current table to the next table. Each entry is four bytes. */ typedef struct { unsigned char op; /* operation, extra bits, table bits */ unsigned char bits; /* bits in this part of the code */ unsigned short val; /* offset in table or code value */ } code; /* op values as set by inflate_table(): 00000000 - literal 0000tttt - table link, tttt != 0 is the number of table index bits 0001eeee - length or distance, eeee is the number of extra bits 01100000 - end of block 01000000 - invalid code */ /* Maximum size of dynamic tree. The maximum found in a long but non- exhaustive search was 1444 code structures (852 for length/literals and 592 for distances, the latter actually the result of an exhaustive search). The true maximum is not known, but the value below is more than safe. */ #define ENOUGH 2048 #define MAXD 592 /* Type of code to build for inftable() */ typedef enum { CODES, LENS, DISTS } codetype; extern int zlib_inflate_table (codetype type, unsigned short *lens, unsigned codes, code **table, unsigned *bits, unsigned short *work); #endif apfsprogs-0.2.0/include/apfs/zlib_inflate/infutil.h000066400000000000000000000012121471277137200223650ustar00rootroot00000000000000/* infutil.h -- types and macros common to blocks and codes * Copyright (C) 1995-1998 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ #ifndef _INFUTIL_H #define _INFUTIL_H #include "zlib.h" /* memory allocation for inflation */ struct inflate_workspace { struct inflate_state inflate_state; unsigned char working_window[1 << MAX_WBITS]; }; #define WS(z) ((struct inflate_workspace *)(z->workspace)) #endif apfsprogs-0.2.0/include/apfs/zlib_inflate/zconf.h000066400000000000000000000033511471277137200220400ustar00rootroot00000000000000/* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-1998 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef _ZCONF_H #define _ZCONF_H /* The memory requirements for deflate are (in bytes): (1 << (windowBits+2)) + (1 << (memLevel+9)) that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) plus a few kilobytes for small objects. For example, if you want to reduce the default memory requirements from 256K to 128K, compile with make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" Of course this will generally degrade compression (there's no free lunch). The memory requirements for inflate are (in bytes) 1 << windowBits that is, 32K for windowBits=15 (default value) plus a few kilobytes for small objects. */ /* Maximum value for memLevel in deflateInit2 */ #ifndef MAX_MEM_LEVEL # define MAX_MEM_LEVEL 8 #endif /* Maximum value for windowBits in deflateInit2 and inflateInit2. * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files * created by gzip. (Files created by minigzip can still be extracted by * gzip.) */ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif /* default windowBits for decompression. MAX_WBITS is for compression only */ #ifndef DEF_WBITS # define DEF_WBITS MAX_WBITS #endif /* default memLevel */ #if MAX_MEM_LEVEL >= 8 # define DEF_MEM_LEVEL 8 #else # define DEF_MEM_LEVEL MAX_MEM_LEVEL #endif /* Type declarations */ typedef unsigned char Byte; /* 8 bits */ typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ typedef void *voidp; #endif /* _ZCONF_H */ apfsprogs-0.2.0/include/apfs/zlib_inflate/zlib.h000066400000000000000000000677511471277137200216770ustar00rootroot00000000000000/* zlib.h -- interface of the 'zlib' general purpose compression library Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). */ #ifndef _ZLIB_H #define _ZLIB_H #include "../types.h" #include "zconf.h" /* zlib deflate based on ZLIB_VERSION "1.1.3" */ /* zlib inflate based on ZLIB_VERSION "1.2.3" */ /* This is a modified version of zlib for use inside the Linux kernel. The main changes are to perform all memory allocation in advance. Inflation Changes: * Z_PACKET_FLUSH is added and used by ppp_deflate. Before returning this checks there is no more input data available and the next data is a STORED block. It also resets the mode to be read for the next data, all as per PPP requirements. * Addition of zlib_inflateIncomp which copies incompressible data into the history window and adjusts the accoutning without calling zlib_inflate itself to inflate the data. */ /* The 'zlib' compression library provides in-memory compression and decompression functions, including integrity checks of the uncompressed data. This version of the library supports only one compression method (deflation) but other algorithms will be added later and will have the same stream interface. Compression can be done in a single step if the buffers are large enough (for example if an input file is mmap'ed), or can be done by repeated calls of the compression function. In the latter case, the application must provide more input and/or consume the output (providing more output space) before each call. The compressed data format used by default by the in-memory functions is the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped around a deflate stream, which is itself documented in RFC 1951. The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio. The zlib format was designed to be compact and fast for use in memory and on communications channels. The gzip format was designed for single- file compression on file systems, has a larger header than zlib to maintain directory information, and uses a different, slower check method than zlib. The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in case of corrupted input. */ struct internal_state; typedef struct z_stream_s { const Byte *next_in; /* next input byte */ uLong avail_in; /* number of bytes available at next_in */ uLong total_in; /* total nb of input bytes read so far */ Byte *next_out; /* next output byte should be put there */ uLong avail_out; /* remaining free space at next_out */ uLong total_out; /* total nb of bytes output so far */ char *msg; /* last error message, NULL if no error */ struct internal_state *state; /* not visible by applications */ void *workspace; /* memory allocated for this stream */ int data_type; /* best guess about the data type: ascii or binary */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ } z_stream; typedef z_stream *z_streamp; /* The application must update next_in and avail_in when avail_in has dropped to zero. It must update next_out and avail_out when avail_out has dropped to zero. The application must initialize zalloc, zfree and opaque before calling the init function. All other fields are set by the compression library and must not be updated by the application. The opaque value provided by the application will be passed as the first parameter for calls of zalloc and zfree. This can be useful for custom memory management. The compression library attaches no meaning to the opaque value. zalloc must return NULL if there is not enough memory for the object. If zlib is used in a multi-threaded application, zalloc and zfree must be thread safe. On 16-bit systems, the functions zalloc and zfree must be able to allocate exactly 65536 bytes, but will not be required to allocate more than this if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers returned by zalloc for objects of exactly 65536 bytes *must* have their offset normalized to zero. The default allocation function provided by this library ensures this (see zutil.c). To reduce memory requirements and avoid any allocation of 64K objects, at the expense of compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). The fields total_in and total_out can be used for statistics or progress reports. After compression, total_in holds the total size of the uncompressed data and may be saved for use in the decompressor (particularly if the decompressor wants to decompress everything in a single step). */ /* constants */ #define Z_NO_FLUSH 0 #define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ #define Z_PACKET_FLUSH 2 #define Z_SYNC_FLUSH 3 #define Z_FULL_FLUSH 4 #define Z_FINISH 5 #define Z_BLOCK 6 /* Only for inflate at present */ /* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 #define Z_NEED_DICT 2 #define Z_ERRNO (-1) #define Z_STREAM_ERROR (-2) #define Z_DATA_ERROR (-3) #define Z_MEM_ERROR (-4) #define Z_BUF_ERROR (-5) #define Z_VERSION_ERROR (-6) /* Return codes for the compression/decompression functions. Negative * values are errors, positive values are used for special but normal events. */ #define Z_NO_COMPRESSION 0 #define Z_BEST_SPEED 1 #define Z_BEST_COMPRESSION 9 #define Z_DEFAULT_COMPRESSION (-1) /* compression levels */ #define Z_FILTERED 1 #define Z_HUFFMAN_ONLY 2 #define Z_DEFAULT_STRATEGY 0 /* compression strategy; see deflateInit2() below for details */ #define Z_BINARY 0 #define Z_ASCII 1 #define Z_UNKNOWN 2 /* Possible values of the data_type field */ #define Z_DEFLATED 8 /* The deflate compression method (the only one supported in this version) */ /* basic functions */ extern int zlib_deflate_workspacesize (int windowBits, int memLevel); /* Returns the number of bytes that needs to be allocated for a per- stream workspace with the specified parameters. A pointer to this number of bytes should be returned in stream->workspace before you call zlib_deflateInit() or zlib_deflateInit2(). If you call zlib_deflateInit(), specify windowBits = MAX_WBITS and memLevel = MAX_MEM_LEVEL here. If you call zlib_deflateInit2(), the windowBits and memLevel parameters passed to zlib_deflateInit2() must not exceed those passed here. */ /* extern int deflateInit (z_streamp strm, int level); Initializes the internal stream state for compression. The fields zalloc, zfree and opaque must be initialized before by the caller. If zalloc and zfree are set to NULL, deflateInit updates them to use default allocation functions. The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION requests a default compromise between speed and compression (currently equivalent to level 6). deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if level is not a valid compression level, Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible with the version assumed by the caller (ZLIB_VERSION). msg is set to null if there is no error message. deflateInit does not perform any compression: this will be done by deflate(). */ extern int zlib_deflate (z_streamp strm, int flush); /* deflate compresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. deflate performs one or both of the following actions: - Compress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in and avail_in are updated and processing will resume at this point for the next call of deflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. This action is forced if the parameter flush is non zero. Forcing flush frequently degrades the compression ratio, so this parameter should be set only when necessary (in interactive applications). Some output may be provided even if flush is not set. Before the call of deflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating avail_in or avail_out accordingly; avail_out should never be zero before the call. The application can consume the compressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. If the parameter flush is set to Z_SYNC_FLUSH, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. (In particular avail_in is zero after the call if enough output space has been provided before the call.) Flushing may degrade compression for some compression algorithms and so it should be used only when necessary. If flush is set to Z_FULL_FLUSH, all output is flushed as with Z_SYNC_FLUSH, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using Z_FULL_FLUSH too often can seriously degrade the compression. If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out). If the parameter flush is set to Z_FINISH, pending input is processed, pending output is flushed and deflate returns with Z_STREAM_END if there was enough output space; if deflate returns with Z_OK, this function must be called again with Z_FINISH and more output space (updated avail_out) but no more input data, until it returns with Z_STREAM_END or an error. After deflate has returned Z_STREAM_END, the only possible operations on the stream are deflateReset or deflateEnd. Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least 0.1% larger than avail_in plus 12 bytes. If deflate does not return Z_STREAM_END, then it must be called again as described above. deflate() sets strm->adler to the adler32 checksum of all input read so far (that is, total_in bytes). deflate() may update data_type if it can make a good guess about the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered binary. This field is only for information purposes and does not affect the compression algorithm in any manner. deflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if all input has been consumed and all output has been produced (only when flush is set to Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible (for example avail_in or avail_out was zero). */ extern int zlib_deflateEnd (z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent, Z_DATA_ERROR if the stream was freed prematurely (some input or output was discarded). In the error case, msg may be set but then points to a static string (which must not be deallocated). */ extern int zlib_inflate_workspacesize (void); /* Returns the number of bytes that needs to be allocated for a per- stream workspace. A pointer to this number of bytes should be returned in stream->workspace before calling zlib_inflateInit(). */ /* extern int zlib_inflateInit (z_streamp strm); Initializes the internal stream state for decompression. The fields next_in, avail_in, and workspace must be initialized before by the caller. If next_in is not NULL and avail_in is large enough (the exact value depends on the compression method), inflateInit determines the compression method from the zlib header and allocates all data structures accordingly; otherwise the allocation will be deferred to the first call of inflate. If zalloc and zfree are set to NULL, inflateInit updates them to use default allocation functions. inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_VERSION_ERROR if the zlib library version is incompatible with the version assumed by the caller. msg is set to null if there is no error message. inflateInit does not perform any decompression apart from reading the zlib header if present: this will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unchanged.) */ extern int zlib_inflate (z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input buffer becomes empty or the output buffer becomes full. It may introduce some output latency (reading input without producing any output) except when forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: - Decompress more input starting at next_in and update next_in and avail_in accordingly. If not all input can be processed (because there is not enough room in the output buffer), next_in is updated and processing will resume at this point for the next call of inflate(). - Provide more output starting at next_out and update next_out and avail_out accordingly. inflate() provides as much output as possible, until there is no more input data or no more space in the output buffer (see below about the flush parameter). Before the call of inflate(), the application should ensure that at least one of the actions is possible, by providing more input and/or consuming more output, and updating the next_* and avail_* values accordingly. The application can consume the uncompressed output when it wants, for example when the output buffer is full (avail_out == 0), or after each call of inflate(). If inflate returns Z_OK and with zero avail_out, it must be called again after making room in the output buffer because there might be more output pending. The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much output as possible to the output buffer. Z_BLOCK requests that inflate() stop if and when it gets to the next deflate block boundary. When decoding the zlib or gzip format, this will cause inflate() to return immediately after the header and before the first block. When doing a raw inflate, inflate() will go ahead and process the first block, and will return when it gets to the end of that block, or when it runs out of data. The Z_BLOCK option assists in appending to or combining deflate streams. Also to assist in this, on return inflate() will set strm->data_type to the number of unused bits in the last byte taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate stream. The end-of-block will not be indicated until all of the uncompressed data from that block has been written to strm->next_out. The number of unused bits may in general be greater than seven, except when bit 7 of data_type is set, in which case the number of unused bits will be less than eight. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; avail_out must be large enough to hold all the uncompressed data. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The next operation on this stream must be inflateEnd to deallocate the decompression state. The use of Z_FINISH is never required, but can be used to inform inflate that a faster approach may be used for the single inflate() call. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the first call. So the only effect of the flush parameter in this implementation is on the return value of inflate(), as noted below, or when it returns early because Z_BLOCK is used. If a preset dictionary is needed after this call (see inflateSetDictionary below), inflate sets strm->adler to the adler32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets strm->adler to the adler32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed adler32 checksum is equal to that saved by the compressor and returns Z_STREAM_END only if the checksum is correct. inflate() will decompress and check either zlib-wrapped or gzip-wrapped deflate data. The header type is detected automatically. Any information contained in the gzip header is not retained, so applications that need that information should instead use raw inflate, see inflateInit2() below, or inflateBack() and perform their own processing of the gzip header and trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was corrupted (input stream not conforming to the zlib format or incorrect check value), Z_STREAM_ERROR if the stream structure was inconsistent (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no progress is possible or if there was not enough room in the output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing. If Z_DATA_ERROR is returned, the application may then call inflateSync() to look for a good compression block if a partial recovery of the data is desired. */ extern int zlib_inflateEnd (z_streamp strm); /* All dynamically allocated data structures for this stream are freed. This function discards any unprocessed input and does not flush any pending output. inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state was inconsistent. In the error case, msg may be set but then points to a static string (which must not be deallocated). */ /* Advanced functions */ /* The following functions are needed only in some special applications. */ /* extern int deflateInit2 (z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy); This is another version of deflateInit with more compression options. The fields next_in, zalloc, zfree and opaque must be initialized before by the caller. The method parameter is the compression method. It must be Z_DEFLATED in this version of the library. The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if deflateInit is used instead. The memLevel parameter specifies how much memory should be allocated for the internal compression state. memLevel=1 uses minimum memory but is slow and reduces compression ratio; memLevel=9 uses maximum memory for optimal speed. The default value is 8. See zconf.h for total memory usage as a function of windowBits and memLevel. The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string match). Filtered data consists mostly of small values with a somewhat random distribution. In this case, the compression algorithm is tuned to compress them better. The effect of Z_FILTERED is to force more Huffman coding and less string matching; it is somewhat intermediate between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid method). msg is set to null if there is no error message. deflateInit2 does not perform any compression: this will be done by deflate(). */ extern int zlib_deflateReset (z_streamp strm); /* This function is equivalent to deflateEnd followed by deflateInit, but does not free and reallocate all the internal compression state. The stream will keep the same compression level and any other attributes that may have been set by deflateInit2. deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being NULL). */ static inline unsigned long deflateBound(unsigned long s) { return s + ((s + 7) >> 3) + ((s + 63) >> 6) + 11; } /* extern int inflateInit2 (z_streamp strm, int windowBits); This is another version of inflateInit with an extra parameter. The fields next_in, avail_in, zalloc, zfree and opaque must be initialized before by the caller. The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used instead. windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing, or it must be equal to 15 if deflateInit2() was not used. If a compressed stream with a larger window size is given as input, inflate() will return with the error code Z_DATA_ERROR instead of trying to allocate a larger window. windowBits can also be -8..-15 for raw inflate. In this case, -windowBits determines the window size. inflate() will then process raw deflate data, not looking for a zlib or gzip header, not generating a check value, and not looking for any check values for comparison at the end of the stream. This is for use with other formats that use the deflate compressed data format such as zip. Those formats provide their own check values. If a custom format is developed using the raw deflate format for compressed data, it is recommended that a check value such as an adler32 or a crc32 be applied to the uncompressed data as is done in the zlib, gzip, and zip formats. For most applications, the zlib format should be used as is. Note that comments above on the use in deflateInit2() applies to the magnitude of windowBits. windowBits can also be greater than 15 for optional gzip decoding. Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection, or add 16 to decode only the gzip format (the zlib format will return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a crc32 instead of an adler32. inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg is set to null if there is no error message. inflateInit2 does not perform any decompression apart from reading the zlib header if present: this will be done by inflate(). (So next_in and avail_in may be modified, but next_out and avail_out are unchanged.) */ extern int zlib_inflateReset (z_streamp strm); /* This function is equivalent to inflateEnd followed by inflateInit, but does not free and reallocate all the internal decompression state. The stream will keep attributes that may have been set by inflateInit2. inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent (such as zalloc or state being NULL). */ extern int zlib_inflateIncomp (z_stream *strm); /* This function adds the data at next_in (avail_in bytes) to the output history without performing any output. There must be no pending output, and the decompressor must be expecting to see the start of a block. Calling this function is equivalent to decompressing a stored block containing the data at next_in (except that the data is not output). */ #define zlib_deflateInit(strm, level) \ zlib_deflateInit2((strm), (level), Z_DEFLATED, MAX_WBITS, \ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) #define zlib_inflateInit(strm) \ zlib_inflateInit2((strm), DEF_WBITS) extern int zlib_deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy); extern int zlib_inflateInit2(z_streamp strm, int windowBits); #if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) struct internal_state {int dummy;}; /* hack for buggy compilers */ #endif /* Utility function: initialize zlib, unpack binary blob, clean up zlib, * return len or negative error code. */ extern int zlib_inflate_blob(void *dst, unsigned dst_sz, const void *src, unsigned src_sz); #endif /* _ZLIB_H */ apfsprogs-0.2.0/include/apfs/zlib_inflate/zutil.h000066400000000000000000000052571471277137200220770ustar00rootroot00000000000000/* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-1998 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ /* @(#) $Id: zutil.h,v 1.1 2000/01/01 03:32:23 davem Exp $ */ #ifndef _Z_UTIL_H #define _Z_UTIL_H #include "zlib.h" typedef unsigned char uch; typedef unsigned short ush; typedef unsigned long ulg; /* common constants */ #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 /* The three kinds of block type */ #define MIN_MATCH 3 #define MAX_MATCH 258 /* The minimum and maximum match lengths */ #define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ /* target dependencies */ /* Common defaults */ #ifndef OS_CODE # define OS_CODE 0x03 /* assume Unix */ #endif /* functions */ typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); /* checksum functions */ #define BASE 65521L /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ #define DO1(buf,i) {s1 += buf[i]; s2 += s1;} #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); /* ========================================================================= */ /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and return the updated checksum. If buf is NULL, this function returns the required initial value for the checksum. An Adler-32 checksum is almost as reliable as a CRC32 but can be computed much faster. Usage example: uLong adler = zlib_adler32(0L, NULL, 0); while (read_buffer(buffer, length) != EOF) { adler = zlib_adler32(adler, buffer, length); } if (adler != original_adler) error(); */ static inline uLong zlib_adler32(uLong adler, const Byte *buf, uInt len) { unsigned long s1 = adler & 0xffff; unsigned long s2 = (adler >> 16) & 0xffff; int k; if (buf == NULL) return 1L; while (len > 0) { k = len < NMAX ? len : NMAX; len -= k; while (k >= 16) { DO16(buf); buf += 16; k -= 16; } if (k != 0) do { s1 += *buf++; s2 += s1; } while (--k); s1 %= BASE; s2 %= BASE; } return (s2 << 16) | s1; } #endif /* _Z_UTIL_H */ apfsprogs-0.2.0/lib/000077500000000000000000000000001471277137200142765ustar00rootroot00000000000000apfsprogs-0.2.0/lib/Makefile000066400000000000000000000010321471277137200157320ustar00rootroot00000000000000SRCS = aes.c checksum.c libzbitmap.c parameters.c sha256.c unicode.c SRCS += $(wildcard zlib_inflate/*.c) OBJS = $(SRCS:.c=.o) DEPS = $(SRCS:.c=.d) SPARSE_VERSION := $(shell sparse --version 2>/dev/null) override CFLAGS += -Wall -fno-strict-aliasing -I$(CURDIR)/../include libapfs.a: $(OBJS) @echo ' Assembling library archive...' @$(AR) rcs $@ $^ %.o: %.c @echo ' Compiling $<...' @$(CC) $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) libapfs.a apfsprogs-0.2.0/lib/aes.c000066400000000000000000001523601471277137200152210ustar00rootroot00000000000000/* * AES and RFC 3394 key wrapping implementations ripped from wpa_supplicant, * packed into a single file and modified slightly to build here. * * Also includes an XTS decryption implementation, very similar to the one * from apfs-fuse . */ #include #include #include #include /* * AES (Rijndael) cipher * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #define AES_SMALL_TABLES #define RCON(i) ((u32) rcons[(i)] << 24) static inline u32 rotr(u32 val, int bits) { return (val >> bits) | (val << (32 - bits)); } #define TE0(i) Te0[((i) >> 24) & 0xff] #define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) #define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) #define TE3(i) rotr(Te0[(i) & 0xff], 24) #define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) #define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) #define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) #define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) #define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) #define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) #define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) #define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) #define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) #define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) #define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) #define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) #define TD0(i) Td0[((i) >> 24) & 0xff] #define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) #define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) #define TD3(i) rotr(Td0[(i) & 0xff], 24) #define TD41(i) ((u32) Td4s[((i) >> 24) & 0xff] << 24) #define TD42(i) ((u32) Td4s[((i) >> 16) & 0xff] << 16) #define TD43(i) ((u32) Td4s[((i) >> 8) & 0xff] << 8) #define TD44(i) ((u32) Td4s[(i) & 0xff]) #define TD0_(i) Td0[(i) & 0xff] #define TD1_(i) rotr(Td0[(i) & 0xff], 8) #define TD2_(i) rotr(Td0[(i) & 0xff], 16) #define TD3_(i) rotr(Td0[(i) & 0xff], 24) #define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) #define PUTU32(ct, st) { \ (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } #define AES_PRIV_SIZE (4 * 4 * 15 + 4) #define AES_PRIV_NR_POS (4 * 15) /* * AES (Rijndael) cipher * * Modifications to public domain implementation: * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ /* * rijndael-alg-fst.c * * @version 3.0 (December 2000) * * Optimised ANSI C code for the Rijndael cipher (now AES) * * @author Vincent Rijmen * @author Antoon Bosselaers * @author Paulo Barreto * * This code is hereby placed in the public domain. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''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 AUTHORS 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. */ /* Te0[x] = S [x].[02, 01, 01, 03]; Te1[x] = S [x].[03, 02, 01, 01]; Te2[x] = S [x].[01, 03, 02, 01]; Te3[x] = S [x].[01, 01, 03, 02]; Te4[x] = S [x].[01, 01, 01, 01]; Td0[x] = Si[x].[0e, 09, 0d, 0b]; Td1[x] = Si[x].[0b, 0e, 09, 0d]; Td2[x] = Si[x].[0d, 0b, 0e, 09]; Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ static const u32 Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; #ifndef AES_SMALL_TABLES const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; const u32 Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; const u32 Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; const u32 Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; #endif /* AES_SMALL_TABLES */ static const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; #ifndef AES_SMALL_TABLES const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; const u32 Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; const u32 Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; const u32 Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #else /* AES_SMALL_TABLES */ static const u8 Td4s[256] = { 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, }; static const u8 rcons[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #endif /* AES_SMALL_TABLES */ /** * Expand the cipher key into the encryption key schedule. * * @return the number of rounds for the given cipher key size. */ static int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) { int i; u32 temp; rk[0] = GETU32(cipherKey ); rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); if (keyBits == 128) { for (i = 0; i < 10; i++) { temp = rk[3]; rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; rk += 4; } return 10; } rk[4] = GETU32(cipherKey + 16); rk[5] = GETU32(cipherKey + 20); if (keyBits == 192) { for (i = 0; i < 8; i++) { temp = rk[5]; rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[7] = rk[1] ^ rk[6]; rk[8] = rk[2] ^ rk[7]; rk[9] = rk[3] ^ rk[8]; if (i == 7) return 12; rk[10] = rk[4] ^ rk[9]; rk[11] = rk[5] ^ rk[10]; rk += 6; } } rk[6] = GETU32(cipherKey + 24); rk[7] = GETU32(cipherKey + 28); if (keyBits == 256) { for (i = 0; i < 7; i++) { temp = rk[7]; rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ RCON(i); rk[9] = rk[1] ^ rk[8]; rk[10] = rk[2] ^ rk[9]; rk[11] = rk[3] ^ rk[10]; if (i == 6) return 14; temp = rk[11]; rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ TE433(temp) ^ TE444(temp); rk[13] = rk[5] ^ rk[12]; rk[14] = rk[6] ^ rk[13]; rk[15] = rk[7] ^ rk[14]; rk += 8; } } return -1; } /* * AES (Rijndael) cipher - decrypt * * Modifications to public domain implementation: * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ /** * Expand the cipher key into the decryption key schedule. * * @return the number of rounds for the given cipher key size. */ static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits) { int Nr, i, j; u32 temp; /* expand the cipher key: */ Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); if (Nr < 0) return Nr; /* invert the order of the round keys: */ for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; } /* apply the inverse MixColumn transform to all round keys but the * first and the last: */ for (i = 1; i < Nr; i++) { rk += 4; for (j = 0; j < 4; j++) { rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ TD1_(TE4((rk[j] >> 16) & 0xff)) ^ TD2_(TE4((rk[j] >> 8) & 0xff)) ^ TD3_(TE4((rk[j] ) & 0xff)); } } return Nr; } static void * aes_decrypt_init(const u8 *key, size_t len) { u32 *rk; int res; rk = malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; res = rijndaelKeySetupDec(rk, key, len * 8); if (res < 0) { free(rk); return NULL; } rk[AES_PRIV_NR_POS] = res; return rk; } static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16], u8 pt[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(ct ) ^ rk[0]; s1 = GETU32(ct + 4) ^ rk[1]; s2 = GETU32(ct + 8) ^ rk[2]; s3 = GETU32(ct + 12) ^ rk[3]; #define ROUND(i,d,s) \ d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] #ifdef FULL_UNROLL ROUND(1,t,s); ROUND(2,s,t); ROUND(3,t,s); ROUND(4,s,t); ROUND(5,t,s); ROUND(6,s,t); ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); if (Nr > 10) { ROUND(10,s,t); ROUND(11,t,s); if (Nr > 12) { ROUND(12,s,t); ROUND(13,t,s); } } rk += Nr << 2; #else /* !FULL_UNROLL */ /* Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { ROUND(1,t,s); rk += 8; if (--r == 0) break; ROUND(0,s,t); } #endif /* ?FULL_UNROLL */ #undef ROUND /* * apply last round and * map cipher state to byte array block: */ s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; PUTU32(pt , s0); s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; PUTU32(pt + 4, s1); s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; PUTU32(pt + 8, s2); s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; PUTU32(pt + 12, s3); } static void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { u32 *rk = ctx; rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); } static void aes_decrypt_deinit(void *ctx) { memset(ctx, 0, AES_PRIV_SIZE); free(ctx); } /* * AES (Rijndael) cipher - encrypt * * Modifications to public domain implementation: * - cleanup * - use C pre-processor to make it easier to change S table access * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(pt ) ^ rk[0]; s1 = GETU32(pt + 4) ^ rk[1]; s2 = GETU32(pt + 8) ^ rk[2]; s3 = GETU32(pt + 12) ^ rk[3]; #define ROUND(i,d,s) \ d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] #ifdef FULL_UNROLL ROUND(1,t,s); ROUND(2,s,t); ROUND(3,t,s); ROUND(4,s,t); ROUND(5,t,s); ROUND(6,s,t); ROUND(7,t,s); ROUND(8,s,t); ROUND(9,t,s); if (Nr > 10) { ROUND(10,s,t); ROUND(11,t,s); if (Nr > 12) { ROUND(12,s,t); ROUND(13,t,s); } } rk += Nr << 2; #else /* !FULL_UNROLL */ /* Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { ROUND(1,t,s); rk += 8; if (--r == 0) break; ROUND(0,s,t); } #endif /* ?FULL_UNROLL */ #undef ROUND /* * apply last round and * map cipher state to byte array block: */ s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; PUTU32(ct , s0); s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; PUTU32(ct + 4, s1); s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; PUTU32(ct + 8, s2); s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUTU32(ct + 12, s3); } static void * aes_encrypt_init(const u8 *key, size_t len) { u32 *rk; int res; rk = malloc(AES_PRIV_SIZE); if (rk == NULL) return NULL; res = rijndaelKeySetupEnc(rk, key, len * 8); if (res < 0) { free(rk); return NULL; } rk[AES_PRIV_NR_POS] = res; return rk; } static int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { u32 *rk = ctx; rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); return 0; } static void aes_encrypt_deinit(void *ctx) { memset(ctx, 0, AES_PRIV_SIZE); free(ctx); } /* * AES key unwrap (128-bit KEK, RFC3394) * * Copyright (c) 2003-2007, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ /** * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) * @kek: Key encryption key (KEK) * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 * bytes * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits * @plain: Plaintext key, n * 64 bits * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) */ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) { u8 a[8], *r, b[16]; int i, j; void *ctx; /* 1) Initialize variables. */ memcpy(a, cipher, 8); r = plain; memcpy(r, cipher + 8, 8 * n); ctx = aes_decrypt_init(kek, 16); if (ctx == NULL) return -1; /* 2) Compute intermediate values. * For j = 5 to 0 * For i = n to 1 * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i * A = MSB(64, B) * R[i] = LSB(64, B) */ for (j = 5; j >= 0; j--) { r = plain + (n - 1) * 8; for (i = n; i >= 1; i--) { memcpy(b, a, 8); b[7] ^= n * j + i; memcpy(b + 8, r, 8); aes_decrypt(ctx, b, b); memcpy(a, b, 8); memcpy(r, b + 8, 8); r -= 8; } } aes_decrypt_deinit(ctx); /* 3) Output results. * * These are already in @plain due to the location of temporary * variables. Just verify that the IV matches with the expected value. */ for (i = 0; i < 8; i++) { if (a[i] != 0xa6) return -1; } return 0; } /* * After reading the XTS implementation from apfs-fuse , * I tried to make my own independent implementation in case I ever needed to * use it for proprietary stuff. I failed because I had the original too fresh * in my mind, so I gave up and even copied some stuff verbatim. Keep in mind * this is all GPLv2 code. * * This is only for little-endian archs, like all code in the fsck. */ static void gf_mul(uint64_t *tweak) { uint8_t c1; uint8_t c2; c1 = (tweak[0] & 0x8000000000000000ULL) ? 1 : 0; c2 = (tweak[1] & 0x8000000000000000ULL) ? 0x87 : 0; tweak[0] = (tweak[0] << 1) ^ c2; tweak[1] = (tweak[1] << 1) | c1; } static void xor128(const u8 *num1, const u8 *num2, u8 *result) { int i; for (i = 0; i < 16; ++i) result[i] = num1[i] ^ num2[i]; } /** * aes_xts_decrypt - Decrypt a single APFS block using XTS-AES * @key1: the primary 128-bit encryption key * @key2: the 128-bit encryption key for the tweak * @tweak: the initial tweak value * @cipher: the ciphertext block * @len: length of @cipher in bytes * @plain: buffer for the plaintext * * Returns 0 on success or -1 on failure. */ int aes_xts_decrypt(const u8 *key1, const u8 *key2, u64 tweak, const u8 *cipher, int len, u8 *plain) { void *ctx1 = NULL, *ctx2 = NULL; int i, sector_count; int ret = -1; ctx1 = aes_decrypt_init(key1, 16); ctx2 = aes_encrypt_init(key2, 16); if (!ctx1 || !ctx2) goto fail; sector_count = len / 512; for (i = 0; i < sector_count; ++i) { u8 enc_tweak[16] = {0}; int j; *(__le64 *)enc_tweak = cpu_to_le64(tweak); aes_encrypt(ctx2, enc_tweak, enc_tweak); for (j = 0; j < 512 / 16; ++j) { int off = i * 512 + j * 16; u8 pp[16], cc[16]; xor128(cipher + off, enc_tweak, cc); aes_decrypt(ctx1, cc, pp); xor128(pp, enc_tweak, plain + off); gf_mul((uint64_t *)enc_tweak); } tweak++; } ret = 0; fail: if (ctx2) aes_encrypt_deinit(ctx2); if (ctx1) aes_decrypt_deinit(ctx1); return ret; } apfsprogs-0.2.0/lib/checksum.c000066400000000000000000000105561471277137200162530ustar00rootroot00000000000000/* * Checksum implementations needed by APFS. */ #include #include /* * Implementation of the crc32c algorithm, adapted and simplified. * * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ static const u32 crc32Table[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L }; u32 crc32c(u32 crc, const void *buf, int size) { const u8 *p = buf; while (size--) crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8); return crc; } /* * Implementation of the Fletcher-64 checksum, as used in APFS. * * Author: Gabriel Krisman Bertazi * Based on the Fletcher64 implementation from linux/drivers/nvdimm. */ u64 fletcher64(void *addr, unsigned long len) { __le32 *buff = addr; u64 sum1 = 0; u64 sum2 = 0; u64 c1, c2; int i; for (i = 0; i < len/sizeof(u32); i++) { sum1 += le32_to_cpu(buff[i]); sum2 += sum1; } c1 = sum1 + sum2; c1 = 0xFFFFFFFF - c1 % 0xFFFFFFFF; c2 = sum1 + c1; c2 = 0xFFFFFFFF - c2 % 0xFFFFFFFF; return (c2 << 32) | c1; } apfsprogs-0.2.0/lib/libzbitmap.c000066400000000000000000000772501471277137200166120ustar00rootroot00000000000000#include #include #include #include "apfs/libzbitmap.h" #define __packed __attribute__((packed)) #define MIN(x, y) ((x) > (y) ? (y) : (x)) #define ZBM_MAGIC 0x094D425A #define ZBM_MAX_DECMP_CHUNK_SIZE 0x8000 #define ZBM_MAX_DECMP_CHUNK_SIZE_BITS 15 struct uint24 { uint16_t lo; uint8_t hi; } __packed; /* This header is shared by both compressed and decompressed chunks */ struct zbm_chunk_hdr { struct uint24 len; /* Length of the chunk */ struct uint24 decmp_len; /* Length of the chunk after decompression */ } __packed; /* The full header for compressed chunks */ struct zbm_cmp_chunk_hdr { /* Shared with decompressed chunks */ struct zbm_chunk_hdr hdr; /* Offset for each of the three metadata areas */ struct uint24 meta_off_1; struct uint24 meta_off_2; struct uint24 meta_off_3; } __packed; /* Pointer to a half-byte */ struct nybl_ptr { uint8_t *addr; /* Address of the byte */ int nibble; /* Which of the two nibbles? */ }; /* 0-2 and 0xf are not real bitmap indexes */ #define ZBM_BITMAP_COUNT (16 - 1 - 3) #define ZBM_BITMAP_BASE 3 #define ZBM_BITMAP_BYTECNT 17 #define ZBM_MAX_PERIOD_BYTECNT 2 struct zbm_bmap { uint8_t bitmap; /* The bitmap */ uint8_t period_bytecnt; /* Read this many bytes to get the new period */ }; struct zbm_state { /* Updated during a chunk read */ uint8_t *dest; /* Write the next byte here */ size_t dest_left; /* Room left in destination buffer */ uint32_t written; /* Bytes written so far for current chunk */ uint16_t period; /* Repetition period for decompression, in bytes */ /* Updated right before a chunk read */ const uint8_t *src_end; /* End of current chunk */ uint32_t len; /* Length of the chunk */ uint32_t decmp_len; /* Expected chunk length after decompression */ /* Updated after a chunk read */ const uint8_t *src; /* Start of buffer, or current chunk if any */ size_t src_left; /* Room left in the source buffer */ size_t prewritten; /* Bytes written for previous chunks */ /* Current position in data and metadata areas for this chunk */ const uint8_t *data; const uint8_t *meta_1; const uint8_t *meta_2; struct nybl_ptr meta_3; /* Array of bitmaps for the current chunk */ struct zbm_bmap bitmaps[ZBM_BITMAP_COUNT]; }; static int zbm_check_magic(struct zbm_state *state) { uint32_t magic; if(state->src_left < sizeof(magic)) return ZBM_INVAL; magic = *(uint32_t *)state->src; if(magic != ZBM_MAGIC) return ZBM_INVAL; state->src += sizeof(magic); state->src_left -= sizeof(magic); return 0; } static uint32_t zbm_u24_to_u32(struct uint24 n) { uint32_t res; res = n.hi; res <<= 16; res += n.lo; return res; } /* Some chunks just have regular uncompressed data, but with a header */ static int zbm_chunk_is_uncompressed(struct zbm_state *state) { return state->len == state->decmp_len + sizeof(struct zbm_chunk_hdr); } static int zbm_handle_uncompressed_chunk(struct zbm_state *state) { state->meta_1 = state->meta_2 = NULL; state->meta_3.addr = NULL; state->meta_3.nibble = 0; state->data = state->src + sizeof(struct zbm_chunk_hdr); memcpy(state->dest, state->data, state->decmp_len); state->dest += state->decmp_len; state->dest_left -= state->decmp_len; state->written = state->decmp_len; return 0; } static int zbm_read_nibble(struct nybl_ptr *nybl, const uint8_t *limit, uint8_t *result) { if(nybl->addr >= limit) return ZBM_INVAL; if(nybl->nibble == 0) { *result = *nybl->addr & 0xf; nybl->nibble = 1; } else { *result = (*nybl->addr >> 4) & 0xf; nybl->nibble = 0; ++nybl->addr; } return 0; } static void zbm_rewind_nibble(struct nybl_ptr *nybl) { if(nybl->nibble == 0) { nybl->nibble = 1; --nybl->addr; } else { nybl->nibble = 0; } } static int zbm_apply_bitmap(struct zbm_state *state, struct zbm_bmap *bitmap) { int i; /* The periods are stored in the first metadata area */ if(bitmap->period_bytecnt) { state->period = 0; for(i = 0; i < bitmap->period_bytecnt; ++i) { if(state->meta_1 >= state->src_end) return ZBM_INVAL; state->period |= *state->meta_1 << i * 8; ++state->meta_1; } } if(state->period == 0) return ZBM_INVAL; for(i = 0; i < 8; ++i) { if(state->written == state->decmp_len) break; if(bitmap->bitmap & 1 << i) { if(state->data >= state->src_end) return ZBM_INVAL; *state->dest = *state->data; ++state->data; } else { if(state->prewritten + state->written < state->period) return ZBM_INVAL; *state->dest = *(state->dest - state->period); } ++state->dest; --state->dest_left; ++state->written; } return 0; } static int zbm_apply_bitmap_number(struct zbm_state *state, uint8_t bmp_num) { struct zbm_bmap next = {0}; /* Not a valid bitmap number (it signals a repetition) */ if(bmp_num == 0xf) return ZBM_INVAL; /* An actual index in the bitmap array */ if(bmp_num > ZBM_MAX_PERIOD_BYTECNT) return zbm_apply_bitmap(state, &state->bitmaps[bmp_num - ZBM_BITMAP_BASE]); /* For < 2, use the next bitmap in the second metadata area */ if(state->meta_2 >= state->src_end) return ZBM_INVAL; next.bitmap = *state->meta_2; next.period_bytecnt = bmp_num; ++state->meta_2; return zbm_apply_bitmap(state, &next); } /* Find out how many times we need to repeat the current bitmap operation */ static int zbm_read_repetition_count(struct zbm_state *state, uint16_t *repeat) { uint8_t nibble; uint16_t total; int err; /* Don't confuse the trailing bitmaps with a repetition count */ if(state->decmp_len - state->written <= 8) { *repeat = 1; return 0; } err = zbm_read_nibble(&state->meta_3, state->src_end, &nibble); if(err) return err; if(nibble != 0xf) { /* No repetition count: the previous bitmap number gets applied once */ zbm_rewind_nibble(&state->meta_3); *repeat = 1; return 0; } /* * Under this scheme, repeating a bitmap number 3 times wouldn't save any * space, so the repetition count starts from 4. */ total = 4; while(nibble == 0xf) { err = zbm_read_nibble(&state->meta_3, state->src_end, &nibble); if(err) return err; total += nibble; if(total < nibble) return ZBM_INVAL; } *repeat = total; return 0; } static int zbm_decompress_single_bitmap(struct zbm_state *state) { uint8_t bmp_num; uint16_t repeat; int i; int err; /* The current nibble is the offset of the next bitmap to apply */ err = zbm_read_nibble(&state->meta_3, state->src_end, &bmp_num); if(err) return err; err = zbm_read_repetition_count(state, &repeat); if(err) return err; for(i = 0; i < repeat; ++i) { err = zbm_apply_bitmap_number(state, bmp_num); if(err) return err; } return 0; } /* Pointer to a bit */ struct bit_ptr { uint8_t *addr; /* Address of the byte */ int offset; /* Bit number */ }; /* This function does not perform boundary checks, the caller must do it */ static int zbm_read_single_bit(struct bit_ptr *bit) { int res = *bit->addr >> bit->offset & 1; ++bit->offset; if(bit->offset != 8) return res; bit->offset = 0; ++bit->addr; return res; } static int zbm_read_single_bitmap(struct bit_ptr *bit, const uint8_t *limit, struct zbm_bmap *result) { int i; result->bitmap = 0; result->period_bytecnt = 0; /* The bitmap itself */ for(i = 0; i < 8; ++i) { if(bit->addr >= limit) return ZBM_INVAL; result->bitmap |= zbm_read_single_bit(bit) << i; } /* * The two trailing bits tell us how many bytes to read for the next * repetition period */ for(i = 0; i < 2; ++i) { if(bit->addr >= limit) return ZBM_INVAL; result->period_bytecnt |= zbm_read_single_bit(bit) << i; } return 0; } static int zbm_read_bitmaps(struct zbm_state *state) { struct bit_ptr bmap = {0}; int err, i; if(state->len < ZBM_BITMAP_BYTECNT) return ZBM_INVAL; bmap.addr = (uint8_t *)state->src_end - ZBM_BITMAP_BYTECNT; bmap.offset = 0; for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { err = zbm_read_single_bitmap(&bmap, state->src_end, &state->bitmaps[i]); if(err) return err; if(state->bitmaps[i].period_bytecnt > ZBM_MAX_PERIOD_BYTECNT) return ZBM_INVAL; } return 0; } static int zbm_handle_compressed_chunk(struct zbm_state *state) { const struct zbm_cmp_chunk_hdr *hdr = NULL; uint32_t meta_off_1, meta_off_2, meta_off_3; int err; state->written = 0; state->period = 8; if(state->len < sizeof(*hdr)) return ZBM_INVAL; hdr = (struct zbm_cmp_chunk_hdr *)state->src; state->data = state->src + sizeof(*hdr); meta_off_1 = zbm_u24_to_u32(hdr->meta_off_1); meta_off_2 = zbm_u24_to_u32(hdr->meta_off_2); meta_off_3 = zbm_u24_to_u32(hdr->meta_off_3); if(meta_off_1 >= state->len || meta_off_2 >= state->len || meta_off_3 >= state->len) return ZBM_INVAL; state->meta_1 = state->src + meta_off_1; state->meta_2 = state->src + meta_off_2; state->meta_3.addr = (uint8_t *)state->src + meta_off_3; state->meta_3.nibble = 0; err = zbm_read_bitmaps(state); if(err) return err; while(state->written < state->decmp_len) { err = zbm_decompress_single_bitmap(state); if(err) return err; } return 0; } static int zbm_handle_chunk(struct zbm_state *state) { const struct zbm_chunk_hdr *decmp_hdr = NULL; if(state->src_left < sizeof(*decmp_hdr)) return ZBM_INVAL; decmp_hdr = (struct zbm_chunk_hdr *)state->src; state->len = zbm_u24_to_u32(decmp_hdr->len); if(state->len > state->src_left) return ZBM_INVAL; state->src_end = state->src + state->len; state->decmp_len = zbm_u24_to_u32(decmp_hdr->decmp_len); if(state->decmp_len > ZBM_MAX_DECMP_CHUNK_SIZE) return ZBM_INVAL; if(!state->dest) /* We just wanted the length, so we are done */ return 0; if(state->decmp_len > state->dest_left) return ZBM_RANGE; if(zbm_chunk_is_uncompressed(state)) return zbm_handle_uncompressed_chunk(state); return zbm_handle_compressed_chunk(state); } int zbm_decompress(void *dest, size_t dest_size, const void *src, size_t src_size, size_t *out_len) { struct zbm_state state = {0}; int err; state.src = src; state.src_left = src_size; state.dest = dest; state.dest_left = dest_size; state.prewritten = 0; err = zbm_check_magic(&state); if(err) return err; /* The final chunk has zero decompressed length */ do { err = zbm_handle_chunk(&state); if(err) return err; state.src += state.len; state.src_left -= state.len; state.prewritten += state.decmp_len; } while(state.decmp_len != 0); *out_len = state.prewritten; return 0; } #define ZBM_MAX_BITMAP_COUNT (ZBM_MAX_DECMP_CHUNK_SIZE >> 3) #define ZBM_POSSIBLE_BMPROTS (1 << 10) struct zbm_compress_state { /* Updated during a chunk write */ const uint8_t *src; /* Next byte to read */ uint32_t read; /* Bytes processed so far for current chunk */ uint32_t written; /* Bytes written so far for current chunk */ uint8_t *dest; /* Write the next byte here */ uint16_t period; /* Repetition period for compression, in bytes */ /* Updated right before a chunk write */ const uint8_t *dest_end; /* Maximum limit for the current chunk */ const uint8_t *src_end; /* End of current decompressed chunk */ uint32_t decmp_len; /* Decompressed length of the chunk */ /* Updated after a chunk write */ size_t dest_left; /* Room left in destination buffer */ size_t preread; /* Bytes processed for previous chunks */ size_t prewritten; /* Bytes written for previous chunks */ size_t src_left; /* Room left in the source buffer */ /* Array of all bitmaps applied for the current chunk */ struct zbm_bmap bitmaps[ZBM_MAX_BITMAP_COUNT]; uint16_t periods[ZBM_MAX_BITMAP_COUNT]; int bmp_cnt; /* How many times was each bitmap-rotation combination applied? */ uint64_t usecnts[ZBM_POSSIBLE_BMPROTS]; /* Array of bitmaps applied most often */ struct zbm_bmap top_bitmaps[ZBM_BITMAP_COUNT]; }; /* Internal error used when a compression attempt was ineffective */ #define ZBM_CANT_COMPRESS (-1024) static int zbm_bmprot(struct zbm_bmap *bmap) { return (int)bmap->bitmap << 2 | (int)bmap->period_bytecnt; } static int zbm_write_magic(struct zbm_compress_state *state) { uint32_t *magic_p; if(state->dest_left < sizeof(*magic_p)) return ZBM_INVAL; magic_p = (uint32_t *)state->dest; *magic_p = ZBM_MAGIC; state->dest += sizeof(*magic_p); state->dest_left -= sizeof(*magic_p); state->prewritten += sizeof(*magic_p); return 0; } static int zbm_build_uncompressed_chunk(struct zbm_compress_state *state) { /* Undo the previous compression attempt */ state->src -= state->read; state->read = 0; state->dest -= state->written - sizeof(struct zbm_chunk_hdr); state->written = sizeof(struct zbm_chunk_hdr); memcpy(state->dest, state->src, state->decmp_len); state->src += state->decmp_len; state->read += state->decmp_len; state->written += state->decmp_len; state->dest += state->decmp_len; return 0; } static uint8_t zbm_compare_bytes(uint64_t bytes1, uint64_t bytes2) { static const uint64_t mask0 = 0xff; static const uint64_t mask1 = 0xff00; static const uint64_t mask2 = 0xff0000; static const uint64_t mask3 = 0xff000000; static const uint64_t mask4 = 0xff00000000; static const uint64_t mask5 = 0xff0000000000; static const uint64_t mask6 = 0xff000000000000; static const uint64_t mask7 = 0xff00000000000000; uint8_t diff = 0; uint64_t xor; xor = bytes1 ^ bytes2; diff += (xor & mask0) != 0; diff += (xor & mask1) != 0; diff += (xor & mask2) != 0; diff += (xor & mask3) != 0; diff += (xor & mask4) != 0; diff += (xor & mask5) != 0; diff += (xor & mask6) != 0; diff += (xor & mask7) != 0; return diff; } static void zbm_append_new_bitmap(struct zbm_compress_state *state, uint8_t bitmap, uint16_t period) { struct zbm_bmap *new_bmp = NULL; new_bmp = &state->bitmaps[state->bmp_cnt]; new_bmp->bitmap = bitmap; state->periods[state->bmp_cnt] = period; if(period == state->period) new_bmp->period_bytecnt = 0; else if(period <= 0xff) new_bmp->period_bytecnt = 1; else new_bmp->period_bytecnt = 2; ++state->bmp_cnt; ++state->usecnts[zbm_bmprot(new_bmp)]; state->period = period; } static uint8_t zbm_calculate_bitmap(const uint8_t *bytes1, const uint8_t *bytes2, uint8_t size) { uint8_t bitmap; uint8_t i; bitmap = 0; for(i = 0; i < size; ++i) { if(bytes1[i] != bytes2[i]) bitmap |= 1 << i; } return bitmap; } static void zbm_find_good_pattern(struct zbm_compress_state *state) { uint64_t needle; const uint8_t *to_compare = NULL; const uint8_t *next = NULL; const uint8_t *best = NULL; const uint8_t *split = NULL; int bytecnt, diff, best_cost; uint8_t bitmap; int i; bytecnt = MIN(8, state->decmp_len - state->read); needle = 0; for(i = 0; i < bytecnt; ++i) needle |= (uint64_t)state->src[i] << i * 8; /* * We'll estimate the cost of a period as the number of digits needed to * store it plus the number of characters that get changed. This isn't * perfect of course, because bitmap reuse is also a good idea. */ diff = zbm_compare_bytes(*(uint64_t *)(state->src - state->period), needle); best = state->src - state->period; best_cost = diff; if(best_cost <= 1) goto done; /* We first look for patterns that can be reached with a 1-byte period */ to_compare = state->src - MIN(0xff, state->preread + state->read); split = to_compare; while(to_compare <= state->src - 8) { diff = zbm_compare_bytes(*(uint64_t *)to_compare, needle); if(diff + 1 < best_cost) { best = to_compare; best_cost = diff + 1; if(best_cost == 1) goto done; } ++to_compare; } if(best_cost == 2) goto done; /* * We now look for patterns that can be reached with a 2-byte period, but * we focus only on the ones that start with the same byte. Checking * everything would take too long. */ to_compare = state->src - MIN(0xffff, state->preread + state->read); while(to_compare < split) { next = memchr(to_compare, state->src[0], split - to_compare); if(!next) break; diff = zbm_compare_bytes(*(uint64_t *)next, needle); if(diff + 2 < best_cost) { best = next; best_cost = diff + 2; if(best_cost == 2) goto done; } to_compare = next + 1; } done: bitmap = zbm_calculate_bitmap(best, state->src, bytecnt); zbm_append_new_bitmap(state, bitmap, state->src - best); } static int zbm_write_new_data(struct zbm_compress_state *state) { struct zbm_bmap *bmap = &state->bitmaps[state->bmp_cnt - 1]; int i; for(i = 0; i < 8; ++i) { if(state->src == state->src_end) break; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; if(bmap->bitmap & 1 << i) { *state->dest = *state->src; ++state->dest; ++state->written; } ++state->src; ++state->read; } return 0; } static int zbm_compress_eight_bytes(struct zbm_compress_state *state) { zbm_find_good_pattern(state); return zbm_write_new_data(state); } static int zbm_build_initial_bitmap(struct zbm_compress_state *state) { struct zbm_bmap *init = &state->bitmaps[0]; init->bitmap = 0xff; init->period_bytecnt = 0; state->bmp_cnt = 1; state->usecnts[zbm_bmprot(init)] = 1; if(state->decmp_len - state->read < 8) return ZBM_CANT_COMPRESS; memcpy(state->dest, state->src, 8); state->dest += 8; state->src += 8; state->read += 8; state->written += 8; return 0; } static int zbm_write_metadata_1(struct zbm_compress_state *state) { struct zbm_bmap *bmap = NULL; uint16_t period; int i; /* The first metadata area stores the periods, when they change */ for(i = 0; i < state->bmp_cnt; ++i) { bmap = &state->bitmaps[i]; period = state->periods[i]; if(bmap->period_bytecnt == 0) continue; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; *state->dest = period; ++state->dest; ++state->written; if(bmap->period_bytecnt == 1) continue; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; *state->dest = period >> 8; ++state->dest; ++state->written; } return 0; } static int zbm_write_metadata_2(struct zbm_compress_state *state) { struct zbm_bmap *bmap = NULL; int i; /* The second metadata area stores most of the bitmaps */ for(i = 0; i < state->bmp_cnt; ++i) { bmap = &state->bitmaps[i]; /* This is one of the top bitmaps that go in the end of the chunk */ if(state->usecnts[zbm_bmprot(bmap)] == 0) continue; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; *state->dest = bmap->bitmap; ++state->dest; ++state->written; } return 0; } static int zbm_equal_bmaps(struct zbm_bmap *bmap1, struct zbm_bmap *bmap2) { if(!bmap1 || !bmap2) return 0; if(bmap1->bitmap != bmap2->bitmap) return 0; if(bmap1->period_bytecnt != bmap2->period_bytecnt) return 0; return 1; } static int zbm_bmap_num_for_index(struct zbm_compress_state *state, int idx) { struct zbm_bmap *bmap = NULL; struct zbm_bmap *top_bmap = NULL; int i; bmap = &state->bitmaps[idx]; /* This is one of the top bitmaps that go in the end of the chunk */ if(state->usecnts[zbm_bmprot(bmap)] == 0) { for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { top_bmap = &state->top_bitmaps[i]; if(zbm_equal_bmaps(top_bmap, bmap)) return i + 3; } } /* This is a regular bitmap from the second metadata area */ return bmap->period_bytecnt; } static int zbm_bmap_num_and_repcount(struct zbm_compress_state *state, int idx, int *repeat) { int bmap_num, next_num; int i; bmap_num = zbm_bmap_num_for_index(state, idx); *repeat = 1; for(i = idx + 1; i < state->bmp_cnt; ++i) { next_num = zbm_bmap_num_for_index(state, i); if(next_num != bmap_num) break; *repeat += 1; } return bmap_num; } static int zbm_write_nibble(struct nybl_ptr *nybl, const uint8_t *limit, uint8_t val) { if(nybl->addr >= limit) return ZBM_CANT_COMPRESS; if(nybl->nibble == 0) { *nybl->addr = val; nybl->nibble = 1; } else { *nybl->addr |= val << 4; nybl->nibble = 0; ++nybl->addr; } return 0; } static int zbm_write_repetition_count(struct nybl_ptr *nybl, const uint8_t *limit, int repeat) { int nibble = 0xf; int err; /* 0xf marks that this is a repetition */ err = zbm_write_nibble(nybl, limit, nibble); if(err) return err; /* We count from the minimum possible repetition count */ repeat -= 4; while(repeat > 0) { nibble = MIN(repeat, 0xf); err = zbm_write_nibble(nybl, limit, nibble); if(err) return err; repeat -= nibble; } /* The repetition must always end in a non-0xf nibble */ if(nibble == 0xf) { err = zbm_write_nibble(nybl, limit, 0); if(err) return err; } return 0; } static int zbm_write_metadata_3(struct zbm_compress_state *state) { struct nybl_ptr nybl = {0}; int to_write, repeat; int i, j; int err; nybl.addr = state->dest; nybl.nibble = 0; for(i = 0; i < state->bmp_cnt; i += repeat) { to_write = zbm_bmap_num_and_repcount(state, i, &repeat); err = zbm_write_nibble(&nybl, state->dest_end, to_write); if(err) return err; /* Fewer than 3 repetitions are done trivially */ if(repeat <= 3) { for(j = 1; j < repeat; ++j) { err = zbm_write_nibble(&nybl, state->dest_end, to_write); if(err) return err; } } else { err = zbm_write_repetition_count(&nybl, state->dest_end, repeat); if(err) return err; } } /* Leave the trailing nibble alone and move on to the next byte */ if(nybl.nibble == 1) { ++nybl.addr; nybl.nibble = 0; } state->written += nybl.addr - state->dest; state->dest = nybl.addr; return 0; } /* This function does not perform boundary checks, the caller must do it */ static void zbm_write_single_bit(struct bit_ptr *bit, int val) { *bit->addr |= val << bit->offset; ++bit->offset; if(bit->offset != 8) return; bit->offset = 0; ++bit->addr; return; } /* This function does not perform boundary checks, the caller must do it */ static void zbm_write_single_bitmap(struct bit_ptr *bit, struct zbm_bmap bmap) { int i; /* The bitmap itself */ for(i = 0; i < 8; ++i) zbm_write_single_bit(bit, bmap.bitmap >> i & 1); /* The trailing bits for the period bytecount */ for(i = 0; i < 2; ++i) zbm_write_single_bit(bit, bmap.period_bytecnt >> i & 1); } static int zbm_write_trailing_bitmaps(struct zbm_compress_state *state) { struct bit_ptr bmap = {0}; int i; if(state->dest_end - state->dest < ZBM_BITMAP_BYTECNT) return ZBM_CANT_COMPRESS; memset(state->dest, 0, ZBM_BITMAP_BYTECNT); bmap.addr = state->dest; bmap.offset = 0; for(i = 0; i < ZBM_BITMAP_COUNT; ++i) zbm_write_single_bitmap(&bmap, state->top_bitmaps[i]); state->dest += ZBM_BITMAP_BYTECNT; state->written += ZBM_BITMAP_BYTECNT; return 0; } static struct uint24 zbm_u32_to_u24(uint32_t n) { struct uint24 res; res.lo = n; res.hi = n >> 16; return res; } struct top_bmap { struct zbm_bmap *bmap; uint64_t usecnt; }; /* The array is sorted by use count, in descending order */ static void zbm_insert_in_top_bmaps(struct zbm_bmap *bmap, uint64_t usecnt, struct top_bmap tops[ZBM_BITMAP_COUNT]) { int i; for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { if(zbm_equal_bmaps(tops[i].bmap, bmap)) /* Already in the array */ return; if(tops[i].usecnt < usecnt) break; } if(i == ZBM_BITMAP_COUNT) return; memmove(&tops[i + 1], &tops[i], (ZBM_BITMAP_COUNT - i - 1) * sizeof(tops[i])); tops[i].bmap = bmap; tops[i].usecnt = usecnt; } static void zbm_find_most_common_bitmaps(struct zbm_compress_state *state) { struct top_bmap tops[ZBM_BITMAP_COUNT] = {0}; struct zbm_bmap *bmap = NULL; int i; for(i = 0; i < state->bmp_cnt; ++i) { bmap = &state->bitmaps[i]; zbm_insert_in_top_bmaps(bmap, state->usecnts[zbm_bmprot(bmap)], tops); bmap = NULL; } for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { if(tops[i].usecnt == 0) { /* Very few bitmaps, all are top */ state->top_bitmaps[i].bitmap = 0; state->top_bitmaps[i].period_bytecnt = 0; continue; } bmap = tops[i].bmap; state->top_bitmaps[i] = *bmap; /* Mark this as unused for the second metadata area */ state->usecnts[zbm_bmprot(bmap)] = 0; } } static int zbm_build_metadata(struct zbm_compress_state *state, struct zbm_cmp_chunk_hdr *hdr) { int err; zbm_find_most_common_bitmaps(state); hdr->meta_off_1 = zbm_u32_to_u24(state->written); err = zbm_write_metadata_1(state); if(err) return err; hdr->meta_off_2 = zbm_u32_to_u24(state->written); err = zbm_write_metadata_2(state); if(err) return err; hdr->meta_off_3 = zbm_u32_to_u24(state->written); err = zbm_write_metadata_3(state); if(err) return err; return zbm_write_trailing_bitmaps(state); } static int zbm_build_compressed_chunk(struct zbm_compress_state *state) { struct zbm_cmp_chunk_hdr *hdr = NULL; int err; if(sizeof(*hdr) > (size_t)(state->dest_end - state->dest)) return ZBM_CANT_COMPRESS; state->dest -= sizeof(hdr->hdr); state->written -= sizeof(hdr->hdr); hdr = (struct zbm_cmp_chunk_hdr *)state->dest; state->dest += sizeof(*hdr); state->written += sizeof(*hdr); memset(state->bitmaps, 0, sizeof(state->bitmaps)); memset(state->periods, 0, sizeof(state->bitmaps)); state->bmp_cnt = 0; memset(state->usecnts, 0, sizeof(state->usecnts)); memset(state->top_bitmaps, 0, sizeof(state->top_bitmaps)); if(state->preread == 0) { err = zbm_build_initial_bitmap(state); if(err) return err; } state->period = 8; while(state->read < state->decmp_len) { err = zbm_compress_eight_bytes(state); if(err) return err; } return zbm_build_metadata(state, hdr); } static int zbm_compress_single_chunk(struct zbm_compress_state *state) { struct zbm_chunk_hdr *decmp_hdr = NULL; unsigned int max_chunk_sz; int err; state->read = 0; state->written = 0; state->decmp_len = MIN(state->src_left, ZBM_MAX_DECMP_CHUNK_SIZE); state->src_end = state->src + state->decmp_len; /* If a chunk gets bigger than this, we'll just store it uncompressed */ max_chunk_sz = state->decmp_len + sizeof(*decmp_hdr); if(max_chunk_sz > state->dest_left) return ZBM_RANGE; state->dest_end = state->dest + max_chunk_sz; decmp_hdr = (struct zbm_chunk_hdr *)state->dest; state->dest += sizeof(*decmp_hdr); state->written += sizeof(*decmp_hdr); err = zbm_build_compressed_chunk(state); if(err == ZBM_CANT_COMPRESS) err = zbm_build_uncompressed_chunk(state); if(err) return err; decmp_hdr->len = zbm_u32_to_u24(state->written); decmp_hdr->decmp_len = zbm_u32_to_u24(state->decmp_len); return 0; } static int zmb_max_compressed_length(size_t src_size, size_t *out_len) { size_t max_chunk_size = sizeof(struct zbm_chunk_hdr) + 0x8000; size_t chunk_count; chunk_count = (src_size + ZBM_MAX_DECMP_CHUNK_SIZE - 1) >> ZBM_MAX_DECMP_CHUNK_SIZE_BITS; if(chunk_count << ZBM_MAX_DECMP_CHUNK_SIZE_BITS < src_size) return ZBM_OVERFLOW; /* The magic, all the chunks, and the terminating empty chunk */ *out_len = sizeof(uint32_t) + chunk_count * max_chunk_size + ZBM_LAST_CHUNK_SIZE; if(*out_len < src_size) return ZBM_OVERFLOW; return 0; } int zbm_compress(void *dest, size_t dest_size, const void *src, size_t src_size, size_t *out_len) { struct zbm_compress_state *state = NULL; int err = 0; if(!dest) return zmb_max_compressed_length(src_size, out_len); state = calloc(1, sizeof(*state)); if(!state) return ZBM_NOMEM; state->src = src; state->src_left = src_size; state->dest = dest; state->dest_left = dest_size; state->preread = 0; state->prewritten = 0; err = zbm_write_magic(state); if(err) goto fail; do { err = zbm_compress_single_chunk(state); if(err) goto fail; state->src_left -= state->read; state->dest_left -= state->written; state->preread += state->decmp_len; state->prewritten += state->written; } while(state->read != 0); *out_len = state->prewritten; fail: free(state); return err; } int zbm_compress_chunk(void *dest, size_t dest_size, const void *src, size_t src_size, size_t index, size_t *out_len) { struct zbm_compress_state *state = NULL; size_t offset; int err = 0; state = calloc(1, sizeof(*state)); if(!state) return ZBM_NOMEM; state->src = src; state->src_left = src_size; state->dest = dest; state->dest_left = dest_size; state->preread = 0; state->prewritten = 0; if(index == 0) { err = zbm_write_magic(state); if(err) goto fail; } offset = index << ZBM_MAX_DECMP_CHUNK_SIZE_BITS; if(offset >> ZBM_MAX_DECMP_CHUNK_SIZE_BITS != index) return ZBM_OVERFLOW; if(state->src_left < offset) { state->preread = state->src_left; state->src += state->src_left; state->src_left = 0; } else { state->preread = offset; state->src += offset; state->src_left -= offset; } err = zbm_compress_single_chunk(state); if(err) goto fail; state->src_left -= state->read; state->dest_left -= state->written; state->preread += state->decmp_len; state->prewritten += state->written; *out_len = state->prewritten; fail: free(state); return err; } apfsprogs-0.2.0/lib/parameters.c000066400000000000000000000021241471277137200166040ustar00rootroot00000000000000/* * Copyright (C) 2021 Ernesto A. Fernández * * Calculation of filesystem parameters needed by both mkfs and fsck. */ #include #include /** * ip_fq_node_limit - Calculate the node limit for the internal pool free queue * @chunks: chunk count for the container */ u16 ip_fq_node_limit(u64 chunks) { u16 ret = 3 * (chunks + 751) / 1127 - 1; if (ret == 2) ret = 3; /* Leave room for a new root node */ return ret; } /** * main_fq_node_limit - Calculate the node limit for the main free queue * @blocks: block count for the container */ u16 main_fq_node_limit(u64 blocks) { u64 blks_1gb = 0x40000; u64 blks_4gb = 0x100000; u16 ret; /* * The node limit is required to be a function of the block count. * Inside each of the (0, 1G), [1G, 4G) and [4G, +inf) intervals, the * function is linear. */ if (blocks < blks_1gb) ret = 1 + (blocks - 1) / 4544; else if (blocks < blks_4gb) ret = 116 + (blocks - 261281) / 2272; else ret = 512; if (ret == 2) ret = 3; /* Leave room for a new root node */ return ret; } apfsprogs-0.2.0/lib/sha256.c000066400000000000000000000300171471277137200154530ustar00rootroot00000000000000/* * SHA-256 implementation, modified slightly to build it here. */ /*- * Copyright (c) 2001-2003 Allan Saddi * 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. * * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS 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 ALLAN SADDI OR HIS 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. * * $Id: sha256.c 680 2003-07-25 21:57:49Z asaddi $ */ /* * Define WORDS_BIGENDIAN if compiling on a big-endian architecture. * * Define SHA256_TEST to test the implementation using the NIST's * sample messages. The output should be: * * ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad * 248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1 * cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0 */ #include #include #include #define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) #define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) #define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) #define SIGMA0(x) (ROTR((x), 2) ^ ROTR((x), 13) ^ ROTR((x), 22)) #define SIGMA1(x) (ROTR((x), 6) ^ ROTR((x), 11) ^ ROTR((x), 25)) #define sigma0(x) (ROTR((x), 7) ^ ROTR((x), 18) ^ ((x) >> 3)) #define sigma1(x) (ROTR((x), 17) ^ ROTR((x), 19) ^ ((x) >> 10)) #define DO_ROUND() { \ t1 = h + SIGMA1(e) + Ch(e, f, g) + *(Kp++) + *(W++); \ t2 = SIGMA0(a) + Maj(a, b, c); \ h = g; \ g = f; \ f = e; \ e = d + t1; \ d = c; \ c = b; \ b = a; \ a = t1 + t2; \ } static const uint32_t K[64] = { 0x428a2f98L, 0x71374491L, 0xb5c0fbcfL, 0xe9b5dba5L, 0x3956c25bL, 0x59f111f1L, 0x923f82a4L, 0xab1c5ed5L, 0xd807aa98L, 0x12835b01L, 0x243185beL, 0x550c7dc3L, 0x72be5d74L, 0x80deb1feL, 0x9bdc06a7L, 0xc19bf174L, 0xe49b69c1L, 0xefbe4786L, 0x0fc19dc6L, 0x240ca1ccL, 0x2de92c6fL, 0x4a7484aaL, 0x5cb0a9dcL, 0x76f988daL, 0x983e5152L, 0xa831c66dL, 0xb00327c8L, 0xbf597fc7L, 0xc6e00bf3L, 0xd5a79147L, 0x06ca6351L, 0x14292967L, 0x27b70a85L, 0x2e1b2138L, 0x4d2c6dfcL, 0x53380d13L, 0x650a7354L, 0x766a0abbL, 0x81c2c92eL, 0x92722c85L, 0xa2bfe8a1L, 0xa81a664bL, 0xc24b8b70L, 0xc76c51a3L, 0xd192e819L, 0xd6990624L, 0xf40e3585L, 0x106aa070L, 0x19a4c116L, 0x1e376c08L, 0x2748774cL, 0x34b0bcb5L, 0x391c0cb3L, 0x4ed8aa4aL, 0x5b9cca4fL, 0x682e6ff3L, 0x748f82eeL, 0x78a5636fL, 0x84c87814L, 0x8cc70208L, 0x90befffaL, 0xa4506cebL, 0xbef9a3f7L, 0xc67178f2L }; #ifndef RUNTIME_ENDIAN #if WORDS_BIGENDIAN == 1 #define BYTESWAP(x) (x) #define BYTESWAP64(x) (x) #else /* WORDS_BIGENDIAN */ #define BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \ (ROTL((x), 8) & 0x00ff00ffL)) #define BYTESWAP64(x) _byteswap64(x) static inline uint64_t _byteswap64(uint64_t x) { uint32_t a = x >> 32; uint32_t b = (uint32_t) x; return ((uint64_t) BYTESWAP(b) << 32) | (uint64_t) BYTESWAP(a); } #endif /* WORDS_BIGENDIAN */ #else /* !RUNTIME_ENDIAN */ #define BYTESWAP(x) _byteswap(sc->littleEndian, x) #define BYTESWAP64(x) _byteswap64(sc->littleEndian, x) #define _BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \ (ROTL((x), 8) & 0x00ff00ffL)) #define _BYTESWAP64(x) __byteswap64(x) static inline uint64_t __byteswap64(uint64_t x) { uint32_t a = x >> 32; uint32_t b = (uint32_t) x; return ((uint64_t) _BYTESWAP(b) << 32) | (uint64_t) _BYTESWAP(a); } static inline uint32_t _byteswap(int littleEndian, uint32_t x) { if (!littleEndian) return x; else return _BYTESWAP(x); } static inline uint64_t _byteswap64(int littleEndian, uint64_t x) { if (!littleEndian) return x; else return _BYTESWAP64(x); } static inline void setEndian(int *littleEndianp) { union { uint32_t w; uint8_t b[4]; } endian; endian.w = 1L; *littleEndianp = endian.b[0] != 0; } #endif /* !RUNTIME_ENDIAN */ static const uint8_t padding[64] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; void sha256_init (SHA256_CTX *sc) { #ifdef RUNTIME_ENDIAN setEndian (&sc->littleEndian); #endif /* RUNTIME_ENDIAN */ sc->totalLength = 0LL; sc->hash[0] = 0x6a09e667L; sc->hash[1] = 0xbb67ae85L; sc->hash[2] = 0x3c6ef372L; sc->hash[3] = 0xa54ff53aL; sc->hash[4] = 0x510e527fL; sc->hash[5] = 0x9b05688cL; sc->hash[6] = 0x1f83d9abL; sc->hash[7] = 0x5be0cd19L; sc->bufferLength = 0L; } static void burnStack (int size) { char buf[128]; memset (buf, 0, sizeof (buf)); size -= sizeof (buf); if (size > 0) burnStack (size); } static void SHA256Guts (SHA256_CTX *sc, const uint32_t *cbuf) { uint32_t buf[64]; uint32_t *W, *W2, *W7, *W15, *W16; uint32_t a, b, c, d, e, f, g, h; uint32_t t1, t2; const uint32_t *Kp; int i; W = buf; for (i = 15; i >= 0; i--) { *(W++) = BYTESWAP(*cbuf); cbuf++; } W16 = &buf[0]; W15 = &buf[1]; W7 = &buf[9]; W2 = &buf[14]; for (i = 47; i >= 0; i--) { *(W++) = sigma1(*W2) + *(W7++) + sigma0(*W15) + *(W16++); W2++; W15++; } a = sc->hash[0]; b = sc->hash[1]; c = sc->hash[2]; d = sc->hash[3]; e = sc->hash[4]; f = sc->hash[5]; g = sc->hash[6]; h = sc->hash[7]; Kp = K; W = buf; #ifndef SHA256_UNROLL #define SHA256_UNROLL 4 #endif /* !SHA256_UNROLL */ #if SHA256_UNROLL == 1 for (i = 63; i >= 0; i--) DO_ROUND(); #elif SHA256_UNROLL == 2 for (i = 31; i >= 0; i--) { DO_ROUND(); DO_ROUND(); } #elif SHA256_UNROLL == 4 for (i = 15; i >= 0; i--) { DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); } #elif SHA256_UNROLL == 8 for (i = 7; i >= 0; i--) { DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); } #elif SHA256_UNROLL == 16 for (i = 3; i >= 0; i--) { DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); } #elif SHA256_UNROLL == 32 for (i = 1; i >= 0; i--) { DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); } #elif SHA256_UNROLL == 64 DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); DO_ROUND(); #else #error "SHA256_UNROLL must be 1, 2, 4, 8, 16, 32, or 64!" #endif sc->hash[0] += a; sc->hash[1] += b; sc->hash[2] += c; sc->hash[3] += d; sc->hash[4] += e; sc->hash[5] += f; sc->hash[6] += g; sc->hash[7] += h; } void sha256_update (SHA256_CTX *sc, const void *vdata, uint32_t len) { const uint8_t *data = vdata; uint32_t bufferBytesLeft; uint32_t bytesToCopy; int needBurn = 0; #ifdef SHA256_FAST_COPY if (sc->bufferLength) { bufferBytesLeft = 64L - sc->bufferLength; bytesToCopy = bufferBytesLeft; if (bytesToCopy > len) bytesToCopy = len; memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy); sc->totalLength += bytesToCopy * 8L; sc->bufferLength += bytesToCopy; data += bytesToCopy; len -= bytesToCopy; if (sc->bufferLength == 64L) { SHA256Guts (sc, sc->buffer.words); needBurn = 1; sc->bufferLength = 0L; } } while (len > 63L) { sc->totalLength += 512L; SHA256Guts (sc, data); needBurn = 1; data += 64L; len -= 64L; } if (len) { memcpy (&sc->buffer.bytes[sc->bufferLength], data, len); sc->totalLength += len * 8L; sc->bufferLength += len; } #else /* SHA256_FAST_COPY */ while (len) { bufferBytesLeft = 64L - sc->bufferLength; bytesToCopy = bufferBytesLeft; if (bytesToCopy > len) bytesToCopy = len; memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy); sc->totalLength += bytesToCopy * 8L; sc->bufferLength += bytesToCopy; data += bytesToCopy; len -= bytesToCopy; if (sc->bufferLength == 64L) { SHA256Guts (sc, sc->buffer.words); needBurn = 1; sc->bufferLength = 0L; } } #endif /* SHA256_FAST_COPY */ if (needBurn) burnStack (sizeof (uint32_t[74]) + sizeof (uint32_t *[6]) + sizeof (int)); } void sha256_final (SHA256_CTX *sc, uint8_t hash[SHA256_HASH_SIZE]) { uint32_t bytesToPad; uint64_t lengthPad; int i; bytesToPad = 120L - sc->bufferLength; if (bytesToPad > 64L) bytesToPad -= 64L; lengthPad = BYTESWAP64(sc->totalLength); sha256_update (sc, padding, bytesToPad); sha256_update (sc, &lengthPad, 8L); if (hash) { for (i = 0; i < SHA256_HASH_WORDS; i++) { #ifdef SHA256_FAST_COPY *((uint32_t *) hash) = BYTESWAP(sc->hash[i]); #else /* SHA256_FAST_COPY */ hash[0] = (uint8_t) (sc->hash[i] >> 24); hash[1] = (uint8_t) (sc->hash[i] >> 16); hash[2] = (uint8_t) (sc->hash[i] >> 8); hash[3] = (uint8_t) sc->hash[i]; #endif /* SHA256_FAST_COPY */ hash += 4; } } } #ifdef SHA256_TEST #include #include #include int main (int argc, char *argv[]) { SHA256_CTX foo; uint8_t hash[SHA256_HASH_SIZE]; char buf[1000]; int i; sha256_init (&foo); sha256_update (&foo, "abc", 3); sha256_final (&foo, hash); for (i = 0; i < SHA256_HASH_SIZE;) { printf ("%02x", hash[i++]); if (!(i % 4)) printf (" "); } printf ("\n"); sha256_init (&foo); sha256_update (&foo, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56); sha256_final (&foo, hash); for (i = 0; i < SHA256_HASH_SIZE;) { printf ("%02x", hash[i++]); if (!(i % 4)) printf (" "); } printf ("\n"); sha256_init (&foo); memset (buf, 'a', sizeof (buf)); for (i = 0; i < 1000; i++) sha256_update (&foo, buf, sizeof (buf)); sha256_final (&foo, hash); for (i = 0; i < SHA256_HASH_SIZE;) { printf ("%02x", hash[i++]); if (!(i % 4)) printf (" "); } printf ("\n"); exit (0); } #endif /* SHA256_TEST */ apfsprogs-0.2.0/lib/unicode.c000066400000000000000000004651561471277137200161110ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández * * Routines and data for the normalization of unicode strings. * Somewhat based on linux/fs/hfsplus/unicode.c */ #include #include /* * To reuse the implementation from the kernel module, we first * need to copy some code from linux/fs/nls/nls_base.c */ /* * Sample implementation from Unicode home page. * http://www.stonehand.com/unicode/standard/fss-utf.html */ struct utf8_table { int cmask; int cval; int shift; long lmask; long lval; }; static const struct utf8_table utf8_table[] = { {0x80, 0x00, 0*6, 0x7F, 0, /* 1 byte sequence */}, {0xE0, 0xC0, 1*6, 0x7FF, 0x80, /* 2 byte sequence */}, {0xF0, 0xE0, 2*6, 0xFFFF, 0x800, /* 3 byte sequence */}, {0xF8, 0xF0, 3*6, 0x1FFFFF, 0x10000, /* 4 byte sequence */}, {0xFC, 0xF8, 4*6, 0x3FFFFFF, 0x200000, /* 5 byte sequence */}, {0xFE, 0xFC, 5*6, 0x7FFFFFFF, 0x4000000, /* 6 byte sequence */}, {0, /* end of table */} }; #define UNICODE_MAX 0x0010ffff #define PLANE_SIZE 0x00010000 #define SURROGATE_MASK 0xfffff800 #define SURROGATE_PAIR 0x0000d800 #define SURROGATE_LOW 0x00000400 #define SURROGATE_BITS 0x000003ff static int utf8_to_utf32(const u8 *s, int inlen, unicode_t *pu) { unsigned long l; int c0, c, nc; const struct utf8_table *t; nc = 0; c0 = *s; l = c0; for (t = utf8_table; t->cmask; t++) { nc++; if ((c0 & t->cmask) == t->cval) { l &= t->lmask; if (l < t->lval || l > UNICODE_MAX || (l & SURROGATE_MASK) == SURROGATE_PAIR) return -1; *pu = (unicode_t) l; return nc; } if (inlen <= nc) return -1; s++; c = (*s ^ 0x80) & 0xFF; if (c & 0xC0) return -1; l = (l << 6) | c; } return -1; } /* * The actual implementation starts here */ /* The arrays of unicode data are defined at the bottom of the file */ static u16 nfd_trie[]; static unicode_t nfd_values[]; static u16 cf_trie[]; static unicode_t cf_values[]; static u8 ccc_trie[]; #define TRIE_HEIGHT 5 /* A trie node has one child for each possible nibble in the key */ #define TRIE_CHILD_SHIFT 4 #define TRIE_CHILD_MASK ((1 << TRIE_CHILD_SHIFT) - 1) /* A trie value length is stored in the last three bits of its position */ #define TRIE_POS_SHIFT 3 #define TRIE_SIZE_MASK ((1 << TRIE_POS_SHIFT) - 1) /** * trie_find - Look up a trie value * @trie: trie to search * @key: search key (a unicode character) * @result: on return, this either holds the value (on a ccc lookup) or its * position in the value array (on a cf or nfd lookup). * @is_ccc: true if this a ccc (canonical combining class) lookup * * Returns the length of the value (0 if it doesn't exist). */ static int trie_find(void *trie, unicode_t key, void *result, bool is_ccc) { int node = 0; int h; for (h = TRIE_HEIGHT - 1; h >= 0; --h) { int child = (key >> (TRIE_CHILD_SHIFT * h)) & TRIE_CHILD_MASK; int child_index = (node << TRIE_CHILD_SHIFT) + child; if (is_ccc) node = ((u8 *)trie)[child_index]; else node = ((u16 *)trie)[child_index]; if (node == 0) { *(u8 *)result = 0; return 0; } } if (is_ccc) { /* ccc values fit in one byte, so no need for a value array */ *(u8 *)result = node; return 1; } *(u16 *)result = node >> TRIE_POS_SHIFT; return node & TRIE_SIZE_MASK; } /** * init_unicursor - Initialize a unicursor structure * @cursor: cursor to initialize * @utf8str: string to normalize */ void init_unicursor(struct unicursor *cursor, const char *utf8str) { cursor->utf8curr = utf8str; cursor->length = -1; cursor->last_pos = -1; cursor->last_ccc = 0; } #define HANGUL_S_BASE 0xac00 #define HANGUL_L_BASE 0x1100 #define HANGUL_V_BASE 0x1161 #define HANGUL_T_BASE 0x11a7 #define HANGUL_L_COUNT 19 #define HANGUL_V_COUNT 21 #define HANGUL_T_COUNT 28 #define HANGUL_N_COUNT (HANGUL_V_COUNT * HANGUL_T_COUNT) #define HANGUL_S_COUNT (HANGUL_L_COUNT * HANGUL_N_COUNT) /** * is_precomposed_hangul - Check if a character is a Hangul syllable * @utf32char: character to check * * This function was adapted from sample code in section 3.12 of the * Unicode Standard, version 9.0. * * Copyright (C) 1991-2018 Unicode, Inc. All rights reserved. Distributed * under the Terms of Use in http://www.unicode.org/copyright.html. */ static bool is_precomposed_hangul(unicode_t utf32char) { int index; index = utf32char - HANGUL_S_BASE; return (index >= 0 && index < HANGUL_S_COUNT); } /* Signals the end of the normalization for a single character */ #define NORM_END (unicode_t)(-1) /** * decompose_hangul - Decompose a Hangul syllable * @utf32char: Hangul syllable to decompose * @off: offset of the wanted character from the decomposition * * Returns the single character at offset @off in the decomposition of * @utf32char, or NORM_END if this offset is past the end. * * This function was adapted from sample code in section 3.12 of the * Unicode Standard, version 9.0. * * Copyright (C) 1991-2018 Unicode, Inc. All rights reserved. Distributed * under the Terms of Use in http://www.unicode.org/copyright.html. */ static unicode_t decompose_hangul(unicode_t utf32char, int off) { int index; int l, v, t; index = utf32char - HANGUL_S_BASE; l = HANGUL_L_BASE + index / HANGUL_N_COUNT; if (off == 0) return l; v = HANGUL_V_BASE + (index % HANGUL_N_COUNT) / HANGUL_T_COUNT; if (off == 1) return v; t = HANGUL_T_BASE + index % HANGUL_T_COUNT; if (off == 2 && t != HANGUL_T_BASE) return t; return NORM_END; } /** * normalize_char - Normalize a unicode character * @utf32char: character to normalize * @off: offset of the wanted character from the normalization * @case_fold: case fold the char? * * Returns the single character at offset @off in the normalization of * @utf32char, or NORM_END if this offset is past the end. */ static unicode_t normalize_char(unicode_t utf32char, int off, bool case_fold) { int nfd_len; unicode_t *nfd, *cf; u16 pos; int ret; if (is_precomposed_hangul(utf32char)) /* Hangul has no case */ return decompose_hangul(utf32char, off); ret = trie_find(nfd_trie, utf32char, &pos, false /* is_ccc */); if (!ret) { /* The decomposition is just the same character */ nfd_len = 1; nfd = &utf32char; } else { nfd_len = ret; nfd = &nfd_values[pos]; } if (!case_fold) { if (off < nfd_len) return nfd[off]; return NORM_END; } for (; nfd_len > 0; nfd++, nfd_len--) { int cf_len; ret = trie_find(cf_trie, *nfd, &pos, false /* is_ccc */); if (!ret) { /* The case folding is just the same character */ cf_len = 1; cf = nfd; } else { cf_len = ret; cf = &cf_values[pos]; } if (off < cf_len) return cf[off]; off -= cf_len; } return NORM_END; } /** * get_normalization_length - Count the characters until the next starter * @utf8str: string to normalize, may begin with several starters * @case_fold: true if the count should consider case folding * * Returns the number of unicode characters in the normalization of the * substring that begins at @utf8str and ends at the first nonconsecutive * starter. Or 0 if the substring has invalid UTF-8. */ static int get_normalization_length(const char *utf8str, bool case_fold) { int utf8len, pos, norm_len = 0; bool starters_over = false; unicode_t utf32char; while (1) { if (!*utf8str) return norm_len; utf8len = utf8_to_utf32((u8 *)utf8str, 4, &utf32char); if (utf8len < 0) /* Invalid unicode; don't normalize anything */ return 0; for (pos = 0;; pos++, norm_len++) { unicode_t utf32norm; u8 ccc; utf32norm = normalize_char(utf32char, pos, case_fold); if (utf32norm == NORM_END) break; trie_find(ccc_trie, utf32norm, &ccc, true /* is_ccc */); if (ccc != 0) starters_over = true; else if (starters_over) /* Reached the next starter */ return norm_len; } utf8str += utf8len; } } /** * normalize_next - Return the next normalized character from a string * @cursor: unicode cursor for the string * @case_fold: case fold the string? * * Sets @cursor->length to the length of the normalized substring between * @cursor->utf8curr and the first nonconsecutive starter. Returns a single * normalized character, setting @cursor->last_ccc and @cursor->last_pos to * its CCC and position in the substring. When the end of the substring is * reached, updates @cursor->utf8curr to point to the beginning of the next * one. * * Returns 0 if the substring has invalid UTF-8. */ unicode_t normalize_next(struct unicursor *cursor, bool case_fold) { const char *utf8str = cursor->utf8curr; int str_pos, min_pos = -1; unicode_t utf32min = 0; u8 min_ccc; new_starter: if (likely(isascii(*utf8str))) { cursor->utf8curr = utf8str + 1; if (case_fold) return tolower(*utf8str); return *utf8str; } if (cursor->length < 0) { cursor->length = get_normalization_length(utf8str, case_fold); if (cursor->length == 0) return 0; } str_pos = 0; min_ccc = 0xFF; /* Above all possible ccc's */ while (1) { unicode_t utf32char; int utf8len, pos; utf8len = utf8_to_utf32((u8 *)utf8str, 4, &utf32char); for (pos = 0;; pos++, str_pos++) { unicode_t utf32norm; u8 ccc; utf32norm = normalize_char(utf32char, pos, case_fold); if (utf32norm == NORM_END) break; trie_find(ccc_trie, utf32norm, &ccc, true /* is_ccc */); if (ccc >= min_ccc || ccc < cursor->last_ccc) continue; if (ccc > cursor->last_ccc || str_pos > cursor->last_pos) { utf32min = utf32norm; min_ccc = ccc; min_pos = str_pos; } } utf8str += utf8len; if (str_pos == cursor->length) { /* Reached the following starter */ if (min_ccc != 0xFF) { /* Not done with this substring yet */ cursor->last_ccc = min_ccc; cursor->last_pos = min_pos; return utf32min; } /* Continue from the next starter */ init_unicursor(cursor, utf8str); goto new_starter; } } } /* * The following arrays were built with data provided by the Unicode Standard, * version 9.0. * * Copyright (C) 1991-2018 Unicode, Inc. All rights reserved. Distributed * under the Terms of Use in http://www.unicode.org/copyright.html. */ static u16 nfd_trie[] = { /* Node for range 0x_____ */ 0x0001, 0x0002, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0____ */ 0x0004, 0x0005, 0x0006, 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, /* Node for range 0x1____ */ 0x0000, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000a, 0x0000, 0x0000, /* Node for range 0x2____ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000b, /* Node for range 0x00___ */ 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0000, 0x0011, 0x0000, 0x0000, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0000, 0x0017, /* Node for range 0x01___ */ 0x0018, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0019, 0x0000, 0x0000, 0x001a, 0x001b, /* Node for range 0x02___ */ 0x001c, 0x001d, 0x001e, 0x001f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x03___ */ 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0f___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0022, 0x0023, 0x0024, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x11___ */ 0x0025, 0x0026, 0x0000, 0x0027, 0x0028, 0x0029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1d___ */ 0x0000, 0x002a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x2f___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x002b, 0x002c, 0x002d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x000__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x002e, 0x002f, 0x0030, 0x0031, /* Node for range 0x001__ */ 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0000, 0x0000, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, /* Node for range 0x002__ */ 0x0040, 0x0041, 0x0042, 0x0043, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x003__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0044, 0x0000, 0x0000, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x0000, 0x0000, /* Node for range 0x004__ */ 0x004c, 0x004d, 0x0000, 0x004e, 0x0000, 0x004f, 0x0000, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000, 0x0051, 0x0052, 0x0053, 0x0054, /* Node for range 0x006__ */ 0x0000, 0x0000, 0x0055, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0056, 0x0057, 0x0000, 0x0000, /* Node for range 0x009__ */ 0x0000, 0x0000, 0x0058, 0x0059, 0x0000, 0x005a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x005b, 0x005c, 0x0000, 0x0000, /* Node for range 0x00a__ */ 0x0000, 0x0000, 0x0000, 0x005d, 0x0000, 0x005e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00b__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x005f, 0x0060, 0x0000, 0x0000, 0x0000, 0x0061, 0x0000, 0x0000, 0x0062, 0x0000, 0x0000, 0x0000, /* Node for range 0x00c__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0063, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0064, 0x0000, 0x0000, 0x0000, /* Node for range 0x00d__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0065, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0066, 0x0000, 0x0000, /* Node for range 0x00f__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x010__ */ 0x0000, 0x0000, 0x006f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01b__ */ 0x0070, 0x0071, 0x0000, 0x0072, 0x0073, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01e__ */ 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, /* Node for range 0x01f__ */ 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, /* Node for range 0x020__ */ 0x0094, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x021__ */ 0x0000, 0x0000, 0x0095, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0096, 0x0097, 0x0000, 0x0098, 0x0000, 0x0000, 0x0000, /* Node for range 0x022__ */ 0x0099, 0x0000, 0x009a, 0x0000, 0x009b, 0x0000, 0x009c, 0x009d, 0x009e, 0x0000, 0x009f, 0x0000, 0x0000, 0x0000, 0x00a0, 0x0000, /* Node for range 0x023__ */ 0x0000, 0x0000, 0x00a1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x02a__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a2, 0x0000, 0x0000, /* Node for range 0x030__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x0000, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x0000, 0x00ac, /* Node for range 0x0f9__ */ 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, /* Node for range 0x0fa__ */ 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x0000, 0x0000, /* Node for range 0x0fb__ */ 0x0000, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x110__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00cf, 0x00d0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x111__ */ 0x0000, 0x0000, 0x00d1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x113__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x00d2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x114__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d3, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x115__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d4, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1d1__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d5, 0x00d6, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d7, 0x00d8, 0x0000, 0x0000, 0x0000, /* Node for range 0x2f8__ */ 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, /* Node for range 0x2f9__ */ 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, /* Node for range 0x2fa__ */ 0x00f9, 0x00fa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x000c_ */ 0x0002, 0x0012, 0x0022, 0x0032, 0x0042, 0x0052, 0x0000, 0x0062, 0x0072, 0x0082, 0x0092, 0x00a2, 0x00b2, 0x00c2, 0x00d2, 0x00e2, /* Node for range 0x000d_ */ 0x0000, 0x00f2, 0x0102, 0x0112, 0x0122, 0x0132, 0x0142, 0x0000, 0x0000, 0x0152, 0x0162, 0x0172, 0x0182, 0x0192, 0x0000, 0x0000, /* Node for range 0x000e_ */ 0x01a2, 0x01b2, 0x01c2, 0x01d2, 0x01e2, 0x01f2, 0x0000, 0x0202, 0x0212, 0x0222, 0x0232, 0x0242, 0x0252, 0x0262, 0x0272, 0x0282, /* Node for range 0x000f_ */ 0x0000, 0x0292, 0x02a2, 0x02b2, 0x02c2, 0x02d2, 0x02e2, 0x0000, 0x0000, 0x02f2, 0x0302, 0x0312, 0x0322, 0x0332, 0x0000, 0x0342, /* Node for range 0x0010_ */ 0x0352, 0x0362, 0x0372, 0x0382, 0x0392, 0x03a2, 0x03b2, 0x03c2, 0x03d2, 0x03e2, 0x03f2, 0x0402, 0x0412, 0x0422, 0x0432, 0x0442, /* Node for range 0x0011_ */ 0x0000, 0x0000, 0x0452, 0x0462, 0x0472, 0x0482, 0x0492, 0x04a2, 0x04b2, 0x04c2, 0x04d2, 0x04e2, 0x04f2, 0x0502, 0x0512, 0x0522, /* Node for range 0x0012_ */ 0x0532, 0x0542, 0x0552, 0x0562, 0x0572, 0x0582, 0x0000, 0x0000, 0x0592, 0x05a2, 0x05b2, 0x05c2, 0x05d2, 0x05e2, 0x05f2, 0x0602, /* Node for range 0x0013_ */ 0x0612, 0x0000, 0x0000, 0x0000, 0x0622, 0x0632, 0x0642, 0x0652, 0x0000, 0x0662, 0x0672, 0x0682, 0x0692, 0x06a2, 0x06b2, 0x0000, /* Node for range 0x0014_ */ 0x0000, 0x0000, 0x0000, 0x06c2, 0x06d2, 0x06e2, 0x06f2, 0x0702, 0x0712, 0x0000, 0x0000, 0x0000, 0x0722, 0x0732, 0x0742, 0x0752, /* Node for range 0x0015_ */ 0x0762, 0x0772, 0x0000, 0x0000, 0x0782, 0x0792, 0x07a2, 0x07b2, 0x07c2, 0x07d2, 0x07e2, 0x07f2, 0x0802, 0x0812, 0x0822, 0x0832, /* Node for range 0x0016_ */ 0x0842, 0x0852, 0x0862, 0x0872, 0x0882, 0x0892, 0x0000, 0x0000, 0x08a2, 0x08b2, 0x08c2, 0x08d2, 0x08e2, 0x08f2, 0x0902, 0x0912, /* Node for range 0x0017_ */ 0x0922, 0x0932, 0x0942, 0x0952, 0x0962, 0x0972, 0x0982, 0x0992, 0x09a2, 0x09b2, 0x09c2, 0x09d2, 0x09e2, 0x09f2, 0x0a02, 0x0000, /* Node for range 0x001a_ */ 0x0a12, 0x0a22, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0a32, /* Node for range 0x001b_ */ 0x0a42, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x001c_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0a52, 0x0a62, 0x0a72, /* Node for range 0x001d_ */ 0x0a82, 0x0a92, 0x0aa2, 0x0ab2, 0x0ac2, 0x0ad3, 0x0aeb, 0x0b03, 0x0b1b, 0x0b33, 0x0b4b, 0x0b63, 0x0b7b, 0x0000, 0x0b93, 0x0bab, /* Node for range 0x001e_ */ 0x0bc3, 0x0bdb, 0x0bf2, 0x0c02, 0x0000, 0x0000, 0x0c12, 0x0c22, 0x0c32, 0x0c42, 0x0c52, 0x0c62, 0x0c73, 0x0c8b, 0x0ca2, 0x0cb2, /* Node for range 0x001f_ */ 0x0cc2, 0x0000, 0x0000, 0x0000, 0x0cd2, 0x0ce2, 0x0000, 0x0000, 0x0cf2, 0x0d02, 0x0d13, 0x0d2b, 0x0d42, 0x0d52, 0x0d62, 0x0d72, /* Node for range 0x0020_ */ 0x0d82, 0x0d92, 0x0da2, 0x0db2, 0x0dc2, 0x0dd2, 0x0de2, 0x0df2, 0x0e02, 0x0e12, 0x0e22, 0x0e32, 0x0e42, 0x0e52, 0x0e62, 0x0e72, /* Node for range 0x0021_ */ 0x0e82, 0x0e92, 0x0ea2, 0x0eb2, 0x0ec2, 0x0ed2, 0x0ee2, 0x0ef2, 0x0f02, 0x0f12, 0x0f22, 0x0f32, 0x0000, 0x0000, 0x0f42, 0x0f52, /* Node for range 0x0022_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0f62, 0x0f72, 0x0f82, 0x0f92, 0x0fa3, 0x0fbb, 0x0fd3, 0x0feb, 0x1002, 0x1012, /* Node for range 0x0023_ */ 0x1023, 0x103b, 0x1052, 0x1062, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0034_ */ 0x1071, 0x1079, 0x0000, 0x1081, 0x108a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0037_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x1099, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x10a1, 0x0000, /* Node for range 0x0038_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x10aa, 0x10ba, 0x10c9, 0x10d2, 0x10e2, 0x10f2, 0x0000, 0x1102, 0x0000, 0x1112, 0x1122, /* Node for range 0x0039_ */ 0x1133, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x003a_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x114a, 0x115a, 0x116a, 0x117a, 0x118a, 0x119a, /* Node for range 0x003b_ */ 0x11ab, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x003c_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x11c2, 0x11d2, 0x11e2, 0x11f2, 0x1202, 0x0000, /* Node for range 0x003d_ */ 0x0000, 0x0000, 0x0000, 0x1212, 0x1222, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0040_ */ 0x1232, 0x1242, 0x0000, 0x1252, 0x0000, 0x0000, 0x0000, 0x1262, 0x0000, 0x0000, 0x0000, 0x0000, 0x1272, 0x1282, 0x1292, 0x0000, /* Node for range 0x0041_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x12a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0043_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x12b2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0045_ */ 0x12c2, 0x12d2, 0x0000, 0x12e2, 0x0000, 0x0000, 0x0000, 0x12f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x1302, 0x1312, 0x1322, 0x0000, /* Node for range 0x0047_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1332, 0x1342, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x004c_ */ 0x0000, 0x1352, 0x1362, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x004d_ */ 0x1372, 0x1382, 0x1392, 0x13a2, 0x0000, 0x0000, 0x13b2, 0x13c2, 0x0000, 0x0000, 0x13d2, 0x13e2, 0x13f2, 0x1402, 0x1412, 0x1422, /* Node for range 0x004e_ */ 0x0000, 0x0000, 0x1432, 0x1442, 0x1452, 0x1462, 0x1472, 0x1482, 0x0000, 0x0000, 0x1492, 0x14a2, 0x14b2, 0x14c2, 0x14d2, 0x14e2, /* Node for range 0x004f_ */ 0x14f2, 0x1502, 0x1512, 0x1522, 0x1532, 0x1542, 0x0000, 0x0000, 0x1552, 0x1562, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0062_ */ 0x0000, 0x0000, 0x1572, 0x1582, 0x1592, 0x15a2, 0x15b2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x006c_ */ 0x15c2, 0x0000, 0x15d2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x006d_ */ 0x0000, 0x0000, 0x0000, 0x15e2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0092_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0093_ */ 0x0000, 0x1602, 0x0000, 0x0000, 0x1612, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0095_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1622, 0x1632, 0x1642, 0x1652, 0x1662, 0x1672, 0x1682, 0x1692, /* Node for range 0x009c_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x16a2, 0x16b2, 0x0000, 0x0000, 0x0000, /* Node for range 0x009d_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x16c2, 0x16d2, 0x0000, 0x16e2, /* Node for range 0x00a3_ */ 0x0000, 0x0000, 0x0000, 0x16f2, 0x0000, 0x0000, 0x1702, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00a5_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1712, 0x1722, 0x1732, 0x0000, 0x0000, 0x1742, 0x0000, /* Node for range 0x00b4_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1752, 0x0000, 0x0000, 0x1762, 0x1772, 0x0000, 0x0000, 0x0000, /* Node for range 0x00b5_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1782, 0x1792, 0x0000, 0x0000, /* Node for range 0x00b9_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x17a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00bc_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x17b2, 0x17c2, 0x17d2, 0x0000, 0x0000, 0x0000, /* Node for range 0x00c4_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x17e2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00cc_ */ 0x17f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1802, 0x1812, 0x0000, 0x1822, 0x1833, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00d4_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x184a, 0x185a, 0x186a, 0x0000, 0x0000, 0x0000, /* Node for range 0x00dd_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x187a, 0x0000, 0x188a, 0x189b, 0x18b2, 0x0000, /* Node for range 0x00f4_ */ 0x0000, 0x0000, 0x0000, 0x18c2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x18d2, 0x0000, 0x0000, /* Node for range 0x00f5_ */ 0x0000, 0x0000, 0x18e2, 0x0000, 0x0000, 0x0000, 0x0000, 0x18f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x1902, 0x0000, 0x0000, 0x0000, /* Node for range 0x00f6_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1912, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00f7_ */ 0x0000, 0x0000, 0x0000, 0x1922, 0x0000, 0x1932, 0x1942, 0x0000, 0x1952, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00f8_ */ 0x0000, 0x1962, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x00f9_ */ 0x0000, 0x0000, 0x0000, 0x1972, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1982, 0x0000, 0x0000, /* Node for range 0x00fa_ */ 0x0000, 0x0000, 0x1992, 0x0000, 0x0000, 0x0000, 0x0000, 0x19a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x19b2, 0x0000, 0x0000, 0x0000, /* Node for range 0x00fb_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x19c2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0102_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x19d2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01b0_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x19e2, 0x0000, 0x19f2, 0x0000, 0x1a02, 0x0000, 0x1a12, 0x0000, 0x1a22, 0x0000, /* Node for range 0x01b1_ */ 0x0000, 0x0000, 0x1a32, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01b3_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1a42, 0x0000, 0x1a52, 0x0000, 0x0000, /* Node for range 0x01b4_ */ 0x1a62, 0x1a72, 0x0000, 0x1a82, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01e0_ */ 0x1a92, 0x1aa2, 0x1ab2, 0x1ac2, 0x1ad2, 0x1ae2, 0x1af2, 0x1b02, 0x1b13, 0x1b2b, 0x1b42, 0x1b52, 0x1b62, 0x1b72, 0x1b82, 0x1b92, /* Node for range 0x01e1_ */ 0x1ba2, 0x1bb2, 0x1bc2, 0x1bd2, 0x1be3, 0x1bfb, 0x1c13, 0x1c2b, 0x1c42, 0x1c52, 0x1c62, 0x1c72, 0x1c83, 0x1c9b, 0x1cb2, 0x1cc2, /* Node for range 0x01e2_ */ 0x1cd2, 0x1ce2, 0x1cf2, 0x1d02, 0x1d12, 0x1d22, 0x1d32, 0x1d42, 0x1d52, 0x1d62, 0x1d72, 0x1d82, 0x1d92, 0x1da2, 0x1db3, 0x1dcb, /* Node for range 0x01e3_ */ 0x1de2, 0x1df2, 0x1e02, 0x1e12, 0x1e22, 0x1e32, 0x1e42, 0x1e52, 0x1e63, 0x1e7b, 0x1e92, 0x1ea2, 0x1eb2, 0x1ec2, 0x1ed2, 0x1ee2, /* Node for range 0x01e4_ */ 0x1ef2, 0x1f02, 0x1f12, 0x1f22, 0x1f32, 0x1f42, 0x1f52, 0x1f62, 0x1f72, 0x1f82, 0x1f92, 0x1fa2, 0x1fb3, 0x1fcb, 0x1fe3, 0x1ffb, /* Node for range 0x01e5_ */ 0x2013, 0x202b, 0x2043, 0x205b, 0x2072, 0x2082, 0x2092, 0x20a2, 0x20b2, 0x20c2, 0x20d2, 0x20e2, 0x20f3, 0x210b, 0x2122, 0x2132, /* Node for range 0x01e6_ */ 0x2142, 0x2152, 0x2162, 0x2172, 0x2183, 0x219b, 0x21b3, 0x21cb, 0x21e3, 0x21fb, 0x2212, 0x2222, 0x2232, 0x2242, 0x2252, 0x2262, /* Node for range 0x01e7_ */ 0x2272, 0x2282, 0x2292, 0x22a2, 0x22b2, 0x22c2, 0x22d2, 0x22e2, 0x22f3, 0x230b, 0x2323, 0x233b, 0x2352, 0x2362, 0x2372, 0x2382, /* Node for range 0x01e8_ */ 0x2392, 0x23a2, 0x23b2, 0x23c2, 0x23d2, 0x23e2, 0x23f2, 0x2402, 0x2412, 0x2422, 0x2432, 0x2442, 0x2452, 0x2462, 0x2472, 0x2482, /* Node for range 0x01e9_ */ 0x2492, 0x24a2, 0x24b2, 0x24c2, 0x24d2, 0x24e2, 0x24f2, 0x2502, 0x2512, 0x2522, 0x0000, 0x2532, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01ea_ */ 0x2542, 0x2552, 0x2562, 0x2572, 0x2583, 0x259b, 0x25b3, 0x25cb, 0x25e3, 0x25fb, 0x2613, 0x262b, 0x2643, 0x265b, 0x2673, 0x268b, /* Node for range 0x01eb_ */ 0x26a3, 0x26bb, 0x26d3, 0x26eb, 0x2703, 0x271b, 0x2733, 0x274b, 0x2762, 0x2772, 0x2782, 0x2792, 0x27a2, 0x27b2, 0x27c3, 0x27db, /* Node for range 0x01ec_ */ 0x27f3, 0x280b, 0x2823, 0x283b, 0x2853, 0x286b, 0x2883, 0x289b, 0x28b2, 0x28c2, 0x28d2, 0x28e2, 0x28f2, 0x2902, 0x2912, 0x2922, /* Node for range 0x01ed_ */ 0x2933, 0x294b, 0x2963, 0x297b, 0x2993, 0x29ab, 0x29c3, 0x29db, 0x29f3, 0x2a0b, 0x2a23, 0x2a3b, 0x2a53, 0x2a6b, 0x2a83, 0x2a9b, /* Node for range 0x01ee_ */ 0x2ab3, 0x2acb, 0x2ae3, 0x2afb, 0x2b12, 0x2b22, 0x2b32, 0x2b42, 0x2b53, 0x2b6b, 0x2b83, 0x2b9b, 0x2bb3, 0x2bcb, 0x2be3, 0x2bfb, /* Node for range 0x01ef_ */ 0x2c13, 0x2c2b, 0x2c42, 0x2c52, 0x2c62, 0x2c72, 0x2c82, 0x2c92, 0x2ca2, 0x2cb2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01f0_ */ 0x2cc2, 0x2cd2, 0x2ce3, 0x2cfb, 0x2d13, 0x2d2b, 0x2d43, 0x2d5b, 0x2d72, 0x2d82, 0x2d93, 0x2dab, 0x2dc3, 0x2ddb, 0x2df3, 0x2e0b, /* Node for range 0x01f1_ */ 0x2e22, 0x2e32, 0x2e43, 0x2e5b, 0x2e73, 0x2e8b, 0x0000, 0x0000, 0x2ea2, 0x2eb2, 0x2ec3, 0x2edb, 0x2ef3, 0x2f0b, 0x0000, 0x0000, /* Node for range 0x01f2_ */ 0x2f22, 0x2f32, 0x2f43, 0x2f5b, 0x2f73, 0x2f8b, 0x2fa3, 0x2fbb, 0x2fd2, 0x2fe2, 0x2ff3, 0x300b, 0x3023, 0x303b, 0x3053, 0x306b, /* Node for range 0x01f3_ */ 0x3082, 0x3092, 0x30a3, 0x30bb, 0x30d3, 0x30eb, 0x3103, 0x311b, 0x3132, 0x3142, 0x3153, 0x316b, 0x3183, 0x319b, 0x31b3, 0x31cb, /* Node for range 0x01f4_ */ 0x31e2, 0x31f2, 0x3203, 0x321b, 0x3233, 0x324b, 0x0000, 0x0000, 0x3262, 0x3272, 0x3283, 0x329b, 0x32b3, 0x32cb, 0x0000, 0x0000, /* Node for range 0x01f5_ */ 0x32e2, 0x32f2, 0x3303, 0x331b, 0x3333, 0x334b, 0x3363, 0x337b, 0x0000, 0x3392, 0x0000, 0x33a3, 0x0000, 0x33bb, 0x0000, 0x33d3, /* Node for range 0x01f6_ */ 0x33ea, 0x33fa, 0x340b, 0x3423, 0x343b, 0x3453, 0x346b, 0x3483, 0x349a, 0x34aa, 0x34bb, 0x34d3, 0x34eb, 0x3503, 0x351b, 0x3533, /* Node for range 0x01f7_ */ 0x354a, 0x355a, 0x356a, 0x357a, 0x358a, 0x359a, 0x35aa, 0x35ba, 0x35ca, 0x35da, 0x35ea, 0x35fa, 0x360a, 0x361a, 0x0000, 0x0000, /* Node for range 0x01f8_ */ 0x362b, 0x3643, 0x365c, 0x367c, 0x369c, 0x36bc, 0x36dc, 0x36fc, 0x371b, 0x3733, 0x374c, 0x376c, 0x378c, 0x37ac, 0x37cc, 0x37ec, /* Node for range 0x01f9_ */ 0x380b, 0x3823, 0x383c, 0x385c, 0x387c, 0x389c, 0x38bc, 0x38dc, 0x38fb, 0x3913, 0x392c, 0x394c, 0x396c, 0x398c, 0x39ac, 0x39cc, /* Node for range 0x01fa_ */ 0x39eb, 0x3a03, 0x3a1c, 0x3a3c, 0x3a5c, 0x3a7c, 0x3a9c, 0x3abc, 0x3adb, 0x3af3, 0x3b0c, 0x3b2c, 0x3b4c, 0x3b6c, 0x3b8c, 0x3bac, /* Node for range 0x01fb_ */ 0x3bca, 0x3bda, 0x3beb, 0x3c02, 0x3c13, 0x0000, 0x3c2a, 0x3c3b, 0x3c52, 0x3c62, 0x3c72, 0x3c82, 0x3c92, 0x0000, 0x3ca1, 0x0000, /* Node for range 0x01fc_ */ 0x0000, 0x3caa, 0x3cbb, 0x3cd2, 0x3ce3, 0x0000, 0x3cfa, 0x3d0b, 0x3d22, 0x3d32, 0x3d42, 0x3d52, 0x3d62, 0x3d72, 0x3d82, 0x3d92, /* Node for range 0x01fd_ */ 0x3da2, 0x3db2, 0x3dc3, 0x3ddb, 0x0000, 0x0000, 0x3df2, 0x3e03, 0x3e1a, 0x3e2a, 0x3e3a, 0x3e4a, 0x0000, 0x3e5a, 0x3e6a, 0x3e7a, /* Node for range 0x01fe_ */ 0x3e8a, 0x3e9a, 0x3eab, 0x3ec3, 0x3eda, 0x3eea, 0x3efa, 0x3f0b, 0x3f22, 0x3f32, 0x3f42, 0x3f52, 0x3f62, 0x3f72, 0x3f82, 0x3f91, /* Node for range 0x01ff_ */ 0x0000, 0x0000, 0x3f9b, 0x3fb2, 0x3fc3, 0x0000, 0x3fda, 0x3feb, 0x4002, 0x4012, 0x4022, 0x4032, 0x4042, 0x4051, 0x0000, 0x0000, /* Node for range 0x0200_ */ 0x4059, 0x4061, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0212_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4069, 0x0000, 0x0000, 0x0000, 0x4071, 0x407a, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0219_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x408a, 0x409a, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x021a_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x40aa, 0x0000, /* Node for range 0x021c_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x40ba, 0x40ca, 0x40da, /* Node for range 0x0220_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x40ea, 0x0000, 0x0000, 0x0000, 0x0000, 0x40fa, 0x0000, 0x0000, 0x410a, 0x0000, 0x0000, 0x0000, /* Node for range 0x0222_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x411a, 0x0000, 0x412a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0224_ */ 0x0000, 0x413a, 0x0000, 0x0000, 0x414a, 0x0000, 0x0000, 0x415a, 0x0000, 0x416a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0226_ */ 0x417a, 0x0000, 0x418a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x419a, 0x41aa, 0x41ba, /* Node for range 0x0227_ */ 0x41ca, 0x41da, 0x0000, 0x0000, 0x41ea, 0x41fa, 0x0000, 0x0000, 0x420a, 0x421a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0228_ */ 0x422a, 0x423a, 0x0000, 0x0000, 0x424a, 0x425a, 0x0000, 0x0000, 0x426a, 0x427a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x022a_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x428a, 0x429a, 0x42aa, 0x42ba, /* Node for range 0x022e_ */ 0x42ca, 0x42da, 0x42ea, 0x42fa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x430a, 0x431a, 0x432a, 0x433a, 0x0000, 0x0000, /* Node for range 0x0232_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4349, 0x4351, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x02ad_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x435a, 0x0000, 0x0000, 0x0000, /* Node for range 0x0304_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x436a, 0x0000, 0x437a, 0x0000, /* Node for range 0x0305_ */ 0x438a, 0x0000, 0x439a, 0x0000, 0x43aa, 0x0000, 0x43ba, 0x0000, 0x43ca, 0x0000, 0x43da, 0x0000, 0x43ea, 0x0000, 0x43fa, 0x0000, /* Node for range 0x0306_ */ 0x440a, 0x0000, 0x441a, 0x0000, 0x0000, 0x442a, 0x0000, 0x443a, 0x0000, 0x444a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0307_ */ 0x445a, 0x446a, 0x0000, 0x447a, 0x448a, 0x0000, 0x449a, 0x44aa, 0x0000, 0x44ba, 0x44ca, 0x0000, 0x44da, 0x44ea, 0x0000, 0x0000, /* Node for range 0x0309_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x44fa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x450a, 0x0000, /* Node for range 0x030a_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x451a, 0x0000, 0x452a, 0x0000, /* Node for range 0x030b_ */ 0x453a, 0x0000, 0x454a, 0x0000, 0x455a, 0x0000, 0x456a, 0x0000, 0x457a, 0x0000, 0x458a, 0x0000, 0x459a, 0x0000, 0x45aa, 0x0000, /* Node for range 0x030c_ */ 0x45ba, 0x0000, 0x45ca, 0x0000, 0x0000, 0x45da, 0x0000, 0x45ea, 0x0000, 0x45fa, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x030d_ */ 0x460a, 0x461a, 0x0000, 0x462a, 0x463a, 0x0000, 0x464a, 0x465a, 0x0000, 0x466a, 0x467a, 0x0000, 0x468a, 0x469a, 0x0000, 0x0000, /* Node for range 0x030f_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x46aa, 0x0000, 0x0000, 0x46ba, 0x46ca, 0x46da, 0x46ea, 0x0000, 0x0000, 0x0000, 0x46fa, 0x0000, /* Node for range 0x0f90_ */ 0x4709, 0x4711, 0x4719, 0x4721, 0x4729, 0x4731, 0x4739, 0x4741, 0x4749, 0x4751, 0x4759, 0x4761, 0x4769, 0x4771, 0x4779, 0x4781, /* Node for range 0x0f91_ */ 0x4789, 0x4791, 0x4799, 0x47a1, 0x47a9, 0x47b1, 0x47b9, 0x47c1, 0x47c9, 0x47d1, 0x47d9, 0x47e1, 0x47e9, 0x47f1, 0x47f9, 0x4801, /* Node for range 0x0f92_ */ 0x4809, 0x4811, 0x4819, 0x4821, 0x4829, 0x4831, 0x4839, 0x4841, 0x4849, 0x4851, 0x4859, 0x4861, 0x4869, 0x4871, 0x4879, 0x4881, /* Node for range 0x0f93_ */ 0x4889, 0x4891, 0x4899, 0x48a1, 0x48a9, 0x48b1, 0x48b9, 0x48c1, 0x48c9, 0x48d1, 0x48d9, 0x48e1, 0x48e9, 0x48f1, 0x48f9, 0x4901, /* Node for range 0x0f94_ */ 0x4909, 0x4911, 0x4919, 0x4921, 0x4929, 0x4931, 0x4939, 0x4941, 0x4949, 0x4951, 0x4959, 0x4961, 0x4969, 0x4971, 0x4979, 0x4981, /* Node for range 0x0f95_ */ 0x4989, 0x4991, 0x4999, 0x49a1, 0x49a9, 0x49b1, 0x49b9, 0x49c1, 0x49c9, 0x49d1, 0x49d9, 0x49e1, 0x49e9, 0x49f1, 0x49f9, 0x4a01, /* Node for range 0x0f96_ */ 0x4a09, 0x4a11, 0x4a19, 0x4a21, 0x4a29, 0x4a31, 0x4a39, 0x4a41, 0x4a49, 0x4a51, 0x4a59, 0x4a61, 0x4a69, 0x4a71, 0x4a79, 0x4a81, /* Node for range 0x0f97_ */ 0x4a89, 0x4a91, 0x4a99, 0x4aa1, 0x4aa9, 0x4ab1, 0x4ab9, 0x4ac1, 0x4ac9, 0x4ad1, 0x4ad9, 0x4ae1, 0x4ae9, 0x4af1, 0x4af9, 0x4b01, /* Node for range 0x0f98_ */ 0x4b09, 0x4b11, 0x4b19, 0x4b21, 0x4b29, 0x4b31, 0x4b39, 0x4b41, 0x4b49, 0x4b51, 0x4b59, 0x4b61, 0x4b69, 0x4b71, 0x4b79, 0x4b81, /* Node for range 0x0f99_ */ 0x4b89, 0x4b91, 0x4b99, 0x4ba1, 0x4ba9, 0x4bb1, 0x4bb9, 0x4bc1, 0x4bc9, 0x4bd1, 0x4bd9, 0x4be1, 0x4be9, 0x4bf1, 0x4bf9, 0x4c01, /* Node for range 0x0f9a_ */ 0x4c09, 0x4c11, 0x4c19, 0x4c21, 0x4c29, 0x4c31, 0x4c39, 0x4c41, 0x4c49, 0x4c51, 0x4c59, 0x4c61, 0x4c69, 0x4c71, 0x4c79, 0x4c81, /* Node for range 0x0f9b_ */ 0x4c89, 0x4c91, 0x4c99, 0x4ca1, 0x4ca9, 0x4cb1, 0x4cb9, 0x4cc1, 0x4cc9, 0x4cd1, 0x4cd9, 0x4ce1, 0x4ce9, 0x4cf1, 0x4cf9, 0x4d01, /* Node for range 0x0f9c_ */ 0x4d09, 0x4d11, 0x4d19, 0x4d21, 0x4d29, 0x4d31, 0x4d39, 0x4d41, 0x4d49, 0x4d51, 0x4d59, 0x4d61, 0x4d69, 0x4d71, 0x4d79, 0x4d81, /* Node for range 0x0f9d_ */ 0x4d89, 0x4d91, 0x4d99, 0x4da1, 0x4da9, 0x4db1, 0x4db9, 0x4dc1, 0x4dc9, 0x4dd1, 0x4dd9, 0x4de1, 0x4de9, 0x4df1, 0x4df9, 0x4e01, /* Node for range 0x0f9e_ */ 0x4e09, 0x4e11, 0x4e19, 0x4e21, 0x4e29, 0x4e31, 0x4e39, 0x4e41, 0x4e49, 0x4e51, 0x4e59, 0x4e61, 0x4e69, 0x4e71, 0x4e79, 0x4e81, /* Node for range 0x0f9f_ */ 0x4e89, 0x4e91, 0x4e99, 0x4ea1, 0x4ea9, 0x4eb1, 0x4eb9, 0x4ec1, 0x4ec9, 0x4ed1, 0x4ed9, 0x4ee1, 0x4ee9, 0x4ef1, 0x4ef9, 0x4f01, /* Node for range 0x0fa0_ */ 0x4f09, 0x4f11, 0x4f19, 0x4f21, 0x4f29, 0x4f31, 0x4f39, 0x4f41, 0x4f49, 0x4f51, 0x4f59, 0x4f61, 0x4f69, 0x4f71, 0x0000, 0x0000, /* Node for range 0x0fa1_ */ 0x4f79, 0x0000, 0x4f81, 0x0000, 0x0000, 0x4f89, 0x4f91, 0x4f99, 0x4fa1, 0x4fa9, 0x4fb1, 0x4fb9, 0x4fc1, 0x4fc9, 0x4fd1, 0x0000, /* Node for range 0x0fa2_ */ 0x4fd9, 0x0000, 0x4fe1, 0x0000, 0x0000, 0x4fe9, 0x4ff1, 0x0000, 0x0000, 0x0000, 0x4ff9, 0x5001, 0x5009, 0x5011, 0x5019, 0x5021, /* Node for range 0x0fa3_ */ 0x5029, 0x5031, 0x5039, 0x5041, 0x5049, 0x5051, 0x5059, 0x5061, 0x5069, 0x5071, 0x5079, 0x5081, 0x5089, 0x5091, 0x5099, 0x50a1, /* Node for range 0x0fa4_ */ 0x50a9, 0x50b1, 0x50b9, 0x50c1, 0x50c9, 0x50d1, 0x50d9, 0x50e1, 0x50e9, 0x50f1, 0x50f9, 0x5101, 0x5109, 0x5111, 0x5119, 0x5121, /* Node for range 0x0fa5_ */ 0x5129, 0x5131, 0x5139, 0x5141, 0x5149, 0x5151, 0x5159, 0x5161, 0x5169, 0x5171, 0x5179, 0x5181, 0x5189, 0x5191, 0x5199, 0x51a1, /* Node for range 0x0fa6_ */ 0x51a9, 0x51b1, 0x51b9, 0x51c1, 0x51c9, 0x51d1, 0x51d9, 0x51e1, 0x51e9, 0x51f1, 0x51f9, 0x5201, 0x5209, 0x5211, 0x0000, 0x0000, /* Node for range 0x0fa7_ */ 0x5219, 0x5221, 0x5229, 0x5231, 0x5239, 0x5241, 0x5249, 0x5251, 0x5259, 0x5261, 0x5269, 0x5271, 0x5279, 0x5281, 0x5289, 0x5291, /* Node for range 0x0fa8_ */ 0x5299, 0x52a1, 0x52a9, 0x52b1, 0x52b9, 0x52c1, 0x52c9, 0x52d1, 0x52d9, 0x52e1, 0x52e9, 0x52f1, 0x52f9, 0x5301, 0x5309, 0x5311, /* Node for range 0x0fa9_ */ 0x5319, 0x5321, 0x5329, 0x5331, 0x5339, 0x5341, 0x5349, 0x5351, 0x5359, 0x5361, 0x5369, 0x5371, 0x5379, 0x5381, 0x5389, 0x5391, /* Node for range 0x0faa_ */ 0x5399, 0x53a1, 0x53a9, 0x53b1, 0x53b9, 0x53c1, 0x53c9, 0x53d1, 0x53d9, 0x53e1, 0x53e9, 0x53f1, 0x53f9, 0x5401, 0x5409, 0x5411, /* Node for range 0x0fab_ */ 0x5419, 0x5421, 0x5429, 0x5431, 0x5439, 0x5441, 0x5449, 0x5451, 0x5459, 0x5461, 0x5469, 0x5471, 0x5479, 0x5481, 0x5489, 0x5491, /* Node for range 0x0fac_ */ 0x5499, 0x54a1, 0x54a9, 0x54b1, 0x54b9, 0x54c1, 0x54c9, 0x54d1, 0x54d9, 0x54e1, 0x54e9, 0x54f1, 0x54f9, 0x5501, 0x5509, 0x5511, /* Node for range 0x0fad_ */ 0x5519, 0x5521, 0x5529, 0x5531, 0x5539, 0x5541, 0x5549, 0x5551, 0x5559, 0x5561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0fb1_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x556a, 0x0000, 0x557a, /* Node for range 0x0fb2_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x558a, 0x559a, 0x55ab, 0x55c3, 0x55da, 0x55ea, /* Node for range 0x0fb3_ */ 0x55fa, 0x560a, 0x561a, 0x562a, 0x563a, 0x564a, 0x565a, 0x0000, 0x566a, 0x567a, 0x568a, 0x569a, 0x56aa, 0x0000, 0x56ba, 0x0000, /* Node for range 0x0fb4_ */ 0x56ca, 0x56da, 0x0000, 0x56ea, 0x56fa, 0x0000, 0x570a, 0x571a, 0x572a, 0x573a, 0x574a, 0x575a, 0x576a, 0x577a, 0x578a, 0x0000, /* Node for range 0x1109_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x579a, 0x0000, 0x57aa, 0x0000, 0x0000, 0x0000, /* Node for range 0x110a_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x57ba, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1112_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x57ca, 0x57da, /* Node for range 0x1134_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x57ea, 0x57fa, 0x0000, 0x0000, 0x0000, /* Node for range 0x114b_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x580a, 0x581a, 0x0000, 0x582a, 0x0000, /* Node for range 0x115b_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x583a, 0x584a, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1d15_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x585a, 0x586a, /* Node for range 0x1d16_ */ 0x587b, 0x5893, 0x58ab, 0x58c3, 0x58db, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1d1b_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x58f2, 0x5902, 0x5913, 0x592b, 0x5943, /* Node for range 0x1d1c_ */ 0x595b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x2f80_ */ 0x5971, 0x5979, 0x5981, 0x5989, 0x5991, 0x5999, 0x59a1, 0x59a9, 0x59b1, 0x59b9, 0x59c1, 0x59c9, 0x59d1, 0x59d9, 0x59e1, 0x59e9, /* Node for range 0x2f81_ */ 0x59f1, 0x59f9, 0x5a01, 0x5a09, 0x5a11, 0x5a19, 0x5a21, 0x5a29, 0x5a31, 0x5a39, 0x5a41, 0x5a49, 0x5a51, 0x5a59, 0x5a61, 0x5a69, /* Node for range 0x2f82_ */ 0x5a71, 0x5a79, 0x5a81, 0x5a89, 0x5a91, 0x5a99, 0x5aa1, 0x5aa9, 0x5ab1, 0x5ab9, 0x5ac1, 0x5ac9, 0x5ad1, 0x5ad9, 0x5ae1, 0x5ae9, /* Node for range 0x2f83_ */ 0x5af1, 0x5af9, 0x5b01, 0x5b09, 0x5b11, 0x5b19, 0x5b21, 0x5b29, 0x5b31, 0x5b39, 0x5b41, 0x5b49, 0x5b51, 0x5b59, 0x5b61, 0x5b69, /* Node for range 0x2f84_ */ 0x5b71, 0x5b79, 0x5b81, 0x5b89, 0x5b91, 0x5b99, 0x5ba1, 0x5ba9, 0x5bb1, 0x5bb9, 0x5bc1, 0x5bc9, 0x5bd1, 0x5bd9, 0x5be1, 0x5be9, /* Node for range 0x2f85_ */ 0x5bf1, 0x5bf9, 0x5c01, 0x5c09, 0x5c11, 0x5c19, 0x5c21, 0x5c29, 0x5c31, 0x5c39, 0x5c41, 0x5c49, 0x5c51, 0x5c59, 0x5c61, 0x5c69, /* Node for range 0x2f86_ */ 0x5c71, 0x5c79, 0x5c81, 0x5c89, 0x5c91, 0x5c99, 0x5ca1, 0x5ca9, 0x5cb1, 0x5cb9, 0x5cc1, 0x5cc9, 0x5cd1, 0x5cd9, 0x5ce1, 0x5ce9, /* Node for range 0x2f87_ */ 0x5cf1, 0x5cf9, 0x5d01, 0x5d09, 0x5d11, 0x5d19, 0x5d21, 0x5d29, 0x5d31, 0x5d39, 0x5d41, 0x5d49, 0x5d51, 0x5d59, 0x5d61, 0x5d69, /* Node for range 0x2f88_ */ 0x5d71, 0x5d79, 0x5d81, 0x5d89, 0x5d91, 0x5d99, 0x5da1, 0x5da9, 0x5db1, 0x5db9, 0x5dc1, 0x5dc9, 0x5dd1, 0x5dd9, 0x5de1, 0x5de9, /* Node for range 0x2f89_ */ 0x5df1, 0x5df9, 0x5e01, 0x5e09, 0x5e11, 0x5e19, 0x5e21, 0x5e29, 0x5e31, 0x5e39, 0x5e41, 0x5e49, 0x5e51, 0x5e59, 0x5e61, 0x5e69, /* Node for range 0x2f8a_ */ 0x5e71, 0x5e79, 0x5e81, 0x5e89, 0x5e91, 0x5e99, 0x5ea1, 0x5ea9, 0x5eb1, 0x5eb9, 0x5ec1, 0x5ec9, 0x5ed1, 0x5ed9, 0x5ee1, 0x5ee9, /* Node for range 0x2f8b_ */ 0x5ef1, 0x5ef9, 0x5f01, 0x5f09, 0x5f11, 0x5f19, 0x5f21, 0x5f29, 0x5f31, 0x5f39, 0x5f41, 0x5f49, 0x5f51, 0x5f59, 0x5f61, 0x5f69, /* Node for range 0x2f8c_ */ 0x5f71, 0x5f79, 0x5f81, 0x5f89, 0x5f91, 0x5f99, 0x5fa1, 0x5fa9, 0x5fb1, 0x5fb9, 0x5fc1, 0x5fc9, 0x5fd1, 0x5fd9, 0x5fe1, 0x5fe9, /* Node for range 0x2f8d_ */ 0x5ff1, 0x5ff9, 0x6001, 0x6009, 0x6011, 0x6019, 0x6021, 0x6029, 0x6031, 0x6039, 0x6041, 0x6049, 0x6051, 0x6059, 0x6061, 0x6069, /* Node for range 0x2f8e_ */ 0x6071, 0x6079, 0x6081, 0x6089, 0x6091, 0x6099, 0x60a1, 0x60a9, 0x60b1, 0x60b9, 0x60c1, 0x60c9, 0x60d1, 0x60d9, 0x60e1, 0x60e9, /* Node for range 0x2f8f_ */ 0x60f1, 0x60f9, 0x6101, 0x6109, 0x6111, 0x6119, 0x6121, 0x6129, 0x6131, 0x6139, 0x6141, 0x6149, 0x6151, 0x6159, 0x6161, 0x6169, /* Node for range 0x2f90_ */ 0x6171, 0x6179, 0x6181, 0x6189, 0x6191, 0x6199, 0x61a1, 0x61a9, 0x61b1, 0x61b9, 0x61c1, 0x61c9, 0x61d1, 0x61d9, 0x61e1, 0x61e9, /* Node for range 0x2f91_ */ 0x61f1, 0x61f9, 0x6201, 0x6209, 0x6211, 0x6219, 0x6221, 0x6229, 0x6231, 0x6239, 0x6241, 0x6249, 0x6251, 0x6259, 0x6261, 0x6269, /* Node for range 0x2f92_ */ 0x6271, 0x6279, 0x6281, 0x6289, 0x6291, 0x6299, 0x62a1, 0x62a9, 0x62b1, 0x62b9, 0x62c1, 0x62c9, 0x62d1, 0x62d9, 0x62e1, 0x62e9, /* Node for range 0x2f93_ */ 0x62f1, 0x62f9, 0x6301, 0x6309, 0x6311, 0x6319, 0x6321, 0x6329, 0x6331, 0x6339, 0x6341, 0x6349, 0x6351, 0x6359, 0x6361, 0x6369, /* Node for range 0x2f94_ */ 0x6371, 0x6379, 0x6381, 0x6389, 0x6391, 0x6399, 0x63a1, 0x63a9, 0x63b1, 0x63b9, 0x63c1, 0x63c9, 0x63d1, 0x63d9, 0x63e1, 0x63e9, /* Node for range 0x2f95_ */ 0x63f1, 0x63f9, 0x6401, 0x6409, 0x6411, 0x6419, 0x6421, 0x6429, 0x6431, 0x6439, 0x6441, 0x6449, 0x6451, 0x6459, 0x6461, 0x6469, /* Node for range 0x2f96_ */ 0x6471, 0x6479, 0x6481, 0x6489, 0x6491, 0x6499, 0x64a1, 0x64a9, 0x64b1, 0x64b9, 0x64c1, 0x64c9, 0x64d1, 0x64d9, 0x64e1, 0x64e9, /* Node for range 0x2f97_ */ 0x64f1, 0x64f9, 0x6501, 0x6509, 0x6511, 0x6519, 0x6521, 0x6529, 0x6531, 0x6539, 0x6541, 0x6549, 0x6551, 0x6559, 0x6561, 0x6569, /* Node for range 0x2f98_ */ 0x6571, 0x6579, 0x6581, 0x6589, 0x6591, 0x6599, 0x65a1, 0x65a9, 0x65b1, 0x65b9, 0x65c1, 0x65c9, 0x65d1, 0x65d9, 0x65e1, 0x65e9, /* Node for range 0x2f99_ */ 0x65f1, 0x65f9, 0x6601, 0x6609, 0x6611, 0x6619, 0x6621, 0x6629, 0x6631, 0x6639, 0x6641, 0x6649, 0x6651, 0x6659, 0x6661, 0x6669, /* Node for range 0x2f9a_ */ 0x6671, 0x6679, 0x6681, 0x6689, 0x6691, 0x6699, 0x66a1, 0x66a9, 0x66b1, 0x66b9, 0x66c1, 0x66c9, 0x66d1, 0x66d9, 0x66e1, 0x66e9, /* Node for range 0x2f9b_ */ 0x66f1, 0x66f9, 0x6701, 0x6709, 0x6711, 0x6719, 0x6721, 0x6729, 0x6731, 0x6739, 0x6741, 0x6749, 0x6751, 0x6759, 0x6761, 0x6769, /* Node for range 0x2f9c_ */ 0x6771, 0x6779, 0x6781, 0x6789, 0x6791, 0x6799, 0x67a1, 0x67a9, 0x67b1, 0x67b9, 0x67c1, 0x67c9, 0x67d1, 0x67d9, 0x67e1, 0x67e9, /* Node for range 0x2f9d_ */ 0x67f1, 0x67f9, 0x6801, 0x6809, 0x6811, 0x6819, 0x6821, 0x6829, 0x6831, 0x6839, 0x6841, 0x6849, 0x6851, 0x6859, 0x6861, 0x6869, /* Node for range 0x2f9e_ */ 0x6871, 0x6879, 0x6881, 0x6889, 0x6891, 0x6899, 0x68a1, 0x68a9, 0x68b1, 0x68b9, 0x68c1, 0x68c9, 0x68d1, 0x68d9, 0x68e1, 0x68e9, /* Node for range 0x2f9f_ */ 0x68f1, 0x68f9, 0x6901, 0x6909, 0x6911, 0x6919, 0x6921, 0x6929, 0x6931, 0x6939, 0x6941, 0x6949, 0x6951, 0x6959, 0x6961, 0x6969, /* Node for range 0x2fa0_ */ 0x6971, 0x6979, 0x6981, 0x6989, 0x6991, 0x6999, 0x69a1, 0x69a9, 0x69b1, 0x69b9, 0x69c1, 0x69c9, 0x69d1, 0x69d9, 0x69e1, 0x69e9, /* Node for range 0x2fa1_ */ 0x69f1, 0x69f9, 0x6a01, 0x6a09, 0x6a11, 0x6a19, 0x6a21, 0x6a29, 0x6a31, 0x6a39, 0x6a41, 0x6a49, 0x6a51, 0x6a59, 0x0000, 0x0000, }; static unicode_t nfd_values[] = { 0x000041, 0x000300, 0x000041, 0x000301, 0x000041, 0x000302, 0x000041, 0x000303, 0x000041, 0x000308, 0x000041, 0x00030a, 0x000043, 0x000327, 0x000045, 0x000300, 0x000045, 0x000301, 0x000045, 0x000302, 0x000045, 0x000308, 0x000049, 0x000300, 0x000049, 0x000301, 0x000049, 0x000302, 0x000049, 0x000308, 0x00004e, 0x000303, 0x00004f, 0x000300, 0x00004f, 0x000301, 0x00004f, 0x000302, 0x00004f, 0x000303, 0x00004f, 0x000308, 0x000055, 0x000300, 0x000055, 0x000301, 0x000055, 0x000302, 0x000055, 0x000308, 0x000059, 0x000301, 0x000061, 0x000300, 0x000061, 0x000301, 0x000061, 0x000302, 0x000061, 0x000303, 0x000061, 0x000308, 0x000061, 0x00030a, 0x000063, 0x000327, 0x000065, 0x000300, 0x000065, 0x000301, 0x000065, 0x000302, 0x000065, 0x000308, 0x000069, 0x000300, 0x000069, 0x000301, 0x000069, 0x000302, 0x000069, 0x000308, 0x00006e, 0x000303, 0x00006f, 0x000300, 0x00006f, 0x000301, 0x00006f, 0x000302, 0x00006f, 0x000303, 0x00006f, 0x000308, 0x000075, 0x000300, 0x000075, 0x000301, 0x000075, 0x000302, 0x000075, 0x000308, 0x000079, 0x000301, 0x000079, 0x000308, 0x000041, 0x000304, 0x000061, 0x000304, 0x000041, 0x000306, 0x000061, 0x000306, 0x000041, 0x000328, 0x000061, 0x000328, 0x000043, 0x000301, 0x000063, 0x000301, 0x000043, 0x000302, 0x000063, 0x000302, 0x000043, 0x000307, 0x000063, 0x000307, 0x000043, 0x00030c, 0x000063, 0x00030c, 0x000044, 0x00030c, 0x000064, 0x00030c, 0x000045, 0x000304, 0x000065, 0x000304, 0x000045, 0x000306, 0x000065, 0x000306, 0x000045, 0x000307, 0x000065, 0x000307, 0x000045, 0x000328, 0x000065, 0x000328, 0x000045, 0x00030c, 0x000065, 0x00030c, 0x000047, 0x000302, 0x000067, 0x000302, 0x000047, 0x000306, 0x000067, 0x000306, 0x000047, 0x000307, 0x000067, 0x000307, 0x000047, 0x000327, 0x000067, 0x000327, 0x000048, 0x000302, 0x000068, 0x000302, 0x000049, 0x000303, 0x000069, 0x000303, 0x000049, 0x000304, 0x000069, 0x000304, 0x000049, 0x000306, 0x000069, 0x000306, 0x000049, 0x000328, 0x000069, 0x000328, 0x000049, 0x000307, 0x00004a, 0x000302, 0x00006a, 0x000302, 0x00004b, 0x000327, 0x00006b, 0x000327, 0x00004c, 0x000301, 0x00006c, 0x000301, 0x00004c, 0x000327, 0x00006c, 0x000327, 0x00004c, 0x00030c, 0x00006c, 0x00030c, 0x00004e, 0x000301, 0x00006e, 0x000301, 0x00004e, 0x000327, 0x00006e, 0x000327, 0x00004e, 0x00030c, 0x00006e, 0x00030c, 0x00004f, 0x000304, 0x00006f, 0x000304, 0x00004f, 0x000306, 0x00006f, 0x000306, 0x00004f, 0x00030b, 0x00006f, 0x00030b, 0x000052, 0x000301, 0x000072, 0x000301, 0x000052, 0x000327, 0x000072, 0x000327, 0x000052, 0x00030c, 0x000072, 0x00030c, 0x000053, 0x000301, 0x000073, 0x000301, 0x000053, 0x000302, 0x000073, 0x000302, 0x000053, 0x000327, 0x000073, 0x000327, 0x000053, 0x00030c, 0x000073, 0x00030c, 0x000054, 0x000327, 0x000074, 0x000327, 0x000054, 0x00030c, 0x000074, 0x00030c, 0x000055, 0x000303, 0x000075, 0x000303, 0x000055, 0x000304, 0x000075, 0x000304, 0x000055, 0x000306, 0x000075, 0x000306, 0x000055, 0x00030a, 0x000075, 0x00030a, 0x000055, 0x00030b, 0x000075, 0x00030b, 0x000055, 0x000328, 0x000075, 0x000328, 0x000057, 0x000302, 0x000077, 0x000302, 0x000059, 0x000302, 0x000079, 0x000302, 0x000059, 0x000308, 0x00005a, 0x000301, 0x00007a, 0x000301, 0x00005a, 0x000307, 0x00007a, 0x000307, 0x00005a, 0x00030c, 0x00007a, 0x00030c, 0x00004f, 0x00031b, 0x00006f, 0x00031b, 0x000055, 0x00031b, 0x000075, 0x00031b, 0x000041, 0x00030c, 0x000061, 0x00030c, 0x000049, 0x00030c, 0x000069, 0x00030c, 0x00004f, 0x00030c, 0x00006f, 0x00030c, 0x000055, 0x00030c, 0x000075, 0x00030c, 0x000055, 0x000308, 0x000304, 0x000075, 0x000308, 0x000304, 0x000055, 0x000308, 0x000301, 0x000075, 0x000308, 0x000301, 0x000055, 0x000308, 0x00030c, 0x000075, 0x000308, 0x00030c, 0x000055, 0x000308, 0x000300, 0x000075, 0x000308, 0x000300, 0x000041, 0x000308, 0x000304, 0x000061, 0x000308, 0x000304, 0x000041, 0x000307, 0x000304, 0x000061, 0x000307, 0x000304, 0x0000c6, 0x000304, 0x0000e6, 0x000304, 0x000047, 0x00030c, 0x000067, 0x00030c, 0x00004b, 0x00030c, 0x00006b, 0x00030c, 0x00004f, 0x000328, 0x00006f, 0x000328, 0x00004f, 0x000328, 0x000304, 0x00006f, 0x000328, 0x000304, 0x0001b7, 0x00030c, 0x000292, 0x00030c, 0x00006a, 0x00030c, 0x000047, 0x000301, 0x000067, 0x000301, 0x00004e, 0x000300, 0x00006e, 0x000300, 0x000041, 0x00030a, 0x000301, 0x000061, 0x00030a, 0x000301, 0x0000c6, 0x000301, 0x0000e6, 0x000301, 0x0000d8, 0x000301, 0x0000f8, 0x000301, 0x000041, 0x00030f, 0x000061, 0x00030f, 0x000041, 0x000311, 0x000061, 0x000311, 0x000045, 0x00030f, 0x000065, 0x00030f, 0x000045, 0x000311, 0x000065, 0x000311, 0x000049, 0x00030f, 0x000069, 0x00030f, 0x000049, 0x000311, 0x000069, 0x000311, 0x00004f, 0x00030f, 0x00006f, 0x00030f, 0x00004f, 0x000311, 0x00006f, 0x000311, 0x000052, 0x00030f, 0x000072, 0x00030f, 0x000052, 0x000311, 0x000072, 0x000311, 0x000055, 0x00030f, 0x000075, 0x00030f, 0x000055, 0x000311, 0x000075, 0x000311, 0x000053, 0x000326, 0x000073, 0x000326, 0x000054, 0x000326, 0x000074, 0x000326, 0x000048, 0x00030c, 0x000068, 0x00030c, 0x000041, 0x000307, 0x000061, 0x000307, 0x000045, 0x000327, 0x000065, 0x000327, 0x00004f, 0x000308, 0x000304, 0x00006f, 0x000308, 0x000304, 0x00004f, 0x000303, 0x000304, 0x00006f, 0x000303, 0x000304, 0x00004f, 0x000307, 0x00006f, 0x000307, 0x00004f, 0x000307, 0x000304, 0x00006f, 0x000307, 0x000304, 0x000059, 0x000304, 0x000079, 0x000304, 0x000300, 0x000301, 0x000313, 0x000308, 0x000301, 0x0002b9, 0x00003b, 0x0000a8, 0x000301, 0x000391, 0x000301, 0x0000b7, 0x000395, 0x000301, 0x000397, 0x000301, 0x000399, 0x000301, 0x00039f, 0x000301, 0x0003a5, 0x000301, 0x0003a9, 0x000301, 0x0003b9, 0x000308, 0x000301, 0x000399, 0x000308, 0x0003a5, 0x000308, 0x0003b1, 0x000301, 0x0003b5, 0x000301, 0x0003b7, 0x000301, 0x0003b9, 0x000301, 0x0003c5, 0x000308, 0x000301, 0x0003b9, 0x000308, 0x0003c5, 0x000308, 0x0003bf, 0x000301, 0x0003c5, 0x000301, 0x0003c9, 0x000301, 0x0003d2, 0x000301, 0x0003d2, 0x000308, 0x000415, 0x000300, 0x000415, 0x000308, 0x000413, 0x000301, 0x000406, 0x000308, 0x00041a, 0x000301, 0x000418, 0x000300, 0x000423, 0x000306, 0x000418, 0x000306, 0x000438, 0x000306, 0x000435, 0x000300, 0x000435, 0x000308, 0x000433, 0x000301, 0x000456, 0x000308, 0x00043a, 0x000301, 0x000438, 0x000300, 0x000443, 0x000306, 0x000474, 0x00030f, 0x000475, 0x00030f, 0x000416, 0x000306, 0x000436, 0x000306, 0x000410, 0x000306, 0x000430, 0x000306, 0x000410, 0x000308, 0x000430, 0x000308, 0x000415, 0x000306, 0x000435, 0x000306, 0x0004d8, 0x000308, 0x0004d9, 0x000308, 0x000416, 0x000308, 0x000436, 0x000308, 0x000417, 0x000308, 0x000437, 0x000308, 0x000418, 0x000304, 0x000438, 0x000304, 0x000418, 0x000308, 0x000438, 0x000308, 0x00041e, 0x000308, 0x00043e, 0x000308, 0x0004e8, 0x000308, 0x0004e9, 0x000308, 0x00042d, 0x000308, 0x00044d, 0x000308, 0x000423, 0x000304, 0x000443, 0x000304, 0x000423, 0x000308, 0x000443, 0x000308, 0x000423, 0x00030b, 0x000443, 0x00030b, 0x000427, 0x000308, 0x000447, 0x000308, 0x00042b, 0x000308, 0x00044b, 0x000308, 0x000627, 0x000653, 0x000627, 0x000654, 0x000648, 0x000654, 0x000627, 0x000655, 0x00064a, 0x000654, 0x0006d5, 0x000654, 0x0006c1, 0x000654, 0x0006d2, 0x000654, 0x000928, 0x00093c, 0x000930, 0x00093c, 0x000933, 0x00093c, 0x000915, 0x00093c, 0x000916, 0x00093c, 0x000917, 0x00093c, 0x00091c, 0x00093c, 0x000921, 0x00093c, 0x000922, 0x00093c, 0x00092b, 0x00093c, 0x00092f, 0x00093c, 0x0009c7, 0x0009be, 0x0009c7, 0x0009d7, 0x0009a1, 0x0009bc, 0x0009a2, 0x0009bc, 0x0009af, 0x0009bc, 0x000a32, 0x000a3c, 0x000a38, 0x000a3c, 0x000a16, 0x000a3c, 0x000a17, 0x000a3c, 0x000a1c, 0x000a3c, 0x000a2b, 0x000a3c, 0x000b47, 0x000b56, 0x000b47, 0x000b3e, 0x000b47, 0x000b57, 0x000b21, 0x000b3c, 0x000b22, 0x000b3c, 0x000b92, 0x000bd7, 0x000bc6, 0x000bbe, 0x000bc7, 0x000bbe, 0x000bc6, 0x000bd7, 0x000c46, 0x000c56, 0x000cbf, 0x000cd5, 0x000cc6, 0x000cd5, 0x000cc6, 0x000cd6, 0x000cc6, 0x000cc2, 0x000cc6, 0x000cc2, 0x000cd5, 0x000d46, 0x000d3e, 0x000d47, 0x000d3e, 0x000d46, 0x000d57, 0x000dd9, 0x000dca, 0x000dd9, 0x000dcf, 0x000dd9, 0x000dcf, 0x000dca, 0x000dd9, 0x000ddf, 0x000f42, 0x000fb7, 0x000f4c, 0x000fb7, 0x000f51, 0x000fb7, 0x000f56, 0x000fb7, 0x000f5b, 0x000fb7, 0x000f40, 0x000fb5, 0x000f71, 0x000f72, 0x000f71, 0x000f74, 0x000fb2, 0x000f80, 0x000fb3, 0x000f80, 0x000f71, 0x000f80, 0x000f92, 0x000fb7, 0x000f9c, 0x000fb7, 0x000fa1, 0x000fb7, 0x000fa6, 0x000fb7, 0x000fab, 0x000fb7, 0x000f90, 0x000fb5, 0x001025, 0x00102e, 0x001b05, 0x001b35, 0x001b07, 0x001b35, 0x001b09, 0x001b35, 0x001b0b, 0x001b35, 0x001b0d, 0x001b35, 0x001b11, 0x001b35, 0x001b3a, 0x001b35, 0x001b3c, 0x001b35, 0x001b3e, 0x001b35, 0x001b3f, 0x001b35, 0x001b42, 0x001b35, 0x000041, 0x000325, 0x000061, 0x000325, 0x000042, 0x000307, 0x000062, 0x000307, 0x000042, 0x000323, 0x000062, 0x000323, 0x000042, 0x000331, 0x000062, 0x000331, 0x000043, 0x000327, 0x000301, 0x000063, 0x000327, 0x000301, 0x000044, 0x000307, 0x000064, 0x000307, 0x000044, 0x000323, 0x000064, 0x000323, 0x000044, 0x000331, 0x000064, 0x000331, 0x000044, 0x000327, 0x000064, 0x000327, 0x000044, 0x00032d, 0x000064, 0x00032d, 0x000045, 0x000304, 0x000300, 0x000065, 0x000304, 0x000300, 0x000045, 0x000304, 0x000301, 0x000065, 0x000304, 0x000301, 0x000045, 0x00032d, 0x000065, 0x00032d, 0x000045, 0x000330, 0x000065, 0x000330, 0x000045, 0x000327, 0x000306, 0x000065, 0x000327, 0x000306, 0x000046, 0x000307, 0x000066, 0x000307, 0x000047, 0x000304, 0x000067, 0x000304, 0x000048, 0x000307, 0x000068, 0x000307, 0x000048, 0x000323, 0x000068, 0x000323, 0x000048, 0x000308, 0x000068, 0x000308, 0x000048, 0x000327, 0x000068, 0x000327, 0x000048, 0x00032e, 0x000068, 0x00032e, 0x000049, 0x000330, 0x000069, 0x000330, 0x000049, 0x000308, 0x000301, 0x000069, 0x000308, 0x000301, 0x00004b, 0x000301, 0x00006b, 0x000301, 0x00004b, 0x000323, 0x00006b, 0x000323, 0x00004b, 0x000331, 0x00006b, 0x000331, 0x00004c, 0x000323, 0x00006c, 0x000323, 0x00004c, 0x000323, 0x000304, 0x00006c, 0x000323, 0x000304, 0x00004c, 0x000331, 0x00006c, 0x000331, 0x00004c, 0x00032d, 0x00006c, 0x00032d, 0x00004d, 0x000301, 0x00006d, 0x000301, 0x00004d, 0x000307, 0x00006d, 0x000307, 0x00004d, 0x000323, 0x00006d, 0x000323, 0x00004e, 0x000307, 0x00006e, 0x000307, 0x00004e, 0x000323, 0x00006e, 0x000323, 0x00004e, 0x000331, 0x00006e, 0x000331, 0x00004e, 0x00032d, 0x00006e, 0x00032d, 0x00004f, 0x000303, 0x000301, 0x00006f, 0x000303, 0x000301, 0x00004f, 0x000303, 0x000308, 0x00006f, 0x000303, 0x000308, 0x00004f, 0x000304, 0x000300, 0x00006f, 0x000304, 0x000300, 0x00004f, 0x000304, 0x000301, 0x00006f, 0x000304, 0x000301, 0x000050, 0x000301, 0x000070, 0x000301, 0x000050, 0x000307, 0x000070, 0x000307, 0x000052, 0x000307, 0x000072, 0x000307, 0x000052, 0x000323, 0x000072, 0x000323, 0x000052, 0x000323, 0x000304, 0x000072, 0x000323, 0x000304, 0x000052, 0x000331, 0x000072, 0x000331, 0x000053, 0x000307, 0x000073, 0x000307, 0x000053, 0x000323, 0x000073, 0x000323, 0x000053, 0x000301, 0x000307, 0x000073, 0x000301, 0x000307, 0x000053, 0x00030c, 0x000307, 0x000073, 0x00030c, 0x000307, 0x000053, 0x000323, 0x000307, 0x000073, 0x000323, 0x000307, 0x000054, 0x000307, 0x000074, 0x000307, 0x000054, 0x000323, 0x000074, 0x000323, 0x000054, 0x000331, 0x000074, 0x000331, 0x000054, 0x00032d, 0x000074, 0x00032d, 0x000055, 0x000324, 0x000075, 0x000324, 0x000055, 0x000330, 0x000075, 0x000330, 0x000055, 0x00032d, 0x000075, 0x00032d, 0x000055, 0x000303, 0x000301, 0x000075, 0x000303, 0x000301, 0x000055, 0x000304, 0x000308, 0x000075, 0x000304, 0x000308, 0x000056, 0x000303, 0x000076, 0x000303, 0x000056, 0x000323, 0x000076, 0x000323, 0x000057, 0x000300, 0x000077, 0x000300, 0x000057, 0x000301, 0x000077, 0x000301, 0x000057, 0x000308, 0x000077, 0x000308, 0x000057, 0x000307, 0x000077, 0x000307, 0x000057, 0x000323, 0x000077, 0x000323, 0x000058, 0x000307, 0x000078, 0x000307, 0x000058, 0x000308, 0x000078, 0x000308, 0x000059, 0x000307, 0x000079, 0x000307, 0x00005a, 0x000302, 0x00007a, 0x000302, 0x00005a, 0x000323, 0x00007a, 0x000323, 0x00005a, 0x000331, 0x00007a, 0x000331, 0x000068, 0x000331, 0x000074, 0x000308, 0x000077, 0x00030a, 0x000079, 0x00030a, 0x00017f, 0x000307, 0x000041, 0x000323, 0x000061, 0x000323, 0x000041, 0x000309, 0x000061, 0x000309, 0x000041, 0x000302, 0x000301, 0x000061, 0x000302, 0x000301, 0x000041, 0x000302, 0x000300, 0x000061, 0x000302, 0x000300, 0x000041, 0x000302, 0x000309, 0x000061, 0x000302, 0x000309, 0x000041, 0x000302, 0x000303, 0x000061, 0x000302, 0x000303, 0x000041, 0x000323, 0x000302, 0x000061, 0x000323, 0x000302, 0x000041, 0x000306, 0x000301, 0x000061, 0x000306, 0x000301, 0x000041, 0x000306, 0x000300, 0x000061, 0x000306, 0x000300, 0x000041, 0x000306, 0x000309, 0x000061, 0x000306, 0x000309, 0x000041, 0x000306, 0x000303, 0x000061, 0x000306, 0x000303, 0x000041, 0x000323, 0x000306, 0x000061, 0x000323, 0x000306, 0x000045, 0x000323, 0x000065, 0x000323, 0x000045, 0x000309, 0x000065, 0x000309, 0x000045, 0x000303, 0x000065, 0x000303, 0x000045, 0x000302, 0x000301, 0x000065, 0x000302, 0x000301, 0x000045, 0x000302, 0x000300, 0x000065, 0x000302, 0x000300, 0x000045, 0x000302, 0x000309, 0x000065, 0x000302, 0x000309, 0x000045, 0x000302, 0x000303, 0x000065, 0x000302, 0x000303, 0x000045, 0x000323, 0x000302, 0x000065, 0x000323, 0x000302, 0x000049, 0x000309, 0x000069, 0x000309, 0x000049, 0x000323, 0x000069, 0x000323, 0x00004f, 0x000323, 0x00006f, 0x000323, 0x00004f, 0x000309, 0x00006f, 0x000309, 0x00004f, 0x000302, 0x000301, 0x00006f, 0x000302, 0x000301, 0x00004f, 0x000302, 0x000300, 0x00006f, 0x000302, 0x000300, 0x00004f, 0x000302, 0x000309, 0x00006f, 0x000302, 0x000309, 0x00004f, 0x000302, 0x000303, 0x00006f, 0x000302, 0x000303, 0x00004f, 0x000323, 0x000302, 0x00006f, 0x000323, 0x000302, 0x00004f, 0x00031b, 0x000301, 0x00006f, 0x00031b, 0x000301, 0x00004f, 0x00031b, 0x000300, 0x00006f, 0x00031b, 0x000300, 0x00004f, 0x00031b, 0x000309, 0x00006f, 0x00031b, 0x000309, 0x00004f, 0x00031b, 0x000303, 0x00006f, 0x00031b, 0x000303, 0x00004f, 0x00031b, 0x000323, 0x00006f, 0x00031b, 0x000323, 0x000055, 0x000323, 0x000075, 0x000323, 0x000055, 0x000309, 0x000075, 0x000309, 0x000055, 0x00031b, 0x000301, 0x000075, 0x00031b, 0x000301, 0x000055, 0x00031b, 0x000300, 0x000075, 0x00031b, 0x000300, 0x000055, 0x00031b, 0x000309, 0x000075, 0x00031b, 0x000309, 0x000055, 0x00031b, 0x000303, 0x000075, 0x00031b, 0x000303, 0x000055, 0x00031b, 0x000323, 0x000075, 0x00031b, 0x000323, 0x000059, 0x000300, 0x000079, 0x000300, 0x000059, 0x000323, 0x000079, 0x000323, 0x000059, 0x000309, 0x000079, 0x000309, 0x000059, 0x000303, 0x000079, 0x000303, 0x0003b1, 0x000313, 0x0003b1, 0x000314, 0x0003b1, 0x000313, 0x000300, 0x0003b1, 0x000314, 0x000300, 0x0003b1, 0x000313, 0x000301, 0x0003b1, 0x000314, 0x000301, 0x0003b1, 0x000313, 0x000342, 0x0003b1, 0x000314, 0x000342, 0x000391, 0x000313, 0x000391, 0x000314, 0x000391, 0x000313, 0x000300, 0x000391, 0x000314, 0x000300, 0x000391, 0x000313, 0x000301, 0x000391, 0x000314, 0x000301, 0x000391, 0x000313, 0x000342, 0x000391, 0x000314, 0x000342, 0x0003b5, 0x000313, 0x0003b5, 0x000314, 0x0003b5, 0x000313, 0x000300, 0x0003b5, 0x000314, 0x000300, 0x0003b5, 0x000313, 0x000301, 0x0003b5, 0x000314, 0x000301, 0x000395, 0x000313, 0x000395, 0x000314, 0x000395, 0x000313, 0x000300, 0x000395, 0x000314, 0x000300, 0x000395, 0x000313, 0x000301, 0x000395, 0x000314, 0x000301, 0x0003b7, 0x000313, 0x0003b7, 0x000314, 0x0003b7, 0x000313, 0x000300, 0x0003b7, 0x000314, 0x000300, 0x0003b7, 0x000313, 0x000301, 0x0003b7, 0x000314, 0x000301, 0x0003b7, 0x000313, 0x000342, 0x0003b7, 0x000314, 0x000342, 0x000397, 0x000313, 0x000397, 0x000314, 0x000397, 0x000313, 0x000300, 0x000397, 0x000314, 0x000300, 0x000397, 0x000313, 0x000301, 0x000397, 0x000314, 0x000301, 0x000397, 0x000313, 0x000342, 0x000397, 0x000314, 0x000342, 0x0003b9, 0x000313, 0x0003b9, 0x000314, 0x0003b9, 0x000313, 0x000300, 0x0003b9, 0x000314, 0x000300, 0x0003b9, 0x000313, 0x000301, 0x0003b9, 0x000314, 0x000301, 0x0003b9, 0x000313, 0x000342, 0x0003b9, 0x000314, 0x000342, 0x000399, 0x000313, 0x000399, 0x000314, 0x000399, 0x000313, 0x000300, 0x000399, 0x000314, 0x000300, 0x000399, 0x000313, 0x000301, 0x000399, 0x000314, 0x000301, 0x000399, 0x000313, 0x000342, 0x000399, 0x000314, 0x000342, 0x0003bf, 0x000313, 0x0003bf, 0x000314, 0x0003bf, 0x000313, 0x000300, 0x0003bf, 0x000314, 0x000300, 0x0003bf, 0x000313, 0x000301, 0x0003bf, 0x000314, 0x000301, 0x00039f, 0x000313, 0x00039f, 0x000314, 0x00039f, 0x000313, 0x000300, 0x00039f, 0x000314, 0x000300, 0x00039f, 0x000313, 0x000301, 0x00039f, 0x000314, 0x000301, 0x0003c5, 0x000313, 0x0003c5, 0x000314, 0x0003c5, 0x000313, 0x000300, 0x0003c5, 0x000314, 0x000300, 0x0003c5, 0x000313, 0x000301, 0x0003c5, 0x000314, 0x000301, 0x0003c5, 0x000313, 0x000342, 0x0003c5, 0x000314, 0x000342, 0x0003a5, 0x000314, 0x0003a5, 0x000314, 0x000300, 0x0003a5, 0x000314, 0x000301, 0x0003a5, 0x000314, 0x000342, 0x0003c9, 0x000313, 0x0003c9, 0x000314, 0x0003c9, 0x000313, 0x000300, 0x0003c9, 0x000314, 0x000300, 0x0003c9, 0x000313, 0x000301, 0x0003c9, 0x000314, 0x000301, 0x0003c9, 0x000313, 0x000342, 0x0003c9, 0x000314, 0x000342, 0x0003a9, 0x000313, 0x0003a9, 0x000314, 0x0003a9, 0x000313, 0x000300, 0x0003a9, 0x000314, 0x000300, 0x0003a9, 0x000313, 0x000301, 0x0003a9, 0x000314, 0x000301, 0x0003a9, 0x000313, 0x000342, 0x0003a9, 0x000314, 0x000342, 0x0003b1, 0x000300, 0x0003b1, 0x000301, 0x0003b5, 0x000300, 0x0003b5, 0x000301, 0x0003b7, 0x000300, 0x0003b7, 0x000301, 0x0003b9, 0x000300, 0x0003b9, 0x000301, 0x0003bf, 0x000300, 0x0003bf, 0x000301, 0x0003c5, 0x000300, 0x0003c5, 0x000301, 0x0003c9, 0x000300, 0x0003c9, 0x000301, 0x0003b1, 0x000313, 0x000345, 0x0003b1, 0x000314, 0x000345, 0x0003b1, 0x000313, 0x000300, 0x000345, 0x0003b1, 0x000314, 0x000300, 0x000345, 0x0003b1, 0x000313, 0x000301, 0x000345, 0x0003b1, 0x000314, 0x000301, 0x000345, 0x0003b1, 0x000313, 0x000342, 0x000345, 0x0003b1, 0x000314, 0x000342, 0x000345, 0x000391, 0x000313, 0x000345, 0x000391, 0x000314, 0x000345, 0x000391, 0x000313, 0x000300, 0x000345, 0x000391, 0x000314, 0x000300, 0x000345, 0x000391, 0x000313, 0x000301, 0x000345, 0x000391, 0x000314, 0x000301, 0x000345, 0x000391, 0x000313, 0x000342, 0x000345, 0x000391, 0x000314, 0x000342, 0x000345, 0x0003b7, 0x000313, 0x000345, 0x0003b7, 0x000314, 0x000345, 0x0003b7, 0x000313, 0x000300, 0x000345, 0x0003b7, 0x000314, 0x000300, 0x000345, 0x0003b7, 0x000313, 0x000301, 0x000345, 0x0003b7, 0x000314, 0x000301, 0x000345, 0x0003b7, 0x000313, 0x000342, 0x000345, 0x0003b7, 0x000314, 0x000342, 0x000345, 0x000397, 0x000313, 0x000345, 0x000397, 0x000314, 0x000345, 0x000397, 0x000313, 0x000300, 0x000345, 0x000397, 0x000314, 0x000300, 0x000345, 0x000397, 0x000313, 0x000301, 0x000345, 0x000397, 0x000314, 0x000301, 0x000345, 0x000397, 0x000313, 0x000342, 0x000345, 0x000397, 0x000314, 0x000342, 0x000345, 0x0003c9, 0x000313, 0x000345, 0x0003c9, 0x000314, 0x000345, 0x0003c9, 0x000313, 0x000300, 0x000345, 0x0003c9, 0x000314, 0x000300, 0x000345, 0x0003c9, 0x000313, 0x000301, 0x000345, 0x0003c9, 0x000314, 0x000301, 0x000345, 0x0003c9, 0x000313, 0x000342, 0x000345, 0x0003c9, 0x000314, 0x000342, 0x000345, 0x0003a9, 0x000313, 0x000345, 0x0003a9, 0x000314, 0x000345, 0x0003a9, 0x000313, 0x000300, 0x000345, 0x0003a9, 0x000314, 0x000300, 0x000345, 0x0003a9, 0x000313, 0x000301, 0x000345, 0x0003a9, 0x000314, 0x000301, 0x000345, 0x0003a9, 0x000313, 0x000342, 0x000345, 0x0003a9, 0x000314, 0x000342, 0x000345, 0x0003b1, 0x000306, 0x0003b1, 0x000304, 0x0003b1, 0x000300, 0x000345, 0x0003b1, 0x000345, 0x0003b1, 0x000301, 0x000345, 0x0003b1, 0x000342, 0x0003b1, 0x000342, 0x000345, 0x000391, 0x000306, 0x000391, 0x000304, 0x000391, 0x000300, 0x000391, 0x000301, 0x000391, 0x000345, 0x0003b9, 0x0000a8, 0x000342, 0x0003b7, 0x000300, 0x000345, 0x0003b7, 0x000345, 0x0003b7, 0x000301, 0x000345, 0x0003b7, 0x000342, 0x0003b7, 0x000342, 0x000345, 0x000395, 0x000300, 0x000395, 0x000301, 0x000397, 0x000300, 0x000397, 0x000301, 0x000397, 0x000345, 0x001fbf, 0x000300, 0x001fbf, 0x000301, 0x001fbf, 0x000342, 0x0003b9, 0x000306, 0x0003b9, 0x000304, 0x0003b9, 0x000308, 0x000300, 0x0003b9, 0x000308, 0x000301, 0x0003b9, 0x000342, 0x0003b9, 0x000308, 0x000342, 0x000399, 0x000306, 0x000399, 0x000304, 0x000399, 0x000300, 0x000399, 0x000301, 0x001ffe, 0x000300, 0x001ffe, 0x000301, 0x001ffe, 0x000342, 0x0003c5, 0x000306, 0x0003c5, 0x000304, 0x0003c5, 0x000308, 0x000300, 0x0003c5, 0x000308, 0x000301, 0x0003c1, 0x000313, 0x0003c1, 0x000314, 0x0003c5, 0x000342, 0x0003c5, 0x000308, 0x000342, 0x0003a5, 0x000306, 0x0003a5, 0x000304, 0x0003a5, 0x000300, 0x0003a5, 0x000301, 0x0003a1, 0x000314, 0x0000a8, 0x000300, 0x0000a8, 0x000301, 0x000060, 0x0003c9, 0x000300, 0x000345, 0x0003c9, 0x000345, 0x0003c9, 0x000301, 0x000345, 0x0003c9, 0x000342, 0x0003c9, 0x000342, 0x000345, 0x00039f, 0x000300, 0x00039f, 0x000301, 0x0003a9, 0x000300, 0x0003a9, 0x000301, 0x0003a9, 0x000345, 0x0000b4, 0x002002, 0x002003, 0x0003a9, 0x00004b, 0x000041, 0x00030a, 0x002190, 0x000338, 0x002192, 0x000338, 0x002194, 0x000338, 0x0021d0, 0x000338, 0x0021d4, 0x000338, 0x0021d2, 0x000338, 0x002203, 0x000338, 0x002208, 0x000338, 0x00220b, 0x000338, 0x002223, 0x000338, 0x002225, 0x000338, 0x00223c, 0x000338, 0x002243, 0x000338, 0x002245, 0x000338, 0x002248, 0x000338, 0x00003d, 0x000338, 0x002261, 0x000338, 0x00224d, 0x000338, 0x00003c, 0x000338, 0x00003e, 0x000338, 0x002264, 0x000338, 0x002265, 0x000338, 0x002272, 0x000338, 0x002273, 0x000338, 0x002276, 0x000338, 0x002277, 0x000338, 0x00227a, 0x000338, 0x00227b, 0x000338, 0x002282, 0x000338, 0x002283, 0x000338, 0x002286, 0x000338, 0x002287, 0x000338, 0x0022a2, 0x000338, 0x0022a8, 0x000338, 0x0022a9, 0x000338, 0x0022ab, 0x000338, 0x00227c, 0x000338, 0x00227d, 0x000338, 0x002291, 0x000338, 0x002292, 0x000338, 0x0022b2, 0x000338, 0x0022b3, 0x000338, 0x0022b4, 0x000338, 0x0022b5, 0x000338, 0x003008, 0x003009, 0x002add, 0x000338, 0x00304b, 0x003099, 0x00304d, 0x003099, 0x00304f, 0x003099, 0x003051, 0x003099, 0x003053, 0x003099, 0x003055, 0x003099, 0x003057, 0x003099, 0x003059, 0x003099, 0x00305b, 0x003099, 0x00305d, 0x003099, 0x00305f, 0x003099, 0x003061, 0x003099, 0x003064, 0x003099, 0x003066, 0x003099, 0x003068, 0x003099, 0x00306f, 0x003099, 0x00306f, 0x00309a, 0x003072, 0x003099, 0x003072, 0x00309a, 0x003075, 0x003099, 0x003075, 0x00309a, 0x003078, 0x003099, 0x003078, 0x00309a, 0x00307b, 0x003099, 0x00307b, 0x00309a, 0x003046, 0x003099, 0x00309d, 0x003099, 0x0030ab, 0x003099, 0x0030ad, 0x003099, 0x0030af, 0x003099, 0x0030b1, 0x003099, 0x0030b3, 0x003099, 0x0030b5, 0x003099, 0x0030b7, 0x003099, 0x0030b9, 0x003099, 0x0030bb, 0x003099, 0x0030bd, 0x003099, 0x0030bf, 0x003099, 0x0030c1, 0x003099, 0x0030c4, 0x003099, 0x0030c6, 0x003099, 0x0030c8, 0x003099, 0x0030cf, 0x003099, 0x0030cf, 0x00309a, 0x0030d2, 0x003099, 0x0030d2, 0x00309a, 0x0030d5, 0x003099, 0x0030d5, 0x00309a, 0x0030d8, 0x003099, 0x0030d8, 0x00309a, 0x0030db, 0x003099, 0x0030db, 0x00309a, 0x0030a6, 0x003099, 0x0030ef, 0x003099, 0x0030f0, 0x003099, 0x0030f1, 0x003099, 0x0030f2, 0x003099, 0x0030fd, 0x003099, 0x008c48, 0x0066f4, 0x008eca, 0x008cc8, 0x006ed1, 0x004e32, 0x0053e5, 0x009f9c, 0x009f9c, 0x005951, 0x0091d1, 0x005587, 0x005948, 0x0061f6, 0x007669, 0x007f85, 0x00863f, 0x0087ba, 0x0088f8, 0x00908f, 0x006a02, 0x006d1b, 0x0070d9, 0x0073de, 0x00843d, 0x00916a, 0x0099f1, 0x004e82, 0x005375, 0x006b04, 0x00721b, 0x00862d, 0x009e1e, 0x005d50, 0x006feb, 0x0085cd, 0x008964, 0x0062c9, 0x0081d8, 0x00881f, 0x005eca, 0x006717, 0x006d6a, 0x0072fc, 0x0090ce, 0x004f86, 0x0051b7, 0x0052de, 0x0064c4, 0x006ad3, 0x007210, 0x0076e7, 0x008001, 0x008606, 0x00865c, 0x008def, 0x009732, 0x009b6f, 0x009dfa, 0x00788c, 0x00797f, 0x007da0, 0x0083c9, 0x009304, 0x009e7f, 0x008ad6, 0x0058df, 0x005f04, 0x007c60, 0x00807e, 0x007262, 0x0078ca, 0x008cc2, 0x0096f7, 0x0058d8, 0x005c62, 0x006a13, 0x006dda, 0x006f0f, 0x007d2f, 0x007e37, 0x00964b, 0x0052d2, 0x00808b, 0x0051dc, 0x0051cc, 0x007a1c, 0x007dbe, 0x0083f1, 0x009675, 0x008b80, 0x0062cf, 0x006a02, 0x008afe, 0x004e39, 0x005be7, 0x006012, 0x007387, 0x007570, 0x005317, 0x0078fb, 0x004fbf, 0x005fa9, 0x004e0d, 0x006ccc, 0x006578, 0x007d22, 0x0053c3, 0x00585e, 0x007701, 0x008449, 0x008aaa, 0x006bba, 0x008fb0, 0x006c88, 0x0062fe, 0x0082e5, 0x0063a0, 0x007565, 0x004eae, 0x005169, 0x0051c9, 0x006881, 0x007ce7, 0x00826f, 0x008ad2, 0x0091cf, 0x0052f5, 0x005442, 0x005973, 0x005eec, 0x0065c5, 0x006ffe, 0x00792a, 0x0095ad, 0x009a6a, 0x009e97, 0x009ece, 0x00529b, 0x0066c6, 0x006b77, 0x008f62, 0x005e74, 0x006190, 0x006200, 0x00649a, 0x006f23, 0x007149, 0x007489, 0x0079ca, 0x007df4, 0x00806f, 0x008f26, 0x0084ee, 0x009023, 0x00934a, 0x005217, 0x0052a3, 0x0054bd, 0x0070c8, 0x0088c2, 0x008aaa, 0x005ec9, 0x005ff5, 0x00637b, 0x006bae, 0x007c3e, 0x007375, 0x004ee4, 0x0056f9, 0x005be7, 0x005dba, 0x00601c, 0x0073b2, 0x007469, 0x007f9a, 0x008046, 0x009234, 0x0096f6, 0x009748, 0x009818, 0x004f8b, 0x0079ae, 0x0091b4, 0x0096b8, 0x0060e1, 0x004e86, 0x0050da, 0x005bee, 0x005c3f, 0x006599, 0x006a02, 0x0071ce, 0x007642, 0x0084fc, 0x00907c, 0x009f8d, 0x006688, 0x00962e, 0x005289, 0x00677b, 0x0067f3, 0x006d41, 0x006e9c, 0x007409, 0x007559, 0x00786b, 0x007d10, 0x00985e, 0x00516d, 0x00622e, 0x009678, 0x00502b, 0x005d19, 0x006dea, 0x008f2a, 0x005f8b, 0x006144, 0x006817, 0x007387, 0x009686, 0x005229, 0x00540f, 0x005c65, 0x006613, 0x00674e, 0x0068a8, 0x006ce5, 0x007406, 0x0075e2, 0x007f79, 0x0088cf, 0x0088e1, 0x0091cc, 0x0096e2, 0x00533f, 0x006eba, 0x00541d, 0x0071d0, 0x007498, 0x0085fa, 0x0096a3, 0x009c57, 0x009e9f, 0x006797, 0x006dcb, 0x0081e8, 0x007acb, 0x007b20, 0x007c92, 0x0072c0, 0x007099, 0x008b58, 0x004ec0, 0x008336, 0x00523a, 0x005207, 0x005ea6, 0x0062d3, 0x007cd6, 0x005b85, 0x006d1e, 0x0066b4, 0x008f3b, 0x00884c, 0x00964d, 0x00898b, 0x005ed3, 0x005140, 0x0055c0, 0x00585a, 0x006674, 0x0051de, 0x00732a, 0x0076ca, 0x00793c, 0x00795e, 0x007965, 0x00798f, 0x009756, 0x007cbe, 0x007fbd, 0x008612, 0x008af8, 0x009038, 0x0090fd, 0x0098ef, 0x0098fc, 0x009928, 0x009db4, 0x0090de, 0x0096b7, 0x004fae, 0x0050e7, 0x00514d, 0x0052c9, 0x0052e4, 0x005351, 0x00559d, 0x005606, 0x005668, 0x005840, 0x0058a8, 0x005c64, 0x005c6e, 0x006094, 0x006168, 0x00618e, 0x0061f2, 0x00654f, 0x0065e2, 0x006691, 0x006885, 0x006d77, 0x006e1a, 0x006f22, 0x00716e, 0x00722b, 0x007422, 0x007891, 0x00793e, 0x007949, 0x007948, 0x007950, 0x007956, 0x00795d, 0x00798d, 0x00798e, 0x007a40, 0x007a81, 0x007bc0, 0x007df4, 0x007e09, 0x007e41, 0x007f72, 0x008005, 0x0081ed, 0x008279, 0x008279, 0x008457, 0x008910, 0x008996, 0x008b01, 0x008b39, 0x008cd3, 0x008d08, 0x008fb6, 0x009038, 0x0096e3, 0x0097ff, 0x00983b, 0x006075, 0x0242ee, 0x008218, 0x004e26, 0x0051b5, 0x005168, 0x004f80, 0x005145, 0x005180, 0x0052c7, 0x0052fa, 0x00559d, 0x005555, 0x005599, 0x0055e2, 0x00585a, 0x0058b3, 0x005944, 0x005954, 0x005a62, 0x005b28, 0x005ed2, 0x005ed9, 0x005f69, 0x005fad, 0x0060d8, 0x00614e, 0x006108, 0x00618e, 0x006160, 0x0061f2, 0x006234, 0x0063c4, 0x00641c, 0x006452, 0x006556, 0x006674, 0x006717, 0x00671b, 0x006756, 0x006b79, 0x006bba, 0x006d41, 0x006edb, 0x006ecb, 0x006f22, 0x00701e, 0x00716e, 0x0077a7, 0x007235, 0x0072af, 0x00732a, 0x007471, 0x007506, 0x00753b, 0x00761d, 0x00761f, 0x0076ca, 0x0076db, 0x0076f4, 0x00774a, 0x007740, 0x0078cc, 0x007ab1, 0x007bc0, 0x007c7b, 0x007d5b, 0x007df4, 0x007f3e, 0x008005, 0x008352, 0x0083ef, 0x008779, 0x008941, 0x008986, 0x008996, 0x008abf, 0x008af8, 0x008acb, 0x008b01, 0x008afe, 0x008aed, 0x008b39, 0x008b8a, 0x008d08, 0x008f38, 0x009072, 0x009199, 0x009276, 0x00967c, 0x0096e3, 0x009756, 0x0097db, 0x0097ff, 0x00980b, 0x00983b, 0x009b12, 0x009f9c, 0x02284a, 0x022844, 0x0233d5, 0x003b9d, 0x004018, 0x004039, 0x025249, 0x025cd0, 0x027ed3, 0x009f43, 0x009f8e, 0x0005d9, 0x0005b4, 0x0005f2, 0x0005b7, 0x0005e9, 0x0005c1, 0x0005e9, 0x0005c2, 0x0005e9, 0x0005bc, 0x0005c1, 0x0005e9, 0x0005bc, 0x0005c2, 0x0005d0, 0x0005b7, 0x0005d0, 0x0005b8, 0x0005d0, 0x0005bc, 0x0005d1, 0x0005bc, 0x0005d2, 0x0005bc, 0x0005d3, 0x0005bc, 0x0005d4, 0x0005bc, 0x0005d5, 0x0005bc, 0x0005d6, 0x0005bc, 0x0005d8, 0x0005bc, 0x0005d9, 0x0005bc, 0x0005da, 0x0005bc, 0x0005db, 0x0005bc, 0x0005dc, 0x0005bc, 0x0005de, 0x0005bc, 0x0005e0, 0x0005bc, 0x0005e1, 0x0005bc, 0x0005e3, 0x0005bc, 0x0005e4, 0x0005bc, 0x0005e6, 0x0005bc, 0x0005e7, 0x0005bc, 0x0005e8, 0x0005bc, 0x0005e9, 0x0005bc, 0x0005ea, 0x0005bc, 0x0005d5, 0x0005b9, 0x0005d1, 0x0005bf, 0x0005db, 0x0005bf, 0x0005e4, 0x0005bf, 0x011099, 0x0110ba, 0x01109b, 0x0110ba, 0x0110a5, 0x0110ba, 0x011131, 0x011127, 0x011132, 0x011127, 0x011347, 0x01133e, 0x011347, 0x011357, 0x0114b9, 0x0114ba, 0x0114b9, 0x0114b0, 0x0114b9, 0x0114bd, 0x0115b8, 0x0115af, 0x0115b9, 0x0115af, 0x01d157, 0x01d165, 0x01d158, 0x01d165, 0x01d158, 0x01d165, 0x01d16e, 0x01d158, 0x01d165, 0x01d16f, 0x01d158, 0x01d165, 0x01d170, 0x01d158, 0x01d165, 0x01d171, 0x01d158, 0x01d165, 0x01d172, 0x01d1b9, 0x01d165, 0x01d1ba, 0x01d165, 0x01d1b9, 0x01d165, 0x01d16e, 0x01d1ba, 0x01d165, 0x01d16e, 0x01d1b9, 0x01d165, 0x01d16f, 0x01d1ba, 0x01d165, 0x01d16f, 0x004e3d, 0x004e38, 0x004e41, 0x020122, 0x004f60, 0x004fae, 0x004fbb, 0x005002, 0x00507a, 0x005099, 0x0050e7, 0x0050cf, 0x00349e, 0x02063a, 0x00514d, 0x005154, 0x005164, 0x005177, 0x02051c, 0x0034b9, 0x005167, 0x00518d, 0x02054b, 0x005197, 0x0051a4, 0x004ecc, 0x0051ac, 0x0051b5, 0x0291df, 0x0051f5, 0x005203, 0x0034df, 0x00523b, 0x005246, 0x005272, 0x005277, 0x003515, 0x0052c7, 0x0052c9, 0x0052e4, 0x0052fa, 0x005305, 0x005306, 0x005317, 0x005349, 0x005351, 0x00535a, 0x005373, 0x00537d, 0x00537f, 0x00537f, 0x00537f, 0x020a2c, 0x007070, 0x0053ca, 0x0053df, 0x020b63, 0x0053eb, 0x0053f1, 0x005406, 0x00549e, 0x005438, 0x005448, 0x005468, 0x0054a2, 0x0054f6, 0x005510, 0x005553, 0x005563, 0x005584, 0x005584, 0x005599, 0x0055ab, 0x0055b3, 0x0055c2, 0x005716, 0x005606, 0x005717, 0x005651, 0x005674, 0x005207, 0x0058ee, 0x0057ce, 0x0057f4, 0x00580d, 0x00578b, 0x005832, 0x005831, 0x0058ac, 0x0214e4, 0x0058f2, 0x0058f7, 0x005906, 0x00591a, 0x005922, 0x005962, 0x0216a8, 0x0216ea, 0x0059ec, 0x005a1b, 0x005a27, 0x0059d8, 0x005a66, 0x0036ee, 0x0036fc, 0x005b08, 0x005b3e, 0x005b3e, 0x0219c8, 0x005bc3, 0x005bd8, 0x005be7, 0x005bf3, 0x021b18, 0x005bff, 0x005c06, 0x005f53, 0x005c22, 0x003781, 0x005c60, 0x005c6e, 0x005cc0, 0x005c8d, 0x021de4, 0x005d43, 0x021de6, 0x005d6e, 0x005d6b, 0x005d7c, 0x005de1, 0x005de2, 0x00382f, 0x005dfd, 0x005e28, 0x005e3d, 0x005e69, 0x003862, 0x022183, 0x00387c, 0x005eb0, 0x005eb3, 0x005eb6, 0x005eca, 0x02a392, 0x005efe, 0x022331, 0x022331, 0x008201, 0x005f22, 0x005f22, 0x0038c7, 0x0232b8, 0x0261da, 0x005f62, 0x005f6b, 0x0038e3, 0x005f9a, 0x005fcd, 0x005fd7, 0x005ff9, 0x006081, 0x00393a, 0x00391c, 0x006094, 0x0226d4, 0x0060c7, 0x006148, 0x00614c, 0x00614e, 0x00614c, 0x00617a, 0x00618e, 0x0061b2, 0x0061a4, 0x0061af, 0x0061de, 0x0061f2, 0x0061f6, 0x006210, 0x00621b, 0x00625d, 0x0062b1, 0x0062d4, 0x006350, 0x022b0c, 0x00633d, 0x0062fc, 0x006368, 0x006383, 0x0063e4, 0x022bf1, 0x006422, 0x0063c5, 0x0063a9, 0x003a2e, 0x006469, 0x00647e, 0x00649d, 0x006477, 0x003a6c, 0x00654f, 0x00656c, 0x02300a, 0x0065e3, 0x0066f8, 0x006649, 0x003b19, 0x006691, 0x003b08, 0x003ae4, 0x005192, 0x005195, 0x006700, 0x00669c, 0x0080ad, 0x0043d9, 0x006717, 0x00671b, 0x006721, 0x00675e, 0x006753, 0x0233c3, 0x003b49, 0x0067fa, 0x006785, 0x006852, 0x006885, 0x02346d, 0x00688e, 0x00681f, 0x006914, 0x003b9d, 0x006942, 0x0069a3, 0x0069ea, 0x006aa8, 0x0236a3, 0x006adb, 0x003c18, 0x006b21, 0x0238a7, 0x006b54, 0x003c4e, 0x006b72, 0x006b9f, 0x006bba, 0x006bbb, 0x023a8d, 0x021d0b, 0x023afa, 0x006c4e, 0x023cbc, 0x006cbf, 0x006ccd, 0x006c67, 0x006d16, 0x006d3e, 0x006d77, 0x006d41, 0x006d69, 0x006d78, 0x006d85, 0x023d1e, 0x006d34, 0x006e2f, 0x006e6e, 0x003d33, 0x006ecb, 0x006ec7, 0x023ed1, 0x006df9, 0x006f6e, 0x023f5e, 0x023f8e, 0x006fc6, 0x007039, 0x00701e, 0x00701b, 0x003d96, 0x00704a, 0x00707d, 0x007077, 0x0070ad, 0x020525, 0x007145, 0x024263, 0x00719c, 0x0243ab, 0x007228, 0x007235, 0x007250, 0x024608, 0x007280, 0x007295, 0x024735, 0x024814, 0x00737a, 0x00738b, 0x003eac, 0x0073a5, 0x003eb8, 0x003eb8, 0x007447, 0x00745c, 0x007471, 0x007485, 0x0074ca, 0x003f1b, 0x007524, 0x024c36, 0x00753e, 0x024c92, 0x007570, 0x02219f, 0x007610, 0x024fa1, 0x024fb8, 0x025044, 0x003ffc, 0x004008, 0x0076f4, 0x0250f3, 0x0250f2, 0x025119, 0x025133, 0x00771e, 0x00771f, 0x00771f, 0x00774a, 0x004039, 0x00778b, 0x004046, 0x004096, 0x02541d, 0x00784e, 0x00788c, 0x0078cc, 0x0040e3, 0x025626, 0x007956, 0x02569a, 0x0256c5, 0x00798f, 0x0079eb, 0x00412f, 0x007a40, 0x007a4a, 0x007a4f, 0x02597c, 0x025aa7, 0x025aa7, 0x007aee, 0x004202, 0x025bab, 0x007bc6, 0x007bc9, 0x004227, 0x025c80, 0x007cd2, 0x0042a0, 0x007ce8, 0x007ce3, 0x007d00, 0x025f86, 0x007d63, 0x004301, 0x007dc7, 0x007e02, 0x007e45, 0x004334, 0x026228, 0x026247, 0x004359, 0x0262d9, 0x007f7a, 0x02633e, 0x007f95, 0x007ffa, 0x008005, 0x0264da, 0x026523, 0x008060, 0x0265a8, 0x008070, 0x02335f, 0x0043d5, 0x0080b2, 0x008103, 0x00440b, 0x00813e, 0x005ab5, 0x0267a7, 0x0267b5, 0x023393, 0x02339c, 0x008201, 0x008204, 0x008f9e, 0x00446b, 0x008291, 0x00828b, 0x00829d, 0x0052b3, 0x0082b1, 0x0082b3, 0x0082bd, 0x0082e6, 0x026b3c, 0x0082e5, 0x00831d, 0x008363, 0x0083ad, 0x008323, 0x0083bd, 0x0083e7, 0x008457, 0x008353, 0x0083ca, 0x0083cc, 0x0083dc, 0x026c36, 0x026d6b, 0x026cd5, 0x00452b, 0x0084f1, 0x0084f3, 0x008516, 0x0273ca, 0x008564, 0x026f2c, 0x00455d, 0x004561, 0x026fb1, 0x0270d2, 0x00456b, 0x008650, 0x00865c, 0x008667, 0x008669, 0x0086a9, 0x008688, 0x00870e, 0x0086e2, 0x008779, 0x008728, 0x00876b, 0x008786, 0x0045d7, 0x0087e1, 0x008801, 0x0045f9, 0x008860, 0x008863, 0x027667, 0x0088d7, 0x0088de, 0x004635, 0x0088fa, 0x0034bb, 0x0278ae, 0x027966, 0x0046be, 0x0046c7, 0x008aa0, 0x008aed, 0x008b8a, 0x008c55, 0x027ca8, 0x008cab, 0x008cc1, 0x008d1b, 0x008d77, 0x027f2f, 0x020804, 0x008dcb, 0x008dbc, 0x008df0, 0x0208de, 0x008ed4, 0x008f38, 0x0285d2, 0x0285ed, 0x009094, 0x0090f1, 0x009111, 0x02872e, 0x00911b, 0x009238, 0x0092d7, 0x0092d8, 0x00927c, 0x0093f9, 0x009415, 0x028bfa, 0x00958b, 0x004995, 0x0095b7, 0x028d77, 0x0049e6, 0x0096c3, 0x005db2, 0x009723, 0x029145, 0x02921a, 0x004a6e, 0x004a76, 0x0097e0, 0x02940a, 0x004ab2, 0x029496, 0x00980b, 0x00980b, 0x009829, 0x0295b6, 0x0098e2, 0x004b33, 0x009929, 0x0099a7, 0x0099c2, 0x0099fe, 0x004bce, 0x029b30, 0x009b12, 0x009c40, 0x009cfd, 0x004cce, 0x004ced, 0x009d67, 0x02a0ce, 0x004cf8, 0x02a105, 0x02a20e, 0x02a291, 0x009ebb, 0x004d56, 0x009ef9, 0x009efe, 0x009f05, 0x009f0f, 0x009f16, 0x009f3b, 0x02a600, }; static u16 cf_trie[] = { /* Node for range 0x_____ */ 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0____ */ 0x0003, 0x0004, 0x0005, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, /* Node for range 0x1____ */ 0x0008, 0x0009, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000a, 0x0000, /* Node for range 0x00___ */ 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01___ */ 0x0011, 0x0000, 0x0000, 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0013, 0x0000, 0x0014, 0x0015, /* Node for range 0x02___ */ 0x0000, 0x0016, 0x0000, 0x0000, 0x0017, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0018, 0x0000, 0x0000, 0x0000, /* Node for range 0x0a___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0019, 0x001a, 0x0000, 0x0000, 0x0000, 0x001b, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0f___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001c, 0x0000, 0x0000, 0x0000, 0x001d, /* Node for range 0x10___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001f, 0x0000, 0x0000, 0x0000, /* Node for range 0x11___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1e___ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0021, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x000__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0022, 0x0023, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0024, 0x0025, 0x0026, 0x0000, 0x0000, /* Node for range 0x001__ */ 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, /* Node for range 0x002__ */ 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x003__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, /* Node for range 0x004__ */ 0x0046, 0x0047, 0x0048, 0x0000, 0x0000, 0x0000, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, /* Node for range 0x005__ */ 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0000, 0x0000, 0x0059, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x010__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x005a, 0x005b, 0x005c, 0x0000, 0x0000, 0x0000, /* Node for range 0x013__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x005d, /* Node for range 0x01c__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x005e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01e__ */ 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, /* Node for range 0x01f__ */ 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0000, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, /* Node for range 0x021__ */ 0x0000, 0x0000, 0x007e, 0x007f, 0x0000, 0x0000, 0x0080, 0x0000, 0x0081, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x024__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0082, 0x0083, 0x0000, 0x0000, 0x0000, /* Node for range 0x02c__ */ 0x0084, 0x0085, 0x0086, 0x0000, 0x0000, 0x0000, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, /* Node for range 0x0a6__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0091, 0x0092, 0x0093, 0x0000, 0x0094, 0x0095, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0a7__ */ 0x0000, 0x0000, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0ab__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0fb__ */ 0x00a5, 0x00a6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0ff__ */ 0x0000, 0x0000, 0x00a7, 0x00a8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x104__ */ 0x00a9, 0x00aa, 0x00ab, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00ac, 0x00ad, 0x00ae, 0x0000, 0x0000, /* Node for range 0x10c__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x118__ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00b3, 0x00b4, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1e9__ */ 0x00b5, 0x00b6, 0x00b7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0004_ */ 0x0000, 0x0001, 0x0009, 0x0011, 0x0019, 0x0021, 0x0029, 0x0031, 0x0039, 0x0041, 0x0049, 0x0051, 0x0059, 0x0061, 0x0069, 0x0071, /* Node for range 0x0005_ */ 0x0079, 0x0081, 0x0089, 0x0091, 0x0099, 0x00a1, 0x00a9, 0x00b1, 0x00b9, 0x00c1, 0x00c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x000b_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x000c_ */ 0x00d9, 0x00e1, 0x00e9, 0x00f1, 0x00f9, 0x0101, 0x0109, 0x0111, 0x0119, 0x0121, 0x0129, 0x0131, 0x0139, 0x0141, 0x0149, 0x0151, /* Node for range 0x000d_ */ 0x0159, 0x0161, 0x0169, 0x0171, 0x0179, 0x0181, 0x0189, 0x0000, 0x0191, 0x0199, 0x01a1, 0x01a9, 0x01b1, 0x01b9, 0x01c1, 0x01ca, /* Node for range 0x0010_ */ 0x01d9, 0x0000, 0x01e1, 0x0000, 0x01e9, 0x0000, 0x01f1, 0x0000, 0x01f9, 0x0000, 0x0201, 0x0000, 0x0209, 0x0000, 0x0211, 0x0000, /* Node for range 0x0011_ */ 0x0219, 0x0000, 0x0221, 0x0000, 0x0229, 0x0000, 0x0231, 0x0000, 0x0239, 0x0000, 0x0241, 0x0000, 0x0249, 0x0000, 0x0251, 0x0000, /* Node for range 0x0012_ */ 0x0259, 0x0000, 0x0261, 0x0000, 0x0269, 0x0000, 0x0271, 0x0000, 0x0279, 0x0000, 0x0281, 0x0000, 0x0289, 0x0000, 0x0291, 0x0000, /* Node for range 0x0013_ */ 0x029a, 0x0000, 0x02a9, 0x0000, 0x02b1, 0x0000, 0x02b9, 0x0000, 0x0000, 0x02c1, 0x0000, 0x02c9, 0x0000, 0x02d1, 0x0000, 0x02d9, /* Node for range 0x0014_ */ 0x0000, 0x02e1, 0x0000, 0x02e9, 0x0000, 0x02f1, 0x0000, 0x02f9, 0x0000, 0x0302, 0x0311, 0x0000, 0x0319, 0x0000, 0x0321, 0x0000, /* Node for range 0x0015_ */ 0x0329, 0x0000, 0x0331, 0x0000, 0x0339, 0x0000, 0x0341, 0x0000, 0x0349, 0x0000, 0x0351, 0x0000, 0x0359, 0x0000, 0x0361, 0x0000, /* Node for range 0x0016_ */ 0x0369, 0x0000, 0x0371, 0x0000, 0x0379, 0x0000, 0x0381, 0x0000, 0x0389, 0x0000, 0x0391, 0x0000, 0x0399, 0x0000, 0x03a1, 0x0000, /* Node for range 0x0017_ */ 0x03a9, 0x0000, 0x03b1, 0x0000, 0x03b9, 0x0000, 0x03c1, 0x0000, 0x03c9, 0x03d1, 0x0000, 0x03d9, 0x0000, 0x03e1, 0x0000, 0x03e9, /* Node for range 0x0018_ */ 0x0000, 0x03f1, 0x03f9, 0x0000, 0x0401, 0x0000, 0x0409, 0x0411, 0x0000, 0x0419, 0x0421, 0x0429, 0x0000, 0x0000, 0x0431, 0x0439, /* Node for range 0x0019_ */ 0x0441, 0x0449, 0x0000, 0x0451, 0x0459, 0x0000, 0x0461, 0x0469, 0x0471, 0x0000, 0x0000, 0x0000, 0x0479, 0x0481, 0x0000, 0x0489, /* Node for range 0x001a_ */ 0x0491, 0x0000, 0x0499, 0x0000, 0x04a1, 0x0000, 0x04a9, 0x04b1, 0x0000, 0x04b9, 0x0000, 0x0000, 0x04c1, 0x0000, 0x04c9, 0x04d1, /* Node for range 0x001b_ */ 0x0000, 0x04d9, 0x04e1, 0x04e9, 0x0000, 0x04f1, 0x0000, 0x04f9, 0x0501, 0x0000, 0x0000, 0x0000, 0x0509, 0x0000, 0x0000, 0x0000, /* Node for range 0x001c_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0511, 0x0519, 0x0000, 0x0521, 0x0529, 0x0000, 0x0531, 0x0539, 0x0000, 0x0541, 0x0000, 0x0549, /* Node for range 0x001d_ */ 0x0000, 0x0551, 0x0000, 0x0559, 0x0000, 0x0561, 0x0000, 0x0569, 0x0000, 0x0571, 0x0000, 0x0579, 0x0000, 0x0000, 0x0581, 0x0000, /* Node for range 0x001e_ */ 0x0589, 0x0000, 0x0591, 0x0000, 0x0599, 0x0000, 0x05a1, 0x0000, 0x05a9, 0x0000, 0x05b1, 0x0000, 0x05b9, 0x0000, 0x05c1, 0x0000, /* Node for range 0x001f_ */ 0x05ca, 0x05d9, 0x05e1, 0x0000, 0x05e9, 0x0000, 0x05f1, 0x05f9, 0x0601, 0x0000, 0x0609, 0x0000, 0x0611, 0x0000, 0x0619, 0x0000, /* Node for range 0x0020_ */ 0x0621, 0x0000, 0x0629, 0x0000, 0x0631, 0x0000, 0x0639, 0x0000, 0x0641, 0x0000, 0x0649, 0x0000, 0x0651, 0x0000, 0x0659, 0x0000, /* Node for range 0x0021_ */ 0x0661, 0x0000, 0x0669, 0x0000, 0x0671, 0x0000, 0x0679, 0x0000, 0x0681, 0x0000, 0x0689, 0x0000, 0x0691, 0x0000, 0x0699, 0x0000, /* Node for range 0x0022_ */ 0x06a1, 0x0000, 0x06a9, 0x0000, 0x06b1, 0x0000, 0x06b9, 0x0000, 0x06c1, 0x0000, 0x06c9, 0x0000, 0x06d1, 0x0000, 0x06d9, 0x0000, /* Node for range 0x0023_ */ 0x06e1, 0x0000, 0x06e9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x06f1, 0x06f9, 0x0000, 0x0701, 0x0709, 0x0000, /* Node for range 0x0024_ */ 0x0000, 0x0711, 0x0000, 0x0719, 0x0721, 0x0729, 0x0731, 0x0000, 0x0739, 0x0000, 0x0741, 0x0000, 0x0749, 0x0000, 0x0751, 0x0000, /* Node for range 0x0034_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0759, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0037_ */ 0x0761, 0x0000, 0x0769, 0x0000, 0x0000, 0x0000, 0x0771, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0779, /* Node for range 0x0038_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0781, 0x0000, 0x0789, 0x0791, 0x0799, 0x0000, 0x07a1, 0x0000, 0x07a9, 0x07b1, /* Node for range 0x0039_ */ 0x07bb, 0x07d1, 0x07d9, 0x07e1, 0x07e9, 0x07f1, 0x07f9, 0x0801, 0x0809, 0x0811, 0x0819, 0x0821, 0x0829, 0x0831, 0x0839, 0x0841, /* Node for range 0x003a_ */ 0x0849, 0x0851, 0x0000, 0x0859, 0x0861, 0x0869, 0x0871, 0x0879, 0x0881, 0x0889, 0x0891, 0x0899, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x003b_ */ 0x08a3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x003c_ */ 0x0000, 0x0000, 0x08b9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x08c1, /* Node for range 0x003d_ */ 0x08c9, 0x08d1, 0x0000, 0x0000, 0x0000, 0x08d9, 0x08e1, 0x0000, 0x08e9, 0x0000, 0x08f1, 0x0000, 0x08f9, 0x0000, 0x0901, 0x0000, /* Node for range 0x003e_ */ 0x0909, 0x0000, 0x0911, 0x0000, 0x0919, 0x0000, 0x0921, 0x0000, 0x0929, 0x0000, 0x0931, 0x0000, 0x0939, 0x0000, 0x0941, 0x0000, /* Node for range 0x003f_ */ 0x0949, 0x0951, 0x0000, 0x0000, 0x0959, 0x0961, 0x0000, 0x0969, 0x0000, 0x0971, 0x0979, 0x0000, 0x0000, 0x0981, 0x0989, 0x0991, /* Node for range 0x0040_ */ 0x0999, 0x09a1, 0x09a9, 0x09b1, 0x09b9, 0x09c1, 0x09c9, 0x09d1, 0x09d9, 0x09e1, 0x09e9, 0x09f1, 0x09f9, 0x0a01, 0x0a09, 0x0a11, /* Node for range 0x0041_ */ 0x0a19, 0x0a21, 0x0a29, 0x0a31, 0x0a39, 0x0a41, 0x0a49, 0x0a51, 0x0a59, 0x0a61, 0x0a69, 0x0a71, 0x0a79, 0x0a81, 0x0a89, 0x0a91, /* Node for range 0x0042_ */ 0x0a99, 0x0aa1, 0x0aa9, 0x0ab1, 0x0ab9, 0x0ac1, 0x0ac9, 0x0ad1, 0x0ad9, 0x0ae1, 0x0ae9, 0x0af1, 0x0af9, 0x0b01, 0x0b09, 0x0b11, /* Node for range 0x0046_ */ 0x0b19, 0x0000, 0x0b21, 0x0000, 0x0b29, 0x0000, 0x0b31, 0x0000, 0x0b39, 0x0000, 0x0b41, 0x0000, 0x0b49, 0x0000, 0x0b51, 0x0000, /* Node for range 0x0047_ */ 0x0b59, 0x0000, 0x0b61, 0x0000, 0x0b69, 0x0000, 0x0b71, 0x0000, 0x0b79, 0x0000, 0x0b81, 0x0000, 0x0b89, 0x0000, 0x0b91, 0x0000, /* Node for range 0x0048_ */ 0x0b99, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0ba1, 0x0000, 0x0ba9, 0x0000, 0x0bb1, 0x0000, /* Node for range 0x0049_ */ 0x0bb9, 0x0000, 0x0bc1, 0x0000, 0x0bc9, 0x0000, 0x0bd1, 0x0000, 0x0bd9, 0x0000, 0x0be1, 0x0000, 0x0be9, 0x0000, 0x0bf1, 0x0000, /* Node for range 0x004a_ */ 0x0bf9, 0x0000, 0x0c01, 0x0000, 0x0c09, 0x0000, 0x0c11, 0x0000, 0x0c19, 0x0000, 0x0c21, 0x0000, 0x0c29, 0x0000, 0x0c31, 0x0000, /* Node for range 0x004b_ */ 0x0c39, 0x0000, 0x0c41, 0x0000, 0x0c49, 0x0000, 0x0c51, 0x0000, 0x0c59, 0x0000, 0x0c61, 0x0000, 0x0c69, 0x0000, 0x0c71, 0x0000, /* Node for range 0x004c_ */ 0x0c79, 0x0c81, 0x0000, 0x0c89, 0x0000, 0x0c91, 0x0000, 0x0c99, 0x0000, 0x0ca1, 0x0000, 0x0ca9, 0x0000, 0x0cb1, 0x0000, 0x0000, /* Node for range 0x004d_ */ 0x0cb9, 0x0000, 0x0cc1, 0x0000, 0x0cc9, 0x0000, 0x0cd1, 0x0000, 0x0cd9, 0x0000, 0x0ce1, 0x0000, 0x0ce9, 0x0000, 0x0cf1, 0x0000, /* Node for range 0x004e_ */ 0x0cf9, 0x0000, 0x0d01, 0x0000, 0x0d09, 0x0000, 0x0d11, 0x0000, 0x0d19, 0x0000, 0x0d21, 0x0000, 0x0d29, 0x0000, 0x0d31, 0x0000, /* Node for range 0x004f_ */ 0x0d39, 0x0000, 0x0d41, 0x0000, 0x0d49, 0x0000, 0x0d51, 0x0000, 0x0d59, 0x0000, 0x0d61, 0x0000, 0x0d69, 0x0000, 0x0d71, 0x0000, /* Node for range 0x0050_ */ 0x0d79, 0x0000, 0x0d81, 0x0000, 0x0d89, 0x0000, 0x0d91, 0x0000, 0x0d99, 0x0000, 0x0da1, 0x0000, 0x0da9, 0x0000, 0x0db1, 0x0000, /* Node for range 0x0051_ */ 0x0db9, 0x0000, 0x0dc1, 0x0000, 0x0dc9, 0x0000, 0x0dd1, 0x0000, 0x0dd9, 0x0000, 0x0de1, 0x0000, 0x0de9, 0x0000, 0x0df1, 0x0000, /* Node for range 0x0052_ */ 0x0df9, 0x0000, 0x0e01, 0x0000, 0x0e09, 0x0000, 0x0e11, 0x0000, 0x0e19, 0x0000, 0x0e21, 0x0000, 0x0e29, 0x0000, 0x0e31, 0x0000, /* Node for range 0x0053_ */ 0x0000, 0x0e39, 0x0e41, 0x0e49, 0x0e51, 0x0e59, 0x0e61, 0x0e69, 0x0e71, 0x0e79, 0x0e81, 0x0e89, 0x0e91, 0x0e99, 0x0ea1, 0x0ea9, /* Node for range 0x0054_ */ 0x0eb1, 0x0eb9, 0x0ec1, 0x0ec9, 0x0ed1, 0x0ed9, 0x0ee1, 0x0ee9, 0x0ef1, 0x0ef9, 0x0f01, 0x0f09, 0x0f11, 0x0f19, 0x0f21, 0x0f29, /* Node for range 0x0055_ */ 0x0f31, 0x0f39, 0x0f41, 0x0f49, 0x0f51, 0x0f59, 0x0f61, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0058_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0f6a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x010a_ */ 0x0f79, 0x0f81, 0x0f89, 0x0f91, 0x0f99, 0x0fa1, 0x0fa9, 0x0fb1, 0x0fb9, 0x0fc1, 0x0fc9, 0x0fd1, 0x0fd9, 0x0fe1, 0x0fe9, 0x0ff1, /* Node for range 0x010b_ */ 0x0ff9, 0x1001, 0x1009, 0x1011, 0x1019, 0x1021, 0x1029, 0x1031, 0x1039, 0x1041, 0x1049, 0x1051, 0x1059, 0x1061, 0x1069, 0x1071, /* Node for range 0x010c_ */ 0x1079, 0x1081, 0x1089, 0x1091, 0x1099, 0x10a1, 0x0000, 0x10a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x10b1, 0x0000, 0x0000, /* Node for range 0x013f_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x10b9, 0x10c1, 0x10c9, 0x10d1, 0x10d9, 0x10e1, 0x0000, 0x0000, /* Node for range 0x01c8_ */ 0x10e9, 0x10f1, 0x10f9, 0x1101, 0x1109, 0x1111, 0x1119, 0x1121, 0x1129, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01e0_ */ 0x1131, 0x0000, 0x1139, 0x0000, 0x1141, 0x0000, 0x1149, 0x0000, 0x1151, 0x0000, 0x1159, 0x0000, 0x1161, 0x0000, 0x1169, 0x0000, /* Node for range 0x01e1_ */ 0x1171, 0x0000, 0x1179, 0x0000, 0x1181, 0x0000, 0x1189, 0x0000, 0x1191, 0x0000, 0x1199, 0x0000, 0x11a1, 0x0000, 0x11a9, 0x0000, /* Node for range 0x01e2_ */ 0x11b1, 0x0000, 0x11b9, 0x0000, 0x11c1, 0x0000, 0x11c9, 0x0000, 0x11d1, 0x0000, 0x11d9, 0x0000, 0x11e1, 0x0000, 0x11e9, 0x0000, /* Node for range 0x01e3_ */ 0x11f1, 0x0000, 0x11f9, 0x0000, 0x1201, 0x0000, 0x1209, 0x0000, 0x1211, 0x0000, 0x1219, 0x0000, 0x1221, 0x0000, 0x1229, 0x0000, /* Node for range 0x01e4_ */ 0x1231, 0x0000, 0x1239, 0x0000, 0x1241, 0x0000, 0x1249, 0x0000, 0x1251, 0x0000, 0x1259, 0x0000, 0x1261, 0x0000, 0x1269, 0x0000, /* Node for range 0x01e5_ */ 0x1271, 0x0000, 0x1279, 0x0000, 0x1281, 0x0000, 0x1289, 0x0000, 0x1291, 0x0000, 0x1299, 0x0000, 0x12a1, 0x0000, 0x12a9, 0x0000, /* Node for range 0x01e6_ */ 0x12b1, 0x0000, 0x12b9, 0x0000, 0x12c1, 0x0000, 0x12c9, 0x0000, 0x12d1, 0x0000, 0x12d9, 0x0000, 0x12e1, 0x0000, 0x12e9, 0x0000, /* Node for range 0x01e7_ */ 0x12f1, 0x0000, 0x12f9, 0x0000, 0x1301, 0x0000, 0x1309, 0x0000, 0x1311, 0x0000, 0x1319, 0x0000, 0x1321, 0x0000, 0x1329, 0x0000, /* Node for range 0x01e8_ */ 0x1331, 0x0000, 0x1339, 0x0000, 0x1341, 0x0000, 0x1349, 0x0000, 0x1351, 0x0000, 0x1359, 0x0000, 0x1361, 0x0000, 0x1369, 0x0000, /* Node for range 0x01e9_ */ 0x1371, 0x0000, 0x1379, 0x0000, 0x1381, 0x0000, 0x138a, 0x139a, 0x13aa, 0x13ba, 0x13ca, 0x13d9, 0x0000, 0x0000, 0x13e2, 0x0000, /* Node for range 0x01ea_ */ 0x13f1, 0x0000, 0x13f9, 0x0000, 0x1401, 0x0000, 0x1409, 0x0000, 0x1411, 0x0000, 0x1419, 0x0000, 0x1421, 0x0000, 0x1429, 0x0000, /* Node for range 0x01eb_ */ 0x1431, 0x0000, 0x1439, 0x0000, 0x1441, 0x0000, 0x1449, 0x0000, 0x1451, 0x0000, 0x1459, 0x0000, 0x1461, 0x0000, 0x1469, 0x0000, /* Node for range 0x01ec_ */ 0x1471, 0x0000, 0x1479, 0x0000, 0x1481, 0x0000, 0x1489, 0x0000, 0x1491, 0x0000, 0x1499, 0x0000, 0x14a1, 0x0000, 0x14a9, 0x0000, /* Node for range 0x01ed_ */ 0x14b1, 0x0000, 0x14b9, 0x0000, 0x14c1, 0x0000, 0x14c9, 0x0000, 0x14d1, 0x0000, 0x14d9, 0x0000, 0x14e1, 0x0000, 0x14e9, 0x0000, /* Node for range 0x01ee_ */ 0x14f1, 0x0000, 0x14f9, 0x0000, 0x1501, 0x0000, 0x1509, 0x0000, 0x1511, 0x0000, 0x1519, 0x0000, 0x1521, 0x0000, 0x1529, 0x0000, /* Node for range 0x01ef_ */ 0x1531, 0x0000, 0x1539, 0x0000, 0x1541, 0x0000, 0x1549, 0x0000, 0x1551, 0x0000, 0x1559, 0x0000, 0x1561, 0x0000, 0x1569, 0x0000, /* Node for range 0x01f0_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1571, 0x1579, 0x1581, 0x1589, 0x1591, 0x1599, 0x15a1, 0x15a9, /* Node for range 0x01f1_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15b1, 0x15b9, 0x15c1, 0x15c9, 0x15d1, 0x15d9, 0x0000, 0x0000, /* Node for range 0x01f2_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x15e1, 0x15e9, 0x15f1, 0x15f9, 0x1601, 0x1609, 0x1611, 0x1619, /* Node for range 0x01f3_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1621, 0x1629, 0x1631, 0x1639, 0x1641, 0x1649, 0x1651, 0x1659, /* Node for range 0x01f4_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1661, 0x1669, 0x1671, 0x1679, 0x1681, 0x1689, 0x0000, 0x0000, /* Node for range 0x01f5_ */ 0x1692, 0x0000, 0x16a3, 0x0000, 0x16bb, 0x0000, 0x16d3, 0x0000, 0x0000, 0x16e9, 0x0000, 0x16f1, 0x0000, 0x16f9, 0x0000, 0x1701, /* Node for range 0x01f6_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1709, 0x1711, 0x1719, 0x1721, 0x1729, 0x1731, 0x1739, 0x1741, /* Node for range 0x01f8_ */ 0x174a, 0x175a, 0x176a, 0x177a, 0x178a, 0x179a, 0x17aa, 0x17ba, 0x17ca, 0x17da, 0x17ea, 0x17fa, 0x180a, 0x181a, 0x182a, 0x183a, /* Node for range 0x01f9_ */ 0x184a, 0x185a, 0x186a, 0x187a, 0x188a, 0x189a, 0x18aa, 0x18ba, 0x18ca, 0x18da, 0x18ea, 0x18fa, 0x190a, 0x191a, 0x192a, 0x193a, /* Node for range 0x01fa_ */ 0x194a, 0x195a, 0x196a, 0x197a, 0x198a, 0x199a, 0x19aa, 0x19ba, 0x19ca, 0x19da, 0x19ea, 0x19fa, 0x1a0a, 0x1a1a, 0x1a2a, 0x1a3a, /* Node for range 0x01fb_ */ 0x0000, 0x0000, 0x1a4a, 0x1a5a, 0x1a6a, 0x0000, 0x1a7a, 0x1a8b, 0x1aa1, 0x1aa9, 0x1ab1, 0x1ab9, 0x1ac2, 0x0000, 0x1ad1, 0x0000, /* Node for range 0x01fc_ */ 0x0000, 0x0000, 0x1ada, 0x1aea, 0x1afa, 0x0000, 0x1b0a, 0x1b1b, 0x1b31, 0x1b39, 0x1b41, 0x1b49, 0x1b52, 0x0000, 0x0000, 0x0000, /* Node for range 0x01fd_ */ 0x0000, 0x0000, 0x1b63, 0x1b7b, 0x0000, 0x0000, 0x1b92, 0x1ba3, 0x1bb9, 0x1bc1, 0x1bc9, 0x1bd1, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x01fe_ */ 0x0000, 0x0000, 0x1bdb, 0x1bf3, 0x1c0a, 0x0000, 0x1c1a, 0x1c2b, 0x1c41, 0x1c49, 0x1c51, 0x1c59, 0x1c61, 0x0000, 0x0000, 0x0000, /* Node for range 0x01ff_ */ 0x0000, 0x0000, 0x1c6a, 0x1c7a, 0x1c8a, 0x0000, 0x1c9a, 0x1cab, 0x1cc1, 0x1cc9, 0x1cd1, 0x1cd9, 0x1ce2, 0x0000, 0x0000, 0x0000, /* Node for range 0x0212_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1cf1, 0x0000, 0x0000, 0x0000, 0x1cf9, 0x1d01, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0213_ */ 0x0000, 0x0000, 0x1d09, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0216_ */ 0x1d11, 0x1d19, 0x1d21, 0x1d29, 0x1d31, 0x1d39, 0x1d41, 0x1d49, 0x1d51, 0x1d59, 0x1d61, 0x1d69, 0x1d71, 0x1d79, 0x1d81, 0x1d89, /* Node for range 0x0218_ */ 0x0000, 0x0000, 0x0000, 0x1d91, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x024b_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1d99, 0x1da1, 0x1da9, 0x1db1, 0x1db9, 0x1dc1, 0x1dc9, 0x1dd1, 0x1dd9, 0x1de1, /* Node for range 0x024c_ */ 0x1de9, 0x1df1, 0x1df9, 0x1e01, 0x1e09, 0x1e11, 0x1e19, 0x1e21, 0x1e29, 0x1e31, 0x1e39, 0x1e41, 0x1e49, 0x1e51, 0x1e59, 0x1e61, /* Node for range 0x02c0_ */ 0x1e69, 0x1e71, 0x1e79, 0x1e81, 0x1e89, 0x1e91, 0x1e99, 0x1ea1, 0x1ea9, 0x1eb1, 0x1eb9, 0x1ec1, 0x1ec9, 0x1ed1, 0x1ed9, 0x1ee1, /* Node for range 0x02c1_ */ 0x1ee9, 0x1ef1, 0x1ef9, 0x1f01, 0x1f09, 0x1f11, 0x1f19, 0x1f21, 0x1f29, 0x1f31, 0x1f39, 0x1f41, 0x1f49, 0x1f51, 0x1f59, 0x1f61, /* Node for range 0x02c2_ */ 0x1f69, 0x1f71, 0x1f79, 0x1f81, 0x1f89, 0x1f91, 0x1f99, 0x1fa1, 0x1fa9, 0x1fb1, 0x1fb9, 0x1fc1, 0x1fc9, 0x1fd1, 0x1fd9, 0x0000, /* Node for range 0x02c6_ */ 0x1fe1, 0x0000, 0x1fe9, 0x1ff1, 0x1ff9, 0x0000, 0x0000, 0x2001, 0x0000, 0x2009, 0x0000, 0x2011, 0x0000, 0x2019, 0x2021, 0x2029, /* Node for range 0x02c7_ */ 0x2031, 0x0000, 0x2039, 0x0000, 0x0000, 0x2041, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2049, 0x2051, /* Node for range 0x02c8_ */ 0x2059, 0x0000, 0x2061, 0x0000, 0x2069, 0x0000, 0x2071, 0x0000, 0x2079, 0x0000, 0x2081, 0x0000, 0x2089, 0x0000, 0x2091, 0x0000, /* Node for range 0x02c9_ */ 0x2099, 0x0000, 0x20a1, 0x0000, 0x20a9, 0x0000, 0x20b1, 0x0000, 0x20b9, 0x0000, 0x20c1, 0x0000, 0x20c9, 0x0000, 0x20d1, 0x0000, /* Node for range 0x02ca_ */ 0x20d9, 0x0000, 0x20e1, 0x0000, 0x20e9, 0x0000, 0x20f1, 0x0000, 0x20f9, 0x0000, 0x2101, 0x0000, 0x2109, 0x0000, 0x2111, 0x0000, /* Node for range 0x02cb_ */ 0x2119, 0x0000, 0x2121, 0x0000, 0x2129, 0x0000, 0x2131, 0x0000, 0x2139, 0x0000, 0x2141, 0x0000, 0x2149, 0x0000, 0x2151, 0x0000, /* Node for range 0x02cc_ */ 0x2159, 0x0000, 0x2161, 0x0000, 0x2169, 0x0000, 0x2171, 0x0000, 0x2179, 0x0000, 0x2181, 0x0000, 0x2189, 0x0000, 0x2191, 0x0000, /* Node for range 0x02cd_ */ 0x2199, 0x0000, 0x21a1, 0x0000, 0x21a9, 0x0000, 0x21b1, 0x0000, 0x21b9, 0x0000, 0x21c1, 0x0000, 0x21c9, 0x0000, 0x21d1, 0x0000, /* Node for range 0x02ce_ */ 0x21d9, 0x0000, 0x21e1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x21e9, 0x0000, 0x21f1, 0x0000, 0x0000, /* Node for range 0x02cf_ */ 0x0000, 0x0000, 0x21f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0a64_ */ 0x2201, 0x0000, 0x2209, 0x0000, 0x2211, 0x0000, 0x2219, 0x0000, 0x2221, 0x0000, 0x2229, 0x0000, 0x2231, 0x0000, 0x2239, 0x0000, /* Node for range 0x0a65_ */ 0x2241, 0x0000, 0x2249, 0x0000, 0x2251, 0x0000, 0x2259, 0x0000, 0x2261, 0x0000, 0x2269, 0x0000, 0x2271, 0x0000, 0x2279, 0x0000, /* Node for range 0x0a66_ */ 0x2281, 0x0000, 0x2289, 0x0000, 0x2291, 0x0000, 0x2299, 0x0000, 0x22a1, 0x0000, 0x22a9, 0x0000, 0x22b1, 0x0000, 0x0000, 0x0000, /* Node for range 0x0a68_ */ 0x22b9, 0x0000, 0x22c1, 0x0000, 0x22c9, 0x0000, 0x22d1, 0x0000, 0x22d9, 0x0000, 0x22e1, 0x0000, 0x22e9, 0x0000, 0x22f1, 0x0000, /* Node for range 0x0a69_ */ 0x22f9, 0x0000, 0x2301, 0x0000, 0x2309, 0x0000, 0x2311, 0x0000, 0x2319, 0x0000, 0x2321, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0a72_ */ 0x0000, 0x0000, 0x2329, 0x0000, 0x2331, 0x0000, 0x2339, 0x0000, 0x2341, 0x0000, 0x2349, 0x0000, 0x2351, 0x0000, 0x2359, 0x0000, /* Node for range 0x0a73_ */ 0x0000, 0x0000, 0x2361, 0x0000, 0x2369, 0x0000, 0x2371, 0x0000, 0x2379, 0x0000, 0x2381, 0x0000, 0x2389, 0x0000, 0x2391, 0x0000, /* Node for range 0x0a74_ */ 0x2399, 0x0000, 0x23a1, 0x0000, 0x23a9, 0x0000, 0x23b1, 0x0000, 0x23b9, 0x0000, 0x23c1, 0x0000, 0x23c9, 0x0000, 0x23d1, 0x0000, /* Node for range 0x0a75_ */ 0x23d9, 0x0000, 0x23e1, 0x0000, 0x23e9, 0x0000, 0x23f1, 0x0000, 0x23f9, 0x0000, 0x2401, 0x0000, 0x2409, 0x0000, 0x2411, 0x0000, /* Node for range 0x0a76_ */ 0x2419, 0x0000, 0x2421, 0x0000, 0x2429, 0x0000, 0x2431, 0x0000, 0x2439, 0x0000, 0x2441, 0x0000, 0x2449, 0x0000, 0x2451, 0x0000, /* Node for range 0x0a77_ */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2459, 0x0000, 0x2461, 0x0000, 0x2469, 0x2471, 0x0000, /* Node for range 0x0a78_ */ 0x2479, 0x0000, 0x2481, 0x0000, 0x2489, 0x0000, 0x2491, 0x0000, 0x0000, 0x0000, 0x0000, 0x2499, 0x0000, 0x24a1, 0x0000, 0x0000, /* Node for range 0x0a79_ */ 0x24a9, 0x0000, 0x24b1, 0x0000, 0x0000, 0x0000, 0x24b9, 0x0000, 0x24c1, 0x0000, 0x24c9, 0x0000, 0x24d1, 0x0000, 0x24d9, 0x0000, /* Node for range 0x0a7a_ */ 0x24e1, 0x0000, 0x24e9, 0x0000, 0x24f1, 0x0000, 0x24f9, 0x0000, 0x2501, 0x0000, 0x2509, 0x2511, 0x2519, 0x2521, 0x2529, 0x0000, /* Node for range 0x0a7b_ */ 0x2531, 0x2539, 0x2541, 0x2549, 0x2551, 0x0000, 0x2559, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0ab7_ */ 0x2561, 0x2569, 0x2571, 0x2579, 0x2581, 0x2589, 0x2591, 0x2599, 0x25a1, 0x25a9, 0x25b1, 0x25b9, 0x25c1, 0x25c9, 0x25d1, 0x25d9, /* Node for range 0x0ab8_ */ 0x25e1, 0x25e9, 0x25f1, 0x25f9, 0x2601, 0x2609, 0x2611, 0x2619, 0x2621, 0x2629, 0x2631, 0x2639, 0x2641, 0x2649, 0x2651, 0x2659, /* Node for range 0x0ab9_ */ 0x2661, 0x2669, 0x2671, 0x2679, 0x2681, 0x2689, 0x2691, 0x2699, 0x26a1, 0x26a9, 0x26b1, 0x26b9, 0x26c1, 0x26c9, 0x26d1, 0x26d9, /* Node for range 0x0aba_ */ 0x26e1, 0x26e9, 0x26f1, 0x26f9, 0x2701, 0x2709, 0x2711, 0x2719, 0x2721, 0x2729, 0x2731, 0x2739, 0x2741, 0x2749, 0x2751, 0x2759, /* Node for range 0x0abb_ */ 0x2761, 0x2769, 0x2771, 0x2779, 0x2781, 0x2789, 0x2791, 0x2799, 0x27a1, 0x27a9, 0x27b1, 0x27b9, 0x27c1, 0x27c9, 0x27d1, 0x27d9, /* Node for range 0x0fb0_ */ 0x27e2, 0x27f2, 0x2802, 0x2813, 0x282b, 0x2842, 0x2852, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0fb1_ */ 0x0000, 0x0000, 0x0000, 0x2862, 0x2872, 0x2882, 0x2892, 0x28a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x0ff2_ */ 0x0000, 0x28b1, 0x28b9, 0x28c1, 0x28c9, 0x28d1, 0x28d9, 0x28e1, 0x28e9, 0x28f1, 0x28f9, 0x2901, 0x2909, 0x2911, 0x2919, 0x2921, /* Node for range 0x0ff3_ */ 0x2929, 0x2931, 0x2939, 0x2941, 0x2949, 0x2951, 0x2959, 0x2961, 0x2969, 0x2971, 0x2979, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x1040_ */ 0x2981, 0x2989, 0x2991, 0x2999, 0x29a1, 0x29a9, 0x29b1, 0x29b9, 0x29c1, 0x29c9, 0x29d1, 0x29d9, 0x29e1, 0x29e9, 0x29f1, 0x29f9, /* Node for range 0x1041_ */ 0x2a01, 0x2a09, 0x2a11, 0x2a19, 0x2a21, 0x2a29, 0x2a31, 0x2a39, 0x2a41, 0x2a49, 0x2a51, 0x2a59, 0x2a61, 0x2a69, 0x2a71, 0x2a79, /* Node for range 0x1042_ */ 0x2a81, 0x2a89, 0x2a91, 0x2a99, 0x2aa1, 0x2aa9, 0x2ab1, 0x2ab9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x104b_ */ 0x2ac1, 0x2ac9, 0x2ad1, 0x2ad9, 0x2ae1, 0x2ae9, 0x2af1, 0x2af9, 0x2b01, 0x2b09, 0x2b11, 0x2b19, 0x2b21, 0x2b29, 0x2b31, 0x2b39, /* Node for range 0x104c_ */ 0x2b41, 0x2b49, 0x2b51, 0x2b59, 0x2b61, 0x2b69, 0x2b71, 0x2b79, 0x2b81, 0x2b89, 0x2b91, 0x2b99, 0x2ba1, 0x2ba9, 0x2bb1, 0x2bb9, /* Node for range 0x104d_ */ 0x2bc1, 0x2bc9, 0x2bd1, 0x2bd9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x10c8_ */ 0x2be1, 0x2be9, 0x2bf1, 0x2bf9, 0x2c01, 0x2c09, 0x2c11, 0x2c19, 0x2c21, 0x2c29, 0x2c31, 0x2c39, 0x2c41, 0x2c49, 0x2c51, 0x2c59, /* Node for range 0x10c9_ */ 0x2c61, 0x2c69, 0x2c71, 0x2c79, 0x2c81, 0x2c89, 0x2c91, 0x2c99, 0x2ca1, 0x2ca9, 0x2cb1, 0x2cb9, 0x2cc1, 0x2cc9, 0x2cd1, 0x2cd9, /* Node for range 0x10ca_ */ 0x2ce1, 0x2ce9, 0x2cf1, 0x2cf9, 0x2d01, 0x2d09, 0x2d11, 0x2d19, 0x2d21, 0x2d29, 0x2d31, 0x2d39, 0x2d41, 0x2d49, 0x2d51, 0x2d59, /* Node for range 0x10cb_ */ 0x2d61, 0x2d69, 0x2d71, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, /* Node for range 0x118a_ */ 0x2d79, 0x2d81, 0x2d89, 0x2d91, 0x2d99, 0x2da1, 0x2da9, 0x2db1, 0x2db9, 0x2dc1, 0x2dc9, 0x2dd1, 0x2dd9, 0x2de1, 0x2de9, 0x2df1, /* Node for range 0x118b_ */ 0x2df9, 0x2e01, 0x2e09, 0x2e11, 0x2e19, 0x2e21, 0x2e29, 0x2e31, 0x2e39, 0x2e41, 0x2e49, 0x2e51, 0x2e59, 0x2e61, 0x2e69, 0x2e71, /* Node for range 0x1e90_ */ 0x2e79, 0x2e81, 0x2e89, 0x2e91, 0x2e99, 0x2ea1, 0x2ea9, 0x2eb1, 0x2eb9, 0x2ec1, 0x2ec9, 0x2ed1, 0x2ed9, 0x2ee1, 0x2ee9, 0x2ef1, /* Node for range 0x1e91_ */ 0x2ef9, 0x2f01, 0x2f09, 0x2f11, 0x2f19, 0x2f21, 0x2f29, 0x2f31, 0x2f39, 0x2f41, 0x2f49, 0x2f51, 0x2f59, 0x2f61, 0x2f69, 0x2f71, /* Node for range 0x1e92_ */ 0x2f79, 0x2f81, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, }; static unicode_t cf_values[] = { 0x000061, 0x000062, 0x000063, 0x000064, 0x000065, 0x000066, 0x000067, 0x000068, 0x000069, 0x00006a, 0x00006b, 0x00006c, 0x00006d, 0x00006e, 0x00006f, 0x000070, 0x000071, 0x000072, 0x000073, 0x000074, 0x000075, 0x000076, 0x000077, 0x000078, 0x000079, 0x00007a, 0x0003bc, 0x0000e0, 0x0000e1, 0x0000e2, 0x0000e3, 0x0000e4, 0x0000e5, 0x0000e6, 0x0000e7, 0x0000e8, 0x0000e9, 0x0000ea, 0x0000eb, 0x0000ec, 0x0000ed, 0x0000ee, 0x0000ef, 0x0000f0, 0x0000f1, 0x0000f2, 0x0000f3, 0x0000f4, 0x0000f5, 0x0000f6, 0x0000f8, 0x0000f9, 0x0000fa, 0x0000fb, 0x0000fc, 0x0000fd, 0x0000fe, 0x000073, 0x000073, 0x000101, 0x000103, 0x000105, 0x000107, 0x000109, 0x00010b, 0x00010d, 0x00010f, 0x000111, 0x000113, 0x000115, 0x000117, 0x000119, 0x00011b, 0x00011d, 0x00011f, 0x000121, 0x000123, 0x000125, 0x000127, 0x000129, 0x00012b, 0x00012d, 0x00012f, 0x000069, 0x000307, 0x000133, 0x000135, 0x000137, 0x00013a, 0x00013c, 0x00013e, 0x000140, 0x000142, 0x000144, 0x000146, 0x000148, 0x0002bc, 0x00006e, 0x00014b, 0x00014d, 0x00014f, 0x000151, 0x000153, 0x000155, 0x000157, 0x000159, 0x00015b, 0x00015d, 0x00015f, 0x000161, 0x000163, 0x000165, 0x000167, 0x000169, 0x00016b, 0x00016d, 0x00016f, 0x000171, 0x000173, 0x000175, 0x000177, 0x0000ff, 0x00017a, 0x00017c, 0x00017e, 0x000073, 0x000253, 0x000183, 0x000185, 0x000254, 0x000188, 0x000256, 0x000257, 0x00018c, 0x0001dd, 0x000259, 0x00025b, 0x000192, 0x000260, 0x000263, 0x000269, 0x000268, 0x000199, 0x00026f, 0x000272, 0x000275, 0x0001a1, 0x0001a3, 0x0001a5, 0x000280, 0x0001a8, 0x000283, 0x0001ad, 0x000288, 0x0001b0, 0x00028a, 0x00028b, 0x0001b4, 0x0001b6, 0x000292, 0x0001b9, 0x0001bd, 0x0001c6, 0x0001c6, 0x0001c9, 0x0001c9, 0x0001cc, 0x0001cc, 0x0001ce, 0x0001d0, 0x0001d2, 0x0001d4, 0x0001d6, 0x0001d8, 0x0001da, 0x0001dc, 0x0001df, 0x0001e1, 0x0001e3, 0x0001e5, 0x0001e7, 0x0001e9, 0x0001eb, 0x0001ed, 0x0001ef, 0x00006a, 0x00030c, 0x0001f3, 0x0001f3, 0x0001f5, 0x000195, 0x0001bf, 0x0001f9, 0x0001fb, 0x0001fd, 0x0001ff, 0x000201, 0x000203, 0x000205, 0x000207, 0x000209, 0x00020b, 0x00020d, 0x00020f, 0x000211, 0x000213, 0x000215, 0x000217, 0x000219, 0x00021b, 0x00021d, 0x00021f, 0x00019e, 0x000223, 0x000225, 0x000227, 0x000229, 0x00022b, 0x00022d, 0x00022f, 0x000231, 0x000233, 0x002c65, 0x00023c, 0x00019a, 0x002c66, 0x000242, 0x000180, 0x000289, 0x00028c, 0x000247, 0x000249, 0x00024b, 0x00024d, 0x00024f, 0x0003b9, 0x000371, 0x000373, 0x000377, 0x0003f3, 0x0003ac, 0x0003ad, 0x0003ae, 0x0003af, 0x0003cc, 0x0003cd, 0x0003ce, 0x0003b9, 0x000308, 0x000301, 0x0003b1, 0x0003b2, 0x0003b3, 0x0003b4, 0x0003b5, 0x0003b6, 0x0003b7, 0x0003b8, 0x0003b9, 0x0003ba, 0x0003bb, 0x0003bc, 0x0003bd, 0x0003be, 0x0003bf, 0x0003c0, 0x0003c1, 0x0003c3, 0x0003c4, 0x0003c5, 0x0003c6, 0x0003c7, 0x0003c8, 0x0003c9, 0x0003ca, 0x0003cb, 0x0003c5, 0x000308, 0x000301, 0x0003c3, 0x0003d7, 0x0003b2, 0x0003b8, 0x0003c6, 0x0003c0, 0x0003d9, 0x0003db, 0x0003dd, 0x0003df, 0x0003e1, 0x0003e3, 0x0003e5, 0x0003e7, 0x0003e9, 0x0003eb, 0x0003ed, 0x0003ef, 0x0003ba, 0x0003c1, 0x0003b8, 0x0003b5, 0x0003f8, 0x0003f2, 0x0003fb, 0x00037b, 0x00037c, 0x00037d, 0x000450, 0x000451, 0x000452, 0x000453, 0x000454, 0x000455, 0x000456, 0x000457, 0x000458, 0x000459, 0x00045a, 0x00045b, 0x00045c, 0x00045d, 0x00045e, 0x00045f, 0x000430, 0x000431, 0x000432, 0x000433, 0x000434, 0x000435, 0x000436, 0x000437, 0x000438, 0x000439, 0x00043a, 0x00043b, 0x00043c, 0x00043d, 0x00043e, 0x00043f, 0x000440, 0x000441, 0x000442, 0x000443, 0x000444, 0x000445, 0x000446, 0x000447, 0x000448, 0x000449, 0x00044a, 0x00044b, 0x00044c, 0x00044d, 0x00044e, 0x00044f, 0x000461, 0x000463, 0x000465, 0x000467, 0x000469, 0x00046b, 0x00046d, 0x00046f, 0x000471, 0x000473, 0x000475, 0x000477, 0x000479, 0x00047b, 0x00047d, 0x00047f, 0x000481, 0x00048b, 0x00048d, 0x00048f, 0x000491, 0x000493, 0x000495, 0x000497, 0x000499, 0x00049b, 0x00049d, 0x00049f, 0x0004a1, 0x0004a3, 0x0004a5, 0x0004a7, 0x0004a9, 0x0004ab, 0x0004ad, 0x0004af, 0x0004b1, 0x0004b3, 0x0004b5, 0x0004b7, 0x0004b9, 0x0004bb, 0x0004bd, 0x0004bf, 0x0004cf, 0x0004c2, 0x0004c4, 0x0004c6, 0x0004c8, 0x0004ca, 0x0004cc, 0x0004ce, 0x0004d1, 0x0004d3, 0x0004d5, 0x0004d7, 0x0004d9, 0x0004db, 0x0004dd, 0x0004df, 0x0004e1, 0x0004e3, 0x0004e5, 0x0004e7, 0x0004e9, 0x0004eb, 0x0004ed, 0x0004ef, 0x0004f1, 0x0004f3, 0x0004f5, 0x0004f7, 0x0004f9, 0x0004fb, 0x0004fd, 0x0004ff, 0x000501, 0x000503, 0x000505, 0x000507, 0x000509, 0x00050b, 0x00050d, 0x00050f, 0x000511, 0x000513, 0x000515, 0x000517, 0x000519, 0x00051b, 0x00051d, 0x00051f, 0x000521, 0x000523, 0x000525, 0x000527, 0x000529, 0x00052b, 0x00052d, 0x00052f, 0x000561, 0x000562, 0x000563, 0x000564, 0x000565, 0x000566, 0x000567, 0x000568, 0x000569, 0x00056a, 0x00056b, 0x00056c, 0x00056d, 0x00056e, 0x00056f, 0x000570, 0x000571, 0x000572, 0x000573, 0x000574, 0x000575, 0x000576, 0x000577, 0x000578, 0x000579, 0x00057a, 0x00057b, 0x00057c, 0x00057d, 0x00057e, 0x00057f, 0x000580, 0x000581, 0x000582, 0x000583, 0x000584, 0x000585, 0x000586, 0x000565, 0x000582, 0x002d00, 0x002d01, 0x002d02, 0x002d03, 0x002d04, 0x002d05, 0x002d06, 0x002d07, 0x002d08, 0x002d09, 0x002d0a, 0x002d0b, 0x002d0c, 0x002d0d, 0x002d0e, 0x002d0f, 0x002d10, 0x002d11, 0x002d12, 0x002d13, 0x002d14, 0x002d15, 0x002d16, 0x002d17, 0x002d18, 0x002d19, 0x002d1a, 0x002d1b, 0x002d1c, 0x002d1d, 0x002d1e, 0x002d1f, 0x002d20, 0x002d21, 0x002d22, 0x002d23, 0x002d24, 0x002d25, 0x002d27, 0x002d2d, 0x0013f0, 0x0013f1, 0x0013f2, 0x0013f3, 0x0013f4, 0x0013f5, 0x000432, 0x000434, 0x00043e, 0x000441, 0x000442, 0x000442, 0x00044a, 0x000463, 0x00a64b, 0x001e01, 0x001e03, 0x001e05, 0x001e07, 0x001e09, 0x001e0b, 0x001e0d, 0x001e0f, 0x001e11, 0x001e13, 0x001e15, 0x001e17, 0x001e19, 0x001e1b, 0x001e1d, 0x001e1f, 0x001e21, 0x001e23, 0x001e25, 0x001e27, 0x001e29, 0x001e2b, 0x001e2d, 0x001e2f, 0x001e31, 0x001e33, 0x001e35, 0x001e37, 0x001e39, 0x001e3b, 0x001e3d, 0x001e3f, 0x001e41, 0x001e43, 0x001e45, 0x001e47, 0x001e49, 0x001e4b, 0x001e4d, 0x001e4f, 0x001e51, 0x001e53, 0x001e55, 0x001e57, 0x001e59, 0x001e5b, 0x001e5d, 0x001e5f, 0x001e61, 0x001e63, 0x001e65, 0x001e67, 0x001e69, 0x001e6b, 0x001e6d, 0x001e6f, 0x001e71, 0x001e73, 0x001e75, 0x001e77, 0x001e79, 0x001e7b, 0x001e7d, 0x001e7f, 0x001e81, 0x001e83, 0x001e85, 0x001e87, 0x001e89, 0x001e8b, 0x001e8d, 0x001e8f, 0x001e91, 0x001e93, 0x001e95, 0x000068, 0x000331, 0x000074, 0x000308, 0x000077, 0x00030a, 0x000079, 0x00030a, 0x000061, 0x0002be, 0x001e61, 0x000073, 0x000073, 0x001ea1, 0x001ea3, 0x001ea5, 0x001ea7, 0x001ea9, 0x001eab, 0x001ead, 0x001eaf, 0x001eb1, 0x001eb3, 0x001eb5, 0x001eb7, 0x001eb9, 0x001ebb, 0x001ebd, 0x001ebf, 0x001ec1, 0x001ec3, 0x001ec5, 0x001ec7, 0x001ec9, 0x001ecb, 0x001ecd, 0x001ecf, 0x001ed1, 0x001ed3, 0x001ed5, 0x001ed7, 0x001ed9, 0x001edb, 0x001edd, 0x001edf, 0x001ee1, 0x001ee3, 0x001ee5, 0x001ee7, 0x001ee9, 0x001eeb, 0x001eed, 0x001eef, 0x001ef1, 0x001ef3, 0x001ef5, 0x001ef7, 0x001ef9, 0x001efb, 0x001efd, 0x001eff, 0x001f00, 0x001f01, 0x001f02, 0x001f03, 0x001f04, 0x001f05, 0x001f06, 0x001f07, 0x001f10, 0x001f11, 0x001f12, 0x001f13, 0x001f14, 0x001f15, 0x001f20, 0x001f21, 0x001f22, 0x001f23, 0x001f24, 0x001f25, 0x001f26, 0x001f27, 0x001f30, 0x001f31, 0x001f32, 0x001f33, 0x001f34, 0x001f35, 0x001f36, 0x001f37, 0x001f40, 0x001f41, 0x001f42, 0x001f43, 0x001f44, 0x001f45, 0x0003c5, 0x000313, 0x0003c5, 0x000313, 0x000300, 0x0003c5, 0x000313, 0x000301, 0x0003c5, 0x000313, 0x000342, 0x001f51, 0x001f53, 0x001f55, 0x001f57, 0x001f60, 0x001f61, 0x001f62, 0x001f63, 0x001f64, 0x001f65, 0x001f66, 0x001f67, 0x001f00, 0x0003b9, 0x001f01, 0x0003b9, 0x001f02, 0x0003b9, 0x001f03, 0x0003b9, 0x001f04, 0x0003b9, 0x001f05, 0x0003b9, 0x001f06, 0x0003b9, 0x001f07, 0x0003b9, 0x001f00, 0x0003b9, 0x001f01, 0x0003b9, 0x001f02, 0x0003b9, 0x001f03, 0x0003b9, 0x001f04, 0x0003b9, 0x001f05, 0x0003b9, 0x001f06, 0x0003b9, 0x001f07, 0x0003b9, 0x001f20, 0x0003b9, 0x001f21, 0x0003b9, 0x001f22, 0x0003b9, 0x001f23, 0x0003b9, 0x001f24, 0x0003b9, 0x001f25, 0x0003b9, 0x001f26, 0x0003b9, 0x001f27, 0x0003b9, 0x001f20, 0x0003b9, 0x001f21, 0x0003b9, 0x001f22, 0x0003b9, 0x001f23, 0x0003b9, 0x001f24, 0x0003b9, 0x001f25, 0x0003b9, 0x001f26, 0x0003b9, 0x001f27, 0x0003b9, 0x001f60, 0x0003b9, 0x001f61, 0x0003b9, 0x001f62, 0x0003b9, 0x001f63, 0x0003b9, 0x001f64, 0x0003b9, 0x001f65, 0x0003b9, 0x001f66, 0x0003b9, 0x001f67, 0x0003b9, 0x001f60, 0x0003b9, 0x001f61, 0x0003b9, 0x001f62, 0x0003b9, 0x001f63, 0x0003b9, 0x001f64, 0x0003b9, 0x001f65, 0x0003b9, 0x001f66, 0x0003b9, 0x001f67, 0x0003b9, 0x001f70, 0x0003b9, 0x0003b1, 0x0003b9, 0x0003ac, 0x0003b9, 0x0003b1, 0x000342, 0x0003b1, 0x000342, 0x0003b9, 0x001fb0, 0x001fb1, 0x001f70, 0x001f71, 0x0003b1, 0x0003b9, 0x0003b9, 0x001f74, 0x0003b9, 0x0003b7, 0x0003b9, 0x0003ae, 0x0003b9, 0x0003b7, 0x000342, 0x0003b7, 0x000342, 0x0003b9, 0x001f72, 0x001f73, 0x001f74, 0x001f75, 0x0003b7, 0x0003b9, 0x0003b9, 0x000308, 0x000300, 0x0003b9, 0x000308, 0x000301, 0x0003b9, 0x000342, 0x0003b9, 0x000308, 0x000342, 0x001fd0, 0x001fd1, 0x001f76, 0x001f77, 0x0003c5, 0x000308, 0x000300, 0x0003c5, 0x000308, 0x000301, 0x0003c1, 0x000313, 0x0003c5, 0x000342, 0x0003c5, 0x000308, 0x000342, 0x001fe0, 0x001fe1, 0x001f7a, 0x001f7b, 0x001fe5, 0x001f7c, 0x0003b9, 0x0003c9, 0x0003b9, 0x0003ce, 0x0003b9, 0x0003c9, 0x000342, 0x0003c9, 0x000342, 0x0003b9, 0x001f78, 0x001f79, 0x001f7c, 0x001f7d, 0x0003c9, 0x0003b9, 0x0003c9, 0x00006b, 0x0000e5, 0x00214e, 0x002170, 0x002171, 0x002172, 0x002173, 0x002174, 0x002175, 0x002176, 0x002177, 0x002178, 0x002179, 0x00217a, 0x00217b, 0x00217c, 0x00217d, 0x00217e, 0x00217f, 0x002184, 0x0024d0, 0x0024d1, 0x0024d2, 0x0024d3, 0x0024d4, 0x0024d5, 0x0024d6, 0x0024d7, 0x0024d8, 0x0024d9, 0x0024da, 0x0024db, 0x0024dc, 0x0024dd, 0x0024de, 0x0024df, 0x0024e0, 0x0024e1, 0x0024e2, 0x0024e3, 0x0024e4, 0x0024e5, 0x0024e6, 0x0024e7, 0x0024e8, 0x0024e9, 0x002c30, 0x002c31, 0x002c32, 0x002c33, 0x002c34, 0x002c35, 0x002c36, 0x002c37, 0x002c38, 0x002c39, 0x002c3a, 0x002c3b, 0x002c3c, 0x002c3d, 0x002c3e, 0x002c3f, 0x002c40, 0x002c41, 0x002c42, 0x002c43, 0x002c44, 0x002c45, 0x002c46, 0x002c47, 0x002c48, 0x002c49, 0x002c4a, 0x002c4b, 0x002c4c, 0x002c4d, 0x002c4e, 0x002c4f, 0x002c50, 0x002c51, 0x002c52, 0x002c53, 0x002c54, 0x002c55, 0x002c56, 0x002c57, 0x002c58, 0x002c59, 0x002c5a, 0x002c5b, 0x002c5c, 0x002c5d, 0x002c5e, 0x002c61, 0x00026b, 0x001d7d, 0x00027d, 0x002c68, 0x002c6a, 0x002c6c, 0x000251, 0x000271, 0x000250, 0x000252, 0x002c73, 0x002c76, 0x00023f, 0x000240, 0x002c81, 0x002c83, 0x002c85, 0x002c87, 0x002c89, 0x002c8b, 0x002c8d, 0x002c8f, 0x002c91, 0x002c93, 0x002c95, 0x002c97, 0x002c99, 0x002c9b, 0x002c9d, 0x002c9f, 0x002ca1, 0x002ca3, 0x002ca5, 0x002ca7, 0x002ca9, 0x002cab, 0x002cad, 0x002caf, 0x002cb1, 0x002cb3, 0x002cb5, 0x002cb7, 0x002cb9, 0x002cbb, 0x002cbd, 0x002cbf, 0x002cc1, 0x002cc3, 0x002cc5, 0x002cc7, 0x002cc9, 0x002ccb, 0x002ccd, 0x002ccf, 0x002cd1, 0x002cd3, 0x002cd5, 0x002cd7, 0x002cd9, 0x002cdb, 0x002cdd, 0x002cdf, 0x002ce1, 0x002ce3, 0x002cec, 0x002cee, 0x002cf3, 0x00a641, 0x00a643, 0x00a645, 0x00a647, 0x00a649, 0x00a64b, 0x00a64d, 0x00a64f, 0x00a651, 0x00a653, 0x00a655, 0x00a657, 0x00a659, 0x00a65b, 0x00a65d, 0x00a65f, 0x00a661, 0x00a663, 0x00a665, 0x00a667, 0x00a669, 0x00a66b, 0x00a66d, 0x00a681, 0x00a683, 0x00a685, 0x00a687, 0x00a689, 0x00a68b, 0x00a68d, 0x00a68f, 0x00a691, 0x00a693, 0x00a695, 0x00a697, 0x00a699, 0x00a69b, 0x00a723, 0x00a725, 0x00a727, 0x00a729, 0x00a72b, 0x00a72d, 0x00a72f, 0x00a733, 0x00a735, 0x00a737, 0x00a739, 0x00a73b, 0x00a73d, 0x00a73f, 0x00a741, 0x00a743, 0x00a745, 0x00a747, 0x00a749, 0x00a74b, 0x00a74d, 0x00a74f, 0x00a751, 0x00a753, 0x00a755, 0x00a757, 0x00a759, 0x00a75b, 0x00a75d, 0x00a75f, 0x00a761, 0x00a763, 0x00a765, 0x00a767, 0x00a769, 0x00a76b, 0x00a76d, 0x00a76f, 0x00a77a, 0x00a77c, 0x001d79, 0x00a77f, 0x00a781, 0x00a783, 0x00a785, 0x00a787, 0x00a78c, 0x000265, 0x00a791, 0x00a793, 0x00a797, 0x00a799, 0x00a79b, 0x00a79d, 0x00a79f, 0x00a7a1, 0x00a7a3, 0x00a7a5, 0x00a7a7, 0x00a7a9, 0x000266, 0x00025c, 0x000261, 0x00026c, 0x00026a, 0x00029e, 0x000287, 0x00029d, 0x00ab53, 0x00a7b5, 0x00a7b7, 0x0013a0, 0x0013a1, 0x0013a2, 0x0013a3, 0x0013a4, 0x0013a5, 0x0013a6, 0x0013a7, 0x0013a8, 0x0013a9, 0x0013aa, 0x0013ab, 0x0013ac, 0x0013ad, 0x0013ae, 0x0013af, 0x0013b0, 0x0013b1, 0x0013b2, 0x0013b3, 0x0013b4, 0x0013b5, 0x0013b6, 0x0013b7, 0x0013b8, 0x0013b9, 0x0013ba, 0x0013bb, 0x0013bc, 0x0013bd, 0x0013be, 0x0013bf, 0x0013c0, 0x0013c1, 0x0013c2, 0x0013c3, 0x0013c4, 0x0013c5, 0x0013c6, 0x0013c7, 0x0013c8, 0x0013c9, 0x0013ca, 0x0013cb, 0x0013cc, 0x0013cd, 0x0013ce, 0x0013cf, 0x0013d0, 0x0013d1, 0x0013d2, 0x0013d3, 0x0013d4, 0x0013d5, 0x0013d6, 0x0013d7, 0x0013d8, 0x0013d9, 0x0013da, 0x0013db, 0x0013dc, 0x0013dd, 0x0013de, 0x0013df, 0x0013e0, 0x0013e1, 0x0013e2, 0x0013e3, 0x0013e4, 0x0013e5, 0x0013e6, 0x0013e7, 0x0013e8, 0x0013e9, 0x0013ea, 0x0013eb, 0x0013ec, 0x0013ed, 0x0013ee, 0x0013ef, 0x000066, 0x000066, 0x000066, 0x000069, 0x000066, 0x00006c, 0x000066, 0x000066, 0x000069, 0x000066, 0x000066, 0x00006c, 0x000073, 0x000074, 0x000073, 0x000074, 0x000574, 0x000576, 0x000574, 0x000565, 0x000574, 0x00056b, 0x00057e, 0x000576, 0x000574, 0x00056d, 0x00ff41, 0x00ff42, 0x00ff43, 0x00ff44, 0x00ff45, 0x00ff46, 0x00ff47, 0x00ff48, 0x00ff49, 0x00ff4a, 0x00ff4b, 0x00ff4c, 0x00ff4d, 0x00ff4e, 0x00ff4f, 0x00ff50, 0x00ff51, 0x00ff52, 0x00ff53, 0x00ff54, 0x00ff55, 0x00ff56, 0x00ff57, 0x00ff58, 0x00ff59, 0x00ff5a, 0x010428, 0x010429, 0x01042a, 0x01042b, 0x01042c, 0x01042d, 0x01042e, 0x01042f, 0x010430, 0x010431, 0x010432, 0x010433, 0x010434, 0x010435, 0x010436, 0x010437, 0x010438, 0x010439, 0x01043a, 0x01043b, 0x01043c, 0x01043d, 0x01043e, 0x01043f, 0x010440, 0x010441, 0x010442, 0x010443, 0x010444, 0x010445, 0x010446, 0x010447, 0x010448, 0x010449, 0x01044a, 0x01044b, 0x01044c, 0x01044d, 0x01044e, 0x01044f, 0x0104d8, 0x0104d9, 0x0104da, 0x0104db, 0x0104dc, 0x0104dd, 0x0104de, 0x0104df, 0x0104e0, 0x0104e1, 0x0104e2, 0x0104e3, 0x0104e4, 0x0104e5, 0x0104e6, 0x0104e7, 0x0104e8, 0x0104e9, 0x0104ea, 0x0104eb, 0x0104ec, 0x0104ed, 0x0104ee, 0x0104ef, 0x0104f0, 0x0104f1, 0x0104f2, 0x0104f3, 0x0104f4, 0x0104f5, 0x0104f6, 0x0104f7, 0x0104f8, 0x0104f9, 0x0104fa, 0x0104fb, 0x010cc0, 0x010cc1, 0x010cc2, 0x010cc3, 0x010cc4, 0x010cc5, 0x010cc6, 0x010cc7, 0x010cc8, 0x010cc9, 0x010cca, 0x010ccb, 0x010ccc, 0x010ccd, 0x010cce, 0x010ccf, 0x010cd0, 0x010cd1, 0x010cd2, 0x010cd3, 0x010cd4, 0x010cd5, 0x010cd6, 0x010cd7, 0x010cd8, 0x010cd9, 0x010cda, 0x010cdb, 0x010cdc, 0x010cdd, 0x010cde, 0x010cdf, 0x010ce0, 0x010ce1, 0x010ce2, 0x010ce3, 0x010ce4, 0x010ce5, 0x010ce6, 0x010ce7, 0x010ce8, 0x010ce9, 0x010cea, 0x010ceb, 0x010cec, 0x010ced, 0x010cee, 0x010cef, 0x010cf0, 0x010cf1, 0x010cf2, 0x0118c0, 0x0118c1, 0x0118c2, 0x0118c3, 0x0118c4, 0x0118c5, 0x0118c6, 0x0118c7, 0x0118c8, 0x0118c9, 0x0118ca, 0x0118cb, 0x0118cc, 0x0118cd, 0x0118ce, 0x0118cf, 0x0118d0, 0x0118d1, 0x0118d2, 0x0118d3, 0x0118d4, 0x0118d5, 0x0118d6, 0x0118d7, 0x0118d8, 0x0118d9, 0x0118da, 0x0118db, 0x0118dc, 0x0118dd, 0x0118de, 0x0118df, 0x01e922, 0x01e923, 0x01e924, 0x01e925, 0x01e926, 0x01e927, 0x01e928, 0x01e929, 0x01e92a, 0x01e92b, 0x01e92c, 0x01e92d, 0x01e92e, 0x01e92f, 0x01e930, 0x01e931, 0x01e932, 0x01e933, 0x01e934, 0x01e935, 0x01e936, 0x01e937, 0x01e938, 0x01e939, 0x01e93a, 0x01e93b, 0x01e93c, 0x01e93d, 0x01e93e, 0x01e93f, 0x01e940, 0x01e941, 0x01e942, 0x01e943, }; static u8 ccc_trie[] = { /* Node for range 0x_____ */ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0____ */ 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x08, /* Node for range 0x1____ */ 0x09, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0d, 0x0e, 0x00, /* Node for range 0x00___ */ 0x00, 0x00, 0x00, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* Node for range 0x01___ */ 0x1c, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x00, 0x00, /* Node for range 0x02___ */ 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x27, 0x00, 0x00, /* Node for range 0x03___ */ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a___ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x2a, 0x2b, 0x2c, 0x2d, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0f___ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x2f, 0x00, /* Node for range 0x10___ */ 0x00, 0x30, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x11___ */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, /* Node for range 0x16___ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1b___ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, /* Node for range 0x1d___ */ 0x00, 0x40, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1e___ */ 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x003__ */ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x004__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x005__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x4e, 0x4f, 0x50, 0x00, 0x00, 0x00, /* Node for range 0x006__ */ 0x00, 0x51, 0x00, 0x00, 0x52, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x56, 0x00, /* Node for range 0x007__ */ 0x00, 0x57, 0x00, 0x58, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x5b, /* Node for range 0x008__ */ 0x00, 0x5c, 0x5d, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x60, 0x61, /* Node for range 0x009__ */ 0x00, 0x00, 0x00, 0x62, 0x63, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x66, 0x00, 0x00, 0x00, /* Node for range 0x00a__ */ 0x00, 0x00, 0x00, 0x67, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x6a, 0x00, 0x00, 0x00, /* Node for range 0x00b__ */ 0x00, 0x00, 0x00, 0x6b, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, /* Node for range 0x00c__ */ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x71, 0x00, 0x00, 0x00, /* Node for range 0x00d__ */ 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, /* Node for range 0x00e__ */ 0x00, 0x00, 0x00, 0x74, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x77, 0x00, 0x00, 0x00, /* Node for range 0x00f__ */ 0x00, 0x78, 0x00, 0x79, 0x00, 0x00, 0x00, 0x7a, 0x7b, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, /* Node for range 0x010__ */ 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x013__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x017__ */ 0x00, 0x80, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, /* Node for range 0x018__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x019__ */ 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01a__ */ 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x86, 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01b__ */ 0x00, 0x00, 0x00, 0x89, 0x8a, 0x00, 0x8b, 0x8c, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x8e, 0x8f, /* Node for range 0x01c__ */ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x92, 0x93, /* Node for range 0x01d__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x95, 0x96, 0x97, /* Node for range 0x020__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x99, 0x9a, /* Node for range 0x02c__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x9c, /* Node for range 0x02d__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x9f, /* Node for range 0x030__ */ 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a6__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xa3, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, /* Node for range 0x0a8__ */ 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x00, 0xa8, 0xa9, /* Node for range 0x0a9__ */ 0x00, 0x00, 0xaa, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0xad, 0x00, 0x00, 0x00, /* Node for range 0x0aa__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xaf, 0x00, 0x00, 0xb0, /* Node for range 0x0ab__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, /* Node for range 0x0fb__ */ 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0fe__ */ 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x101__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, /* Node for range 0x102__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, /* Node for range 0x103__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x10a__ */ 0xb7, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, /* Node for range 0x110__ */ 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x111__ */ 0xbd, 0x00, 0x00, 0xbe, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* Node for range 0x112__ */ 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, /* Node for range 0x113__ */ 0x00, 0x00, 0x00, 0xc3, 0xc4, 0x00, 0xc5, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x114__ */ 0x00, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, /* Node for range 0x115__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0xca, 0x00, 0x00, 0x00, /* Node for range 0x116__ */ 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x117__ */ 0x00, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x11c__ */ 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x16a__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, /* Node for range 0x16b__ */ 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1bc__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1d1__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0xd3, 0xd4, 0x00, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1d2__ */ 0x00, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1e0__ */ 0xd7, 0xd8, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1e8__ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, /* Node for range 0x1e9__ */ 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0030_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x0031_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe8, 0xdc, 0xdc, 0xdc, 0xdc, 0xe8, 0xd8, 0xdc, 0xdc, 0xdc, 0xdc, /* Node for range 0x0032_ */ 0xdc, 0xca, 0xca, 0xdc, 0xdc, 0xdc, 0xdc, 0xca, 0xca, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, /* Node for range 0x0033_ */ 0xdc, 0xdc, 0xdc, 0xdc, 0x01, 0x01, 0x01, 0x01, 0x01, 0xdc, 0xdc, 0xdc, 0xdc, 0xe6, 0xe6, 0xe6, /* Node for range 0x0034_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xf0, 0xe6, 0xdc, 0xdc, 0xdc, 0xe6, 0xe6, 0xe6, 0xdc, 0xdc, 0x00, /* Node for range 0x0035_ */ 0xe6, 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, 0xdc, 0xe6, 0xe8, 0xdc, 0xdc, 0xe6, 0xe9, 0xea, 0xea, 0xe9, /* Node for range 0x0036_ */ 0xea, 0xea, 0xe9, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x0048_ */ 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0059_ */ 0x00, 0xdc, 0xe6, 0xe6, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xe6, 0xde, 0xdc, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x005a_ */ 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xde, 0xe4, 0xe6, /* Node for range 0x005b_ */ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x00, 0x17, /* Node for range 0x005c_ */ 0x00, 0x18, 0x19, 0x00, 0xe6, 0xdc, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0061_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x1e, 0x1f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0064_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* Node for range 0x0065_ */ 0x20, 0x21, 0x22, 0xe6, 0xe6, 0xdc, 0xdc, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xdc, /* Node for range 0x0067_ */ 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x006d_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0xe6, /* Node for range 0x006e_ */ 0xe6, 0xe6, 0xe6, 0xdc, 0xe6, 0x00, 0x00, 0xe6, 0xe6, 0x00, 0xdc, 0xe6, 0xe6, 0xdc, 0x00, 0x00, /* Node for range 0x0071_ */ 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0073_ */ 0xe6, 0xdc, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, 0xe6, 0xdc, 0xdc, 0xe6, 0xdc, 0xe6, /* Node for range 0x0074_ */ 0xe6, 0xe6, 0xdc, 0xe6, 0xdc, 0xe6, 0xdc, 0xe6, 0xdc, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x007e_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x007f_ */ 0xe6, 0xe6, 0xdc, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0081_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x0082_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0xe6, 0xe6, 0xe6, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, /* Node for range 0x0085_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xdc, 0xdc, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x008d_ */ 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x008e_ */ 0xe6, 0xe6, 0x00, 0xdc, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, /* Node for range 0x008f_ */ 0x1b, 0x1c, 0x1d, 0xe6, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xdc, 0xdc, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x0093_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x0094_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x0095_ */ 0x00, 0xe6, 0xdc, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x009b_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x009c_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00a3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x00a4_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00ab_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x00ac_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00b3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x00b4_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00bc_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00c4_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00c5_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00cb_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x00cc_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00d4_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x00dc_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00e3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x67, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00e4_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x6b, 0x6b, 0x6b, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00eb_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00ec_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x7a, 0x7a, 0x7a, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00f1_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00f3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00f7_ */ 0x00, 0x81, 0x82, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0x00, 0x00, /* Node for range 0x00f8_ */ 0x82, 0x00, 0xe6, 0xe6, 0x09, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x00fc_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0103_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0108_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, /* Node for range 0x0135_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, /* Node for range 0x0171_ */ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0173_ */ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x017d_ */ 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, /* Node for range 0x018a_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0193_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xe6, 0xdc, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01a1_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01a6_ */ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01a7_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0xdc, /* Node for range 0x01ab_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xe6, 0xe6, 0xdc, 0x00, 0x00, /* Node for range 0x01b3_ */ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01b4_ */ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01b6_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xdc, 0xe6, 0xe6, 0xe6, /* Node for range 0x01b7_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01ba_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01be_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01bf_ */ 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01c3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01cd_ */ 0xe6, 0xe6, 0xe6, 0x00, 0x01, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, 0xdc, /* Node for range 0x01ce_ */ 0xe6, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, /* Node for range 0x01cf_ */ 0x00, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x01dc_ */ 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xdc, 0xe6, 0xe6, 0xea, 0xd6, 0xdc, /* Node for range 0x01dd_ */ 0xca, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x01de_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x01df_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe9, 0xdc, 0xe6, 0xdc, /* Node for range 0x020d_ */ 0xe6, 0xe6, 0x01, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0x01, 0x01, 0x01, 0xe6, 0xe6, 0x00, 0x00, 0x00, /* Node for range 0x020e_ */ 0x00, 0xe6, 0x00, 0x00, 0x00, 0x01, 0x01, 0xe6, 0xdc, 0xe6, 0x01, 0x01, 0xdc, 0xdc, 0xdc, 0xdc, /* Node for range 0x020f_ */ 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x02ce_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, /* Node for range 0x02cf_ */ 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x02d7_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, /* Node for range 0x02de_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x02df_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x0302_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0xe4, 0xe8, 0xde, 0xe0, 0xe0, /* Node for range 0x0309_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a66_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, /* Node for range 0x0a67_ */ 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, /* Node for range 0x0a69_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, /* Node for range 0x0a6f_ */ 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a80_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a8c_ */ 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a8e_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x0a8f_ */ 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a92_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xdc, 0xdc, 0x00, 0x00, /* Node for range 0x0a95_ */ 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a9b_ */ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0a9c_ */ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0aab_ */ 0xe6, 0x00, 0xe6, 0xe6, 0xdc, 0x00, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, /* Node for range 0x0aac_ */ 0x00, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0aaf_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x0abe_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x0fb1_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, /* Node for range 0x0fe2_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xe6, 0xe6, /* Node for range 0x101f_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, /* Node for range 0x102e_ */ 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1037_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x10a0_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xe6, /* Node for range 0x10a3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x01, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x09, /* Node for range 0x10ae_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1104_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1107_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, /* Node for range 0x110b_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1110_ */ 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1113_ */ 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1117_ */ 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x111c_ */ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1123_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x112e_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1133_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, /* Node for range 0x1134_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, /* Node for range 0x1136_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, /* Node for range 0x1137_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1144_ */ 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x114c_ */ 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x115b_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, /* Node for range 0x115c_ */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1163_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, /* Node for range 0x116b_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1172_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x11c3_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, /* Node for range 0x16af_ */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x16b3_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1bc9_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, /* Node for range 0x1d16_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xd8, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0xe2, 0xd8, 0xd8, /* Node for range 0x1d17_ */ 0xd8, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, /* Node for range 0x1d18_ */ 0xdc, 0xdc, 0xdc, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xdc, 0xdc, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1d1a_ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, /* Node for range 0x1d24_ */ 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1e00_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x1e01_ */ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, /* Node for range 0x1e02_ */ 0xe6, 0xe6, 0x00, 0xe6, 0xe6, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1e8d_ */ 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Node for range 0x1e94_ */ 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, }; apfsprogs-0.2.0/lib/zlib_inflate/000077500000000000000000000000001471277137200167405ustar00rootroot00000000000000apfsprogs-0.2.0/lib/zlib_inflate/inffast.c000066400000000000000000000314711471277137200205440ustar00rootroot00000000000000/* inffast.c -- fast decoding * Copyright (C) 1995-2004 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "apfs/zlib_inflate/zutil.h" #include "apfs/zlib_inflate/inftrees.h" #include "apfs/zlib_inflate/inflate.h" #include "apfs/zlib_inflate/inffast.h" #ifndef ASMINF /* Allow machine dependent optimization for post-increment or pre-increment. Based on testing to date, Pre-increment preferred for: - PowerPC G3 (Adler) - MIPS R5000 (Randers-Pehrson) Post-increment preferred for: - none No measurable difference: - Pentium III (Anderson) - M68060 (Nikl) */ union uu { unsigned short us; unsigned char b[2]; }; /* Endian independed version */ static inline unsigned short get_unaligned16(const unsigned short *p) { union uu mm; unsigned char *b = (unsigned char *)p; mm.b[0] = b[0]; mm.b[1] = b[1]; return mm.us; } #ifdef POSTINC # define OFF 0 # define PUP(a) *(a)++ # define UP_UNALIGNED(a) get_unaligned16((a)++) #else # define OFF 1 # define PUP(a) *++(a) # define UP_UNALIGNED(a) get_unaligned16(++(a)) #endif /* Decode literal, length, and distance codes and write out the resulting literal and match bytes until either not enough input or output is available, an end-of-block is encountered, or a data error is encountered. When large enough input and output buffers are supplied to inflate(), for example, a 16K input buffer and a 64K output buffer, more than 95% of the inflate execution time is spent in this routine. Entry assumptions: state->mode == LEN strm->avail_in >= 6 strm->avail_out >= 258 start >= strm->avail_out state->bits < 8 On return, state->mode is one of: LEN -- ran out of enough output space or enough available input TYPE -- reached end of block code, inflate() to interpret next block BAD -- error in block data Notes: - The maximum input bits used by a length/distance pair is 15 bits for the length code, 5 bits for the length extra, 15 bits for the distance code, and 13 bits for the distance extra. This totals 48 bits, or six bytes. Therefore if strm->avail_in >= 6, then there is enough input to avoid checking for available input while decoding. - The maximum bytes that a single length/distance pair can output is 258 bytes, which is the maximum length that can be coded. inflate_fast() requires strm->avail_out >= 258 for each loop to avoid checking for output space. - @start: inflate()'s starting value for strm->avail_out */ void inflate_fast(z_streamp strm, unsigned start) { struct inflate_state *state; const unsigned char *in; /* local strm->next_in */ const unsigned char *last; /* while in < last, enough input available */ unsigned char *out; /* local strm->next_out */ unsigned char *beg; /* inflate()'s initial strm->next_out */ unsigned char *end; /* while out < end, enough space available */ #ifdef INFLATE_STRICT unsigned dmax; /* maximum distance from zlib header */ #endif unsigned wsize; /* window size or zero if not using window */ unsigned whave; /* valid bytes in the window */ unsigned write; /* window write index */ unsigned char *window; /* allocated sliding window, if wsize != 0 */ unsigned long hold; /* local strm->hold */ unsigned bits; /* local strm->bits */ code const *lcode; /* local strm->lencode */ code const *dcode; /* local strm->distcode */ unsigned lmask; /* mask for first level of length codes */ unsigned dmask; /* mask for first level of distance codes */ code this; /* retrieved table entry */ unsigned op; /* code bits, operation, extra bits, or */ /* window position, window bytes to copy */ unsigned len; /* match length, unused bytes */ unsigned dist; /* match distance */ unsigned char *from; /* where to copy match from */ /* copy state to local variables */ state = (struct inflate_state *)strm->state; in = strm->next_in - OFF; last = in + (strm->avail_in - 5); out = strm->next_out - OFF; beg = out - (start - strm->avail_out); end = out + (strm->avail_out - 257); #ifdef INFLATE_STRICT dmax = state->dmax; #endif wsize = state->wsize; whave = state->whave; write = state->write; window = state->window; hold = state->hold; bits = state->bits; lcode = state->lencode; dcode = state->distcode; lmask = (1U << state->lenbits) - 1; dmask = (1U << state->distbits) - 1; /* decode literals and length/distances until end-of-block or not enough input data or output space */ do { if (bits < 15) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; hold += (unsigned long)(PUP(in)) << bits; bits += 8; } this = lcode[hold & lmask]; dolen: op = (unsigned)(this.bits); hold >>= op; bits -= op; op = (unsigned)(this.op); if (op == 0) { /* literal */ PUP(out) = (unsigned char)(this.val); } else if (op & 16) { /* length base */ len = (unsigned)(this.val); op &= 15; /* number of extra bits */ if (op) { if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; } len += (unsigned)hold & ((1U << op) - 1); hold >>= op; bits -= op; } if (bits < 15) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; hold += (unsigned long)(PUP(in)) << bits; bits += 8; } this = dcode[hold & dmask]; dodist: op = (unsigned)(this.bits); hold >>= op; bits -= op; op = (unsigned)(this.op); if (op & 16) { /* distance base */ dist = (unsigned)(this.val); op &= 15; /* number of extra bits */ if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; if (bits < op) { hold += (unsigned long)(PUP(in)) << bits; bits += 8; } } dist += (unsigned)hold & ((1U << op) - 1); #ifdef INFLATE_STRICT if (dist > dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif hold >>= op; bits -= op; op = (unsigned)(out - beg); /* max distance in output */ if (dist > op) { /* see if copy from window */ op = dist - op; /* distance back in window */ if (op > whave) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } from = window - OFF; if (write == 0) { /* very common case */ from += wsize - op; if (op < len) { /* some from window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } else if (write < op) { /* wrap around window */ from += wsize + write - op; op -= write; if (op < len) { /* some from end of window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = window - OFF; if (write < len) { /* some from start of window */ op = write; len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } } else { /* contiguous in window */ from += write - op; if (op < len) { /* some from window */ len -= op; do { PUP(out) = PUP(from); } while (--op); from = out - dist; /* rest from output */ } } while (len > 2) { PUP(out) = PUP(from); PUP(out) = PUP(from); PUP(out) = PUP(from); len -= 3; } if (len) { PUP(out) = PUP(from); if (len > 1) PUP(out) = PUP(from); } } else { unsigned short *sout; unsigned long loops; from = out - dist; /* copy direct from output */ /* minimum length is three */ /* Align out addr */ if (!((long)(out - 1 + OFF) & 1)) { PUP(out) = PUP(from); len--; } sout = (unsigned short *)(out - OFF); if (dist > 2) { unsigned short *sfrom; sfrom = (unsigned short *)(from - OFF); loops = len >> 1; do #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS PUP(sout) = PUP(sfrom); #else PUP(sout) = UP_UNALIGNED(sfrom); #endif while (--loops); out = (unsigned char *)sout + OFF; from = (unsigned char *)sfrom + OFF; } else { /* dist == 1 or dist == 2 */ unsigned short pat16; pat16 = *(sout-1+OFF); if (dist == 1) { union uu mm; /* copy one char pattern to both bytes */ mm.us = pat16; mm.b[0] = mm.b[1]; pat16 = mm.us; } loops = len >> 1; do PUP(sout) = pat16; while (--loops); out = (unsigned char *)sout + OFF; } if (len & 1) PUP(out) = PUP(from); } } else if ((op & 64) == 0) { /* 2nd level distance code */ this = dcode[this.val + (hold & ((1U << op) - 1))]; goto dodist; } else { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } } else if ((op & 64) == 0) { /* 2nd level length code */ this = lcode[this.val + (hold & ((1U << op) - 1))]; goto dolen; } else if (op & 32) { /* end-of-block */ state->mode = TYPE; break; } else { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } } while (in < last && out < end); /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ len = bits >> 3; in -= len; bits -= len << 3; hold &= (1U << bits) - 1; /* update state and return */ strm->next_in = in + OFF; strm->next_out = out + OFF; strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_out = (unsigned)(out < end ? 257 + (end - out) : 257 - (out - end)); state->hold = hold; state->bits = bits; return; } /* inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - Using bit fields for code structure - Different op definition to avoid & for extra bits (do & for table bits) - Three separate decoding do-loops for direct, window, and write == 0 - Special case for distance > 1 copies to do overlapped load and store copy - Explicit branch predictions (based on measured branch probabilities) - Deferring match copy and interspersed it with decoding subsequent codes - Swapping literal/length else - Swapping window/direct else - Larger unrolled copy loops (three is about right) - Moving len -= 3 statement into middle of loop */ #endif /* !ASMINF */ apfsprogs-0.2.0/lib/zlib_inflate/inflate.c000066400000000000000000000661061471277137200205370ustar00rootroot00000000000000/* inflate.c -- zlib decompression * Copyright (C) 1995-2005 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Based on zlib 1.2.3 but modified for the Linux Kernel by * Richard Purdie * * Changes mainly for static instead of dynamic memory allocation * */ #include #include "apfs/zlib_inflate/zutil.h" #include "apfs/zlib_inflate/inftrees.h" #include "apfs/zlib_inflate/inflate.h" #include "apfs/zlib_inflate/inffast.h" #include "apfs/zlib_inflate/infutil.h" int zlib_inflate_workspacesize(void) { return sizeof(struct inflate_workspace); } int zlib_inflateReset(z_streamp strm) { struct inflate_state *state; if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; state = (struct inflate_state *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = NULL; strm->adler = 1; /* to support ill-conceived Java test suite */ state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; /* Initialise Window */ state->wsize = 1U << state->wbits; state->write = 0; state->whave = 0; return Z_OK; } int zlib_inflateInit2(z_streamp strm, int windowBits) { struct inflate_state *state; if (strm == NULL) return Z_STREAM_ERROR; strm->msg = NULL; /* in case we return an error */ state = &WS(strm)->inflate_state; strm->state = (struct internal_state *)state; if (windowBits < 0) { state->wrap = 0; windowBits = -windowBits; } else { state->wrap = (windowBits >> 4) + 1; } if (windowBits < 8 || windowBits > 15) { return Z_STREAM_ERROR; } state->wbits = (unsigned)windowBits; state->window = &WS(strm)->working_window[0]; return zlib_inflateReset(strm); } /* Return state with length and distance decoding tables and index sizes set to fixed code decoding. This returns fixed tables from inffixed.h. */ static void zlib_fixedtables(struct inflate_state *state) { # include "apfs/zlib_inflate/inffixed.h" state->lencode = lenfix; state->lenbits = 9; state->distcode = distfix; state->distbits = 5; } /* Update the window with the last wsize (normally 32K) bytes written before returning. This is only called when a window is already in use, or when output has been written during this inflate call, but the end of the deflate stream has not been reached yet. It is also called to window dictionary data when a dictionary is loaded. Providing output buffers larger than 32K to inflate() should provide a speed advantage, since only the last 32K of output is copied to the sliding window upon return from inflate(), and since all distances after the first 32K of output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ static void zlib_updatewindow(z_streamp strm, unsigned out) { struct inflate_state *state; unsigned copy, dist; state = (struct inflate_state *)strm->state; /* copy state->wsize or less output bytes into the circular window */ copy = out - strm->avail_out; if (copy >= state->wsize) { memcpy(state->window, strm->next_out - state->wsize, state->wsize); state->write = 0; state->whave = state->wsize; } else { dist = state->wsize - state->write; if (dist > copy) dist = copy; memcpy(state->window + state->write, strm->next_out - copy, dist); copy -= dist; if (copy) { memcpy(state->window, strm->next_out - copy, copy); state->write = copy; state->whave = state->wsize; } else { state->write += dist; if (state->write == state->wsize) state->write = 0; if (state->whave < state->wsize) state->whave += dist; } } } /* * At the end of a Deflate-compressed PPP packet, we expect to have seen * a `stored' block type value but not the (zero) length bytes. */ /* Returns true if inflate is currently at the end of a block generated by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored block. When decompressing, PPP checks that at the end of input packet, inflate is waiting for these length bytes. */ static int zlib_inflateSyncPacket(z_streamp strm) { struct inflate_state *state; if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; state = (struct inflate_state *)strm->state; if (state->mode == STORED && state->bits == 0) { state->mode = TYPE; return Z_OK; } return Z_DATA_ERROR; } /* Macros for inflate(): */ /* check function to use adler32() for zlib or crc32() for gzip */ #define UPDATE(check, buf, len) zlib_adler32(check, buf, len) /* Load registers with state in inflate() for speed */ #define LOAD() \ do { \ put = strm->next_out; \ left = strm->avail_out; \ next = strm->next_in; \ have = strm->avail_in; \ hold = state->hold; \ bits = state->bits; \ } while (0) /* Restore state from registers in inflate() */ #define RESTORE() \ do { \ strm->next_out = put; \ strm->avail_out = left; \ strm->next_in = next; \ strm->avail_in = have; \ state->hold = hold; \ state->bits = bits; \ } while (0) /* Clear the input bit accumulator */ #define INITBITS() \ do { \ hold = 0; \ bits = 0; \ } while (0) /* Get a byte of input into the bit accumulator, or return from inflate() if there is no input available. */ #define PULLBYTE() \ do { \ if (have == 0) goto inf_leave; \ have--; \ hold += (unsigned long)(*next++) << bits; \ bits += 8; \ } while (0) /* Assure that there are at least n bits in the bit accumulator. If there is not enough available input to do that, then return from inflate(). */ #define NEEDBITS(n) \ do { \ while (bits < (unsigned)(n)) \ PULLBYTE(); \ } while (0) /* Return the low n bits of the bit accumulator (n < 16) */ #define BITS(n) \ ((unsigned)hold & ((1U << (n)) - 1)) /* Remove n bits from the bit accumulator */ #define DROPBITS(n) \ do { \ hold >>= (n); \ bits -= (unsigned)(n); \ } while (0) /* Remove zero to seven bits as needed to go to a byte boundary */ #define BYTEBITS() \ do { \ hold >>= bits & 7; \ bits -= bits & 7; \ } while (0) /* Reverse the bytes in a 32-bit value */ #define REVERSE(q) \ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is structured roughly as follows: for (;;) switch (state) { ... case STATEn: if (not enough input data or output space to make progress) return; ... make progress ... state = STATEm; break; ... } so when inflate() is called again, the same case is attempted again, and if the appropriate resources are provided, the machine proceeds to the next state. The NEEDBITS() macro is usually the way the state evaluates whether it can proceed or should return. NEEDBITS() does the return if the requested bits are not available. The typical use of the BITS macros is: NEEDBITS(n); ... do something with BITS(n) ... DROPBITS(n); where NEEDBITS(n) either returns from inflate() if there isn't enough input left to load n bits into the accumulator, or it continues. BITS(n) gives the low n bits in the accumulator. When done, DROPBITS(n) drops the low n bits off the accumulator. INITBITS() clears the accumulator and sets the number of available bits to zero. BYTEBITS() discards just enough bits to put the accumulator on a byte boundary. After BYTEBITS() and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return if there is no input available. The decoding of variable length codes uses PULLBYTE() directly in order to pull just enough bytes to decode the next code, and no more. Some states loop until they get enough input, making sure that enough state information is maintained to continue the loop where it left off if NEEDBITS() returns in the loop. For example, want, need, and keep would all have to actually be part of the saved state in case NEEDBITS() returns: case STATEw: while (want < need) { NEEDBITS(n); keep[want++] = BITS(n); DROPBITS(n); } state = STATEx; case STATEx: As shown above, if the next state is also the next case, then the break is omitted. A state may also return if there is not enough output space available to complete that state. Those states are copying stored data, writing a literal byte, and copying a matching string. When returning, a "goto inf_leave" is used to update the total counters, update the check value, and determine whether any progress has been made during that inflate() call in order to return the proper return code. Progress is defined as a change in either strm->avail_in or strm->avail_out. When there is a window, goto inf_leave will update the window with the last output written. If a goto inf_leave occurs in the middle of decompression and there is no window currently, goto inf_leave will create one and copy output to the window for the next call of inflate(). In this implementation, the flush parameter of inflate() only affects the return code (per zlib.h). inflate() always writes as much as possible to strm->next_out, given the space available and the provided input--the effect documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers the allocation of and copying into a sliding window until necessary, which provides the effect documented in zlib.h for Z_FINISH when the entire input stream available. So the only thing the flush parameter actually does is: when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it will return Z_BUF_ERROR if it has not reached the end of the stream. */ int zlib_inflate(z_streamp strm, int flush) { struct inflate_state *state; const unsigned char *next; /* next input */ unsigned char *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ unsigned bits; /* bits in bit buffer */ unsigned in, out; /* save starting available input and output */ unsigned copy; /* number of stored or match bytes to copy */ unsigned char *from; /* where to copy match bytes from */ code this; /* current decoding table entry */ code last; /* parent table entry */ unsigned len; /* length to copy for repeats, bits to drop */ int ret; /* return code */ static const unsigned short order[19] = /* permutation of code lengths */ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; /* Do not check for strm->next_out == NULL here as ppc zImage inflates to strm->next_out = 0 */ if (strm == NULL || strm->state == NULL || (strm->next_in == NULL && strm->avail_in != 0)) return Z_STREAM_ERROR; state = (struct inflate_state *)strm->state; if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ LOAD(); in = have; out = left; ret = Z_OK; for (;;) switch (state->mode) { case HEAD: if (state->wrap == 0) { state->mode = TYPEDO; break; } NEEDBITS(16); if ( ((BITS(8) << 8) + (hold >> 8)) % 31) { strm->msg = (char *)"incorrect header check"; state->mode = BAD; break; } if (BITS(4) != Z_DEFLATED) { strm->msg = (char *)"unknown compression method"; state->mode = BAD; break; } DROPBITS(4); len = BITS(4) + 8; if (len > state->wbits) { strm->msg = (char *)"invalid window size"; state->mode = BAD; break; } state->dmax = 1U << len; strm->adler = state->check = zlib_adler32(0L, NULL, 0); state->mode = hold & 0x200 ? DICTID : TYPE; INITBITS(); break; case DICTID: NEEDBITS(32); strm->adler = state->check = REVERSE(hold); INITBITS(); state->mode = DICT; /* fall through */ case DICT: if (state->havedict == 0) { RESTORE(); return Z_NEED_DICT; } strm->adler = state->check = zlib_adler32(0L, NULL, 0); state->mode = TYPE; /* fall through */ case TYPE: if (flush == Z_BLOCK) goto inf_leave; /* fall through */ case TYPEDO: if (state->last) { BYTEBITS(); state->mode = CHECK; break; } NEEDBITS(3); state->last = BITS(1); DROPBITS(1); switch (BITS(2)) { case 0: /* stored block */ state->mode = STORED; break; case 1: /* fixed block */ zlib_fixedtables(state); state->mode = LEN; /* decode codes */ break; case 2: /* dynamic block */ state->mode = TABLE; break; case 3: strm->msg = (char *)"invalid block type"; state->mode = BAD; } DROPBITS(2); break; case STORED: BYTEBITS(); /* go to byte boundary */ NEEDBITS(32); if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { strm->msg = (char *)"invalid stored block lengths"; state->mode = BAD; break; } state->length = (unsigned)hold & 0xffff; INITBITS(); state->mode = COPY; /* fall through */ case COPY: copy = state->length; if (copy) { if (copy > have) copy = have; if (copy > left) copy = left; if (copy == 0) goto inf_leave; memcpy(put, next, copy); have -= copy; next += copy; left -= copy; put += copy; state->length -= copy; break; } state->mode = TYPE; break; case TABLE: NEEDBITS(14); state->nlen = BITS(5) + 257; DROPBITS(5); state->ndist = BITS(5) + 1; DROPBITS(5); state->ncode = BITS(4) + 4; DROPBITS(4); #ifndef PKZIP_BUG_WORKAROUND if (state->nlen > 286 || state->ndist > 30) { strm->msg = (char *)"too many length or distance symbols"; state->mode = BAD; break; } #endif state->have = 0; state->mode = LENLENS; /* fall through */ case LENLENS: while (state->have < state->ncode) { NEEDBITS(3); state->lens[order[state->have++]] = (unsigned short)BITS(3); DROPBITS(3); } while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; state->lencode = (code const *)(state->next); state->lenbits = 7; ret = zlib_inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid code lengths set"; state->mode = BAD; break; } state->have = 0; state->mode = CODELENS; /* fall through */ case CODELENS: while (state->have < state->nlen + state->ndist) { for (;;) { this = state->lencode[BITS(state->lenbits)]; if ((unsigned)(this.bits) <= bits) break; PULLBYTE(); } if (this.val < 16) { NEEDBITS(this.bits); DROPBITS(this.bits); state->lens[state->have++] = this.val; } else { if (this.val == 16) { NEEDBITS(this.bits + 2); DROPBITS(this.bits); if (state->have == 0) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } len = state->lens[state->have - 1]; copy = 3 + BITS(2); DROPBITS(2); } else if (this.val == 17) { NEEDBITS(this.bits + 3); DROPBITS(this.bits); len = 0; copy = 3 + BITS(3); DROPBITS(3); } else { NEEDBITS(this.bits + 7); DROPBITS(this.bits); len = 0; copy = 11 + BITS(7); DROPBITS(7); } if (state->have + copy > state->nlen + state->ndist) { strm->msg = (char *)"invalid bit length repeat"; state->mode = BAD; break; } while (copy--) state->lens[state->have++] = (unsigned short)len; } } /* handle error breaks in while */ if (state->mode == BAD) break; /* build code tables */ state->next = state->codes; state->lencode = (code const *)(state->next); state->lenbits = 9; ret = zlib_inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); if (ret) { strm->msg = (char *)"invalid literal/lengths set"; state->mode = BAD; break; } state->distcode = (code const *)(state->next); state->distbits = 6; ret = zlib_inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); if (ret) { strm->msg = (char *)"invalid distances set"; state->mode = BAD; break; } state->mode = LEN; /* fall through */ case LEN: if (have >= 6 && left >= 258) { RESTORE(); inflate_fast(strm, out); LOAD(); break; } for (;;) { this = state->lencode[BITS(state->lenbits)]; if ((unsigned)(this.bits) <= bits) break; PULLBYTE(); } if (this.op && (this.op & 0xf0) == 0) { last = this; for (;;) { this = state->lencode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + this.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(this.bits); state->length = (unsigned)this.val; if ((int)(this.op) == 0) { state->mode = LIT; break; } if (this.op & 32) { state->mode = TYPE; break; } if (this.op & 64) { strm->msg = (char *)"invalid literal/length code"; state->mode = BAD; break; } state->extra = (unsigned)(this.op) & 15; state->mode = LENEXT; /* fall through */ case LENEXT: if (state->extra) { NEEDBITS(state->extra); state->length += BITS(state->extra); DROPBITS(state->extra); } state->mode = DIST; /* fall through */ case DIST: for (;;) { this = state->distcode[BITS(state->distbits)]; if ((unsigned)(this.bits) <= bits) break; PULLBYTE(); } if ((this.op & 0xf0) == 0) { last = this; for (;;) { this = state->distcode[last.val + (BITS(last.bits + last.op) >> last.bits)]; if ((unsigned)(last.bits + this.bits) <= bits) break; PULLBYTE(); } DROPBITS(last.bits); } DROPBITS(this.bits); if (this.op & 64) { strm->msg = (char *)"invalid distance code"; state->mode = BAD; break; } state->offset = (unsigned)this.val; state->extra = (unsigned)(this.op) & 15; state->mode = DISTEXT; /* fall through */ case DISTEXT: if (state->extra) { NEEDBITS(state->extra); state->offset += BITS(state->extra); DROPBITS(state->extra); } #ifdef INFLATE_STRICT if (state->offset > state->dmax) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } #endif if (state->offset > state->whave + out - left) { strm->msg = (char *)"invalid distance too far back"; state->mode = BAD; break; } state->mode = MATCH; /* fall through */ case MATCH: if (left == 0) goto inf_leave; copy = out - left; if (state->offset > copy) { /* copy from window */ copy = state->offset - copy; if (copy > state->write) { copy -= state->write; from = state->window + (state->wsize - copy); } else from = state->window + (state->write - copy); if (copy > state->length) copy = state->length; } else { /* copy from output */ from = put - state->offset; copy = state->length; } if (copy > left) copy = left; left -= copy; state->length -= copy; do { *put++ = *from++; } while (--copy); if (state->length == 0) state->mode = LEN; break; case LIT: if (left == 0) goto inf_leave; *put++ = (unsigned char)(state->length); left--; state->mode = LEN; break; case CHECK: if (state->wrap) { NEEDBITS(32); out -= left; strm->total_out += out; state->total += out; if (out) strm->adler = state->check = UPDATE(state->check, put - out, out); out = left; if (( REVERSE(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; } INITBITS(); } state->mode = DONE; /* fall through */ case DONE: ret = Z_STREAM_END; goto inf_leave; case BAD: ret = Z_DATA_ERROR; goto inf_leave; case MEM: return Z_MEM_ERROR; case SYNC: default: return Z_STREAM_ERROR; } /* Return from inflate(), updating the total counts and the check value. If there was no progress during the inflate() call, return a buffer error. Call zlib_updatewindow() to create and/or update the window state. */ inf_leave: RESTORE(); if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) zlib_updatewindow(strm, out); in -= strm->avail_in; out -= strm->avail_out; strm->total_in += in; strm->total_out += out; state->total += out; if (state->wrap && out) strm->adler = state->check = UPDATE(state->check, strm->next_out - out, out); strm->data_type = state->bits + (state->last ? 64 : 0) + (state->mode == TYPE ? 128 : 0); if (flush == Z_PACKET_FLUSH && ret == Z_OK && strm->avail_out != 0 && strm->avail_in == 0) return zlib_inflateSyncPacket(strm); if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) ret = Z_BUF_ERROR; return ret; } int zlib_inflateEnd(z_streamp strm) { if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; return Z_OK; } /* * This subroutine adds the data at next_in/avail_in to the output history * without performing any output. The output buffer must be "caught up"; * i.e. no pending output but this should always be the case. The state must * be waiting on the start of a block (i.e. mode == TYPE or HEAD). On exit, * the output will also be caught up, and the checksum will have been updated * if need be. */ int zlib_inflateIncomp(z_stream *z) { struct inflate_state *state = (struct inflate_state *)z->state; Byte *saved_no = z->next_out; uInt saved_ao = z->avail_out; if (state->mode != TYPE && state->mode != HEAD) return Z_DATA_ERROR; /* Setup some variables to allow misuse of updateWindow */ z->avail_out = 0; z->next_out = (unsigned char*)z->next_in + z->avail_in; zlib_updatewindow(z, z->avail_in); /* Restore saved variables */ z->avail_out = saved_ao; z->next_out = saved_no; z->adler = state->check = UPDATE(state->check, z->next_in, z->avail_in); z->total_out += z->avail_in; z->total_in += z->avail_in; z->next_in += z->avail_in; state->total += z->avail_in; z->avail_in = 0; return Z_OK; } apfsprogs-0.2.0/lib/zlib_inflate/inftrees.c000066400000000000000000000320651471277137200207310ustar00rootroot00000000000000/* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2005 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "apfs/zlib_inflate/zutil.h" #include "apfs/zlib_inflate/inftrees.h" #define MAXBITS 15 /* Build a set of tables to decode the provided canonical Huffman code. The code lengths are lens[0..codes-1]. The result starts at *table, whose indices are 0..2^bits-1. work is a writable array of at least lens shorts, which is used as a work area. type is the type of code to be generated, CODES, LENS, or DISTS. On return, zero is success, -1 is an invalid code, and +1 means that ENOUGH isn't enough. table on return points to the next available entry's address. bits is the requested root table index bits, and on return it is the actual root table index bits. It will differ if the request is greater than the longest code or if it is less than the shortest code. */ int zlib_inflate_table(codetype type, unsigned short *lens, unsigned codes, code **table, unsigned *bits, unsigned short *work) { unsigned len; /* a code's length in bits */ unsigned sym; /* index of code symbols */ unsigned min, max; /* minimum and maximum code lengths */ unsigned root; /* number of index bits for root table */ unsigned curr; /* number of index bits for current table */ unsigned drop; /* code bits to drop for sub-table */ int left; /* number of prefix codes available */ unsigned used; /* code entries in table used */ unsigned huff; /* Huffman code */ unsigned incr; /* for incrementing code, index */ unsigned fill; /* index for replicating entries */ unsigned low; /* low bits for current root entry */ unsigned mask; /* mask for low root bits */ code this; /* table entry for duplication */ code *next; /* next available space in table */ const unsigned short *base; /* base value table to use */ const unsigned short *extra; /* extra bits table to use */ int end; /* use base and extra for symbol > end */ unsigned short count[MAXBITS+1]; /* number of codes of each length */ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64}; /* Process a set of code lengths to create a canonical Huffman code. The code lengths are lens[0..codes-1]. Each length corresponds to the symbols 0..codes-1. The Huffman code is generated by first sorting the symbols by length from short to long, and retaining the symbol order for codes with equal lengths. Then the code starts with all zero bits for the first code of the shortest length, and the codes are integer increments for the same length, and zeros are appended as the length increases. For the deflate format, these bits are stored backwards from their more natural integer increment ordering, and so when the decoding tables are built in the large loop below, the integer codes are incremented backwards. This routine assumes, but does not check, that all of the entries in lens[] are in the range 0..MAXBITS. The caller must assure this. 1..MAXBITS is interpreted as that code length. zero means that that symbol does not occur in this code. The codes are sorted by computing a count of codes for each length, creating from that a table of starting indices for each length in the sorted table, and then entering the symbols in order in the sorted table. The sorted table is work[], with that space being provided by the caller. The length counts are used for other purposes as well, i.e. finding the minimum and maximum length codes, determining if there are any codes at all, checking for a valid set of lengths, and looking ahead at length counts to determine sub-table sizes when building the decoding tables. */ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ for (len = 0; len <= MAXBITS; len++) count[len] = 0; for (sym = 0; sym < codes; sym++) count[lens[sym]]++; /* bound code lengths, force root to be within code lengths */ root = *bits; for (max = MAXBITS; max >= 1; max--) if (count[max] != 0) break; if (root > max) root = max; if (max == 0) { /* no symbols to code at all */ this.op = (unsigned char)64; /* invalid code marker */ this.bits = (unsigned char)1; this.val = (unsigned short)0; *(*table)++ = this; /* make a table to force an error */ *(*table)++ = this; *bits = 1; return 0; /* no symbols, but wait for decoding to report error */ } for (min = 1; min < MAXBITS; min++) if (count[min] != 0) break; if (root < min) root = min; /* check for an over-subscribed or incomplete set of lengths */ left = 1; for (len = 1; len <= MAXBITS; len++) { left <<= 1; left -= count[len]; if (left < 0) return -1; /* over-subscribed */ } if (left > 0 && (type == CODES || max != 1)) return -1; /* incomplete set */ /* generate offsets into symbol table for each length for sorting */ offs[1] = 0; for (len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + count[len]; /* sort symbols by length, by symbol order within each length */ for (sym = 0; sym < codes; sym++) if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; /* Create and fill in decoding tables. In this loop, the table being filled is at next and has curr index bits. The code being used is huff with length len. That code is converted to an index by dropping drop bits off of the bottom. For codes where len is less than drop + curr, those top drop + curr - len bits are incremented through all values to fill the table with replicated entries. root is the number of index bits for the root table. When len exceeds root, sub-tables are created pointed to by the root entry with an index of the low root bits of huff. This is saved in low to check for when a new sub-table should be started. drop is zero when the root table is being filled, and drop is root when sub-tables are being filled. When a new sub-table is needed, it is necessary to look ahead in the code lengths to determine what size sub-table is needed. The length counts are used for this, and so count[] is decremented as codes are entered in the tables. used keeps track of how many table entries have been allocated from the provided *table space. It is checked when a LENS table is being made against the space in *table, ENOUGH, minus the maximum space needed by the worst case distance code, MAXD. This should never happen, but the sufficiency of ENOUGH has not been proven exhaustively, hence the check. This assumes that when type == LENS, bits == 9. sym increments through all symbols, and the loop terminates when all codes of length max, i.e. all codes, have been processed. This routine permits incomplete codes, so another loop after this one fills in the rest of the decoding tables with invalid code markers. */ /* set up for code type */ switch (type) { case CODES: base = extra = work; /* dummy value--not used */ end = 19; break; case LENS: base = lbase; base -= 257; extra = lext; extra -= 257; end = 256; break; default: /* DISTS */ base = dbase; extra = dext; end = -1; } /* initialize state for loop */ huff = 0; /* starting code */ sym = 0; /* starting code symbol */ len = min; /* starting code length */ next = *table; /* current table to fill in */ curr = root; /* current table index bits */ drop = 0; /* current bits to drop from code for index */ low = (unsigned)(-1); /* trigger new sub-table when len > root */ used = 1U << root; /* use root table entries */ mask = used - 1; /* mask for comparing low */ /* check available table space */ if (type == LENS && used >= ENOUGH - MAXD) return 1; /* process all codes and make table entries */ for (;;) { /* create table entry */ this.bits = (unsigned char)(len - drop); if ((int)(work[sym]) < end) { this.op = (unsigned char)0; this.val = work[sym]; } else if ((int)(work[sym]) > end) { this.op = (unsigned char)(extra[work[sym]]); this.val = base[work[sym]]; } else { this.op = (unsigned char)(32 + 64); /* end of block */ this.val = 0; } /* replicate for those indices with low len bits equal to huff */ incr = 1U << (len - drop); fill = 1U << curr; min = fill; /* save offset to next table */ do { fill -= incr; next[(huff >> drop) + fill] = this; } while (fill != 0); /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; /* go to next symbol, update count, len */ sym++; if (--(count[len]) == 0) { if (len == max) break; len = lens[work[sym]]; } /* create new sub-table if needed */ if (len > root && (huff & mask) != low) { /* if first time, transition to sub-tables */ if (drop == 0) drop = root; /* increment past last table */ next += min; /* here min is 1 << curr */ /* determine length of next table */ curr = len - drop; left = (int)(1 << curr); while (curr + drop < max) { left -= count[curr + drop]; if (left <= 0) break; curr++; left <<= 1; } /* check for enough space */ used += 1U << curr; if (type == LENS && used >= ENOUGH - MAXD) return 1; /* point entry in root table to sub-table */ low = huff & mask; (*table)[low].op = (unsigned char)curr; (*table)[low].bits = (unsigned char)root; (*table)[low].val = (unsigned short)(next - *table); } } /* Fill in rest of table for incomplete codes. This loop is similar to the loop above in incrementing huff for table indices. It is assumed that len is equal to curr + drop, so there is no loop needed to increment through high index bits. When the current sub-table is filled, the loop drops back to the root table to fill in any remaining entries there. */ this.op = (unsigned char)64; /* invalid code marker */ this.bits = (unsigned char)(len - drop); this.val = (unsigned short)0; while (huff != 0) { /* when done with sub-table, drop back to root table */ if (drop != 0 && (huff & mask) != low) { drop = 0; len = root; next = *table; this.bits = (unsigned char)len; } /* put invalid code marker in table */ next[huff >> drop] = this; /* backwards increment the len-bit code huff */ incr = 1U << (len - 1); while (huff & incr) incr >>= 1; if (incr != 0) { huff &= incr - 1; huff += incr; } else huff = 0; } /* set return parameters */ *table += used; *bits = root; return 0; } apfsprogs-0.2.0/lib/zlib_inflate/infutil.c000066400000000000000000000023071471277137200205600ustar00rootroot00000000000000#include #include "apfs/zlib_inflate/zutil.h" #if 0 #include #include #endif /* Utility function: initialize zlib, unpack binary blob, clean up zlib, * return len or negative error code. */ int zlib_inflate_blob(void *gunzip_buf, unsigned int sz, const void *buf, unsigned int len) { const u8 *zbuf = buf; struct z_stream_s *strm; int rc; rc = -ENOMEM; strm = malloc(sizeof(*strm)); if (strm == NULL) goto gunzip_nomem1; strm->workspace = malloc(zlib_inflate_workspacesize()); if (strm->workspace == NULL) goto gunzip_nomem2; /* gzip header (1f,8b,08... 10 bytes total + possible asciz filename) * expected to be stripped from input */ strm->next_in = zbuf; strm->avail_in = len; strm->next_out = gunzip_buf; strm->avail_out = sz; rc = zlib_inflateInit2(strm, -MAX_WBITS); if (rc == Z_OK) { rc = zlib_inflate(strm, Z_FINISH); /* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */ if (rc == Z_STREAM_END) rc = sz - strm->avail_out; else rc = -EINVAL; zlib_inflateEnd(strm); } else rc = -EINVAL; free(strm->workspace); gunzip_nomem2: free(strm); gunzip_nomem1: return rc; /* returns Z_OK (0) if successful */ } apfsprogs-0.2.0/mkapfs/000077500000000000000000000000001471277137200150115ustar00rootroot00000000000000apfsprogs-0.2.0/mkapfs/Makefile000066400000000000000000000024111471277137200164470ustar00rootroot00000000000000SRCS = btree.c dir.c mkapfs.c object.c spaceman.c super.c OBJS = $(SRCS:.c=.o) DEPS = $(SRCS:.c=.d) LIBDIR = ../lib LIBRARY = $(LIBDIR)/libapfs.a DESTDIR ?= ~ BINDIR = /bin MANDIR = /share/man/man8 SPARSE_VERSION := $(shell sparse --version 2>/dev/null) GIT_COMMIT = $(shell git describe --always HEAD | tail -c 9) override CFLAGS += -Wall -Wno-address-of-packed-member -fno-strict-aliasing -I$(CURDIR)/../include mkapfs: $(OBJS) $(LIBRARY) @echo ' Linking...' @$(CC) $(CFLAGS) $(LDFLAGS) -o mkapfs $(OBJS) $(LIBRARY) @echo ' Build complete' # Build the common libraries $(LIBRARY): FORCE @echo ' Building libraries...' @$(MAKE) -C $(LIBDIR) --silent --no-print-directory @echo ' Library build complete' FORCE: %.o: %.c @echo ' Compiling $<...' @$(CC) $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif mkapfs.o super.o: version.h version.h: FORCE @printf '#define GIT_COMMIT\t"%s"\n' $(GIT_COMMIT) > version.h -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) mkapfs version.h install: install -d $(DESTDIR)$(BINDIR) install -t $(DESTDIR)$(BINDIR) mkapfs ln -fs -T mkapfs $(DESTDIR)$(BINDIR)/mkfs.apfs install -d $(DESTDIR)$(MANDIR) install -m 644 -t $(DESTDIR)$(MANDIR) mkapfs.8 ln -fs -T mkapfs.8 $(DESTDIR)$(MANDIR)/mkfs.apfs.8 apfsprogs-0.2.0/mkapfs/btree.c000066400000000000000000000265071471277137200162700ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include "btree.h" #include "dir.h" #include "mkapfs.h" #include "object.h" /* Constants used in managing the size of a node's table of contents */ #define BTREE_TOC_ENTRY_INCREMENT 8 #define BTREE_TOC_ENTRY_MAX_UNUSED (2 * BTREE_TOC_ENTRY_INCREMENT) /** * set_empty_btree_info - Set the info footer for an empty b-tree node * @info: pointer to the on-disk info footer * @subtype: subtype of the root node, i.e., tree type * * Should only be called for the free queues, the snapshot metadata tree, and * the extent reference tree. */ static void set_empty_btree_info(struct apfs_btree_info *info, u32 subtype) { u16 flags; if (subtype == APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE) flags = APFS_BTREE_EPHEMERAL | APFS_BTREE_ALLOW_GHOSTS; else if (subtype == APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE) flags = APFS_BTREE_PHYSICAL; else flags = APFS_BTREE_PHYSICAL | APFS_BTREE_KV_NONALIGNED; info->bt_fixed.bt_flags = cpu_to_le32(flags); info->bt_fixed.bt_node_size = cpu_to_le32(param->blocksize); /* The other two trees don't have fixed key/value sizes */ if (subtype == APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE) { info->bt_fixed.bt_key_size = cpu_to_le32(sizeof(struct apfs_spaceman_free_queue_key)); info->bt_fixed.bt_val_size = cpu_to_le32(8); info->bt_longest_key = cpu_to_le32(sizeof(struct apfs_spaceman_free_queue_key)); info->bt_longest_val = cpu_to_le32(8); } if (subtype == APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE) { info->bt_fixed.bt_key_size = cpu_to_le32(sizeof(struct apfs_fusion_mt_key)); info->bt_fixed.bt_val_size = cpu_to_le32(sizeof(struct apfs_fusion_mt_val)); info->bt_longest_key = cpu_to_le32(sizeof(struct apfs_fusion_mt_key)); info->bt_longest_val = cpu_to_le32(sizeof(struct apfs_fusion_mt_val)); } info->bt_node_count = cpu_to_le64(1); /* Only one node: the root */ } /** * min_table_size - Get the minimum size of the table of contents for a leaf * @type: btree type for the leaf node */ static int min_table_size(u32 type) { int key_size, val_size, toc_size; int space, count; /* Trees with fixed key/value sizes preallocate the whole table */ switch (type) { case APFS_OBJECT_TYPE_OMAP: key_size = sizeof(struct apfs_omap_key); val_size = sizeof(struct apfs_omap_val); toc_size = sizeof(struct apfs_kvoff); break; case APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE: key_size = sizeof(struct apfs_spaceman_free_queue_key); val_size = sizeof(__le64); /* We assume no ghosts here */ toc_size = sizeof(struct apfs_kvoff); break; case APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE: key_size = sizeof(struct apfs_fusion_mt_key); val_size = sizeof(struct apfs_fusion_mt_val); toc_size = sizeof(struct apfs_kvoff); break; default: /* It should at least have room for one record */ return sizeof(struct apfs_kvloc) * BTREE_TOC_ENTRY_MAX_UNUSED; } /* The footer of root nodes is ignored for some reason */ space = param->blocksize - sizeof(struct apfs_btree_node_phys); count = space / (key_size + val_size + toc_size); return count * toc_size; } /** * make_empty_btree_root - Make an empty root for a b-tree * @bno: block number to use * @oid: object id to use * @subtype: subtype of the root node, i.e., tree type * * Should only be called for the free queues, the snapshot metadata tree, and * the extent reference tree. */ void make_empty_btree_root(u64 bno, u64 oid, u32 subtype) { struct apfs_btree_node_phys *root = get_zeroed_block(bno); u32 type; u16 flags; int toc_len, free_len; int head_len = sizeof(*root); int info_len = sizeof(struct apfs_btree_info); flags = APFS_BTNODE_ROOT | APFS_BTNODE_LEAF; if (subtype == APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE || subtype == APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE) flags |= APFS_BTNODE_FIXED_KV_SIZE; root->btn_flags = cpu_to_le16(flags); toc_len = min_table_size(subtype); /* No keys and no values, so this is straightforward */ root->btn_nkeys = 0; free_len = param->blocksize - head_len - toc_len - info_len; root->btn_table_space.off = 0; root->btn_table_space.len = cpu_to_le16(toc_len); root->btn_free_space.off = 0; root->btn_free_space.len = cpu_to_le16(free_len); /* No fragmentation */ root->btn_key_free_list.off = cpu_to_le16(APFS_BTOFF_INVALID); root->btn_key_free_list.len = 0; root->btn_val_free_list.off = cpu_to_le16(APFS_BTOFF_INVALID); root->btn_val_free_list.len = 0; set_empty_btree_info((void *)root + param->blocksize - info_len, subtype); type = APFS_OBJECT_TYPE_BTREE; if (subtype == APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE) type |= APFS_OBJ_EPHEMERAL; else type |= APFS_OBJ_PHYSICAL; set_object_header(&root->btn_o, param->blocksize, oid, type, subtype); munmap(root, param->blocksize); } /** * set_omap_info - Set the info footer for an object map node * @info: pointer to the on-disk info footer * @nkeys: number of records in the omap */ static void set_omap_info(struct apfs_btree_info *info, int nkeys) { info->bt_fixed.bt_flags = cpu_to_le32(APFS_BTREE_PHYSICAL); info->bt_fixed.bt_node_size = cpu_to_le32(param->blocksize); info->bt_fixed.bt_key_size = cpu_to_le32(sizeof(struct apfs_omap_key)); info->bt_fixed.bt_val_size = cpu_to_le32(sizeof(struct apfs_omap_val)); info->bt_longest_key = cpu_to_le32(sizeof(struct apfs_omap_key)); info->bt_longest_val = cpu_to_le32(sizeof(struct apfs_omap_val)); info->bt_key_count = cpu_to_le64(nkeys); info->bt_node_count = cpu_to_le64(1); /* Only one node: the root */ } /** * make_omap_root - Make the root node of an object map * @bno: block number to use * @is_vol: is this the object map for a volume? */ static void make_omap_root(u64 bno, bool is_vol) { struct apfs_btree_node_phys *root = get_zeroed_block(bno); struct apfs_omap_key *key; struct apfs_omap_val *val; struct apfs_kvoff *kvoff; int toc_len, key_len, val_len, free_len; int head_len = sizeof(*root); int info_len = sizeof(struct apfs_btree_info); root->btn_flags = cpu_to_le16(APFS_BTNODE_ROOT | APFS_BTNODE_LEAF | APFS_BTNODE_FIXED_KV_SIZE); /* The mkfs will need only one record on each omap */ root->btn_nkeys = cpu_to_le32(1); toc_len = min_table_size(APFS_OBJECT_TYPE_OMAP); key_len = 1 * sizeof(*key); val_len = 1 * sizeof(*val); /* Location of the one record */ key = (void *)root + head_len + toc_len; val = (void *)root + param->blocksize - info_len - val_len; kvoff = (void *)root + head_len; kvoff->k = 0; kvoff->v = cpu_to_le16(val_len); /* Set the key and value for the one record */ if (is_vol) { /* Map for the catalog root */ key->ok_oid = cpu_to_le64(FIRST_VOL_CAT_ROOT_OID); val->ov_paddr = cpu_to_le64(FIRST_VOL_CAT_ROOT_BNO); } else { /* Map for the one volume superblock */ key->ok_oid = cpu_to_le64(FIRST_VOL_OID); val->ov_paddr = cpu_to_le64(FIRST_VOL_BNO); } key->ok_xid = cpu_to_le64(MKFS_XID); val->ov_size = cpu_to_le32(param->blocksize); /* Only size supported */ root->btn_table_space.off = 0; root->btn_table_space.len = cpu_to_le16(toc_len); free_len = param->blocksize - head_len - toc_len - key_len - val_len - info_len; root->btn_free_space.off = cpu_to_le16(key_len); root->btn_free_space.len = cpu_to_le16(free_len); /* No fragmentation */ root->btn_key_free_list.off = cpu_to_le16(APFS_BTOFF_INVALID); root->btn_key_free_list.len = 0; root->btn_val_free_list.off = cpu_to_le16(APFS_BTOFF_INVALID); root->btn_val_free_list.len = 0; set_omap_info((void *)root + param->blocksize - info_len, 1); set_object_header(&root->btn_o, param->blocksize, bno, APFS_OBJECT_TYPE_BTREE | APFS_OBJ_PHYSICAL, APFS_OBJECT_TYPE_OMAP); munmap(root, param->blocksize); } /** * make_omap_btree - Make an object map * @bno: block number to use * @is_vol: is this the object map for a volume? */ void make_omap_btree(u64 bno, bool is_vol) { struct apfs_omap_phys *omap = get_zeroed_block(bno); if (!is_vol) omap->om_flags = cpu_to_le32(APFS_OMAP_MANUALLY_MANAGED); omap->om_tree_type = cpu_to_le32(APFS_OBJECT_TYPE_BTREE | APFS_OBJ_PHYSICAL); omap->om_snapshot_tree_type = cpu_to_le32(APFS_OBJECT_TYPE_BTREE | APFS_OBJ_PHYSICAL); if (is_vol) { omap->om_tree_oid = cpu_to_le64(FIRST_VOL_OMAP_ROOT_BNO); make_omap_root(FIRST_VOL_OMAP_ROOT_BNO, is_vol); } else { omap->om_tree_oid = cpu_to_le64(MAIN_OMAP_ROOT_BNO); make_omap_root(MAIN_OMAP_ROOT_BNO, is_vol); } set_object_header(&omap->om_o, param->blocksize, bno, APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_OMAP, APFS_OBJECT_TYPE_INVALID); munmap(omap, param->blocksize); } /** * set_cat_info - Set the info footer for a catalog root node * @info: pointer to the on-disk info footer */ static void set_cat_info(struct apfs_btree_info *info) { int drec_keysz = param->norm_sensitive ? sizeof(struct apfs_drec_key) : sizeof(struct apfs_drec_hashed_key); int maxkey = drec_keysz + strlen("private-dir") + 1; int maxval = sizeof(struct apfs_inode_val) + sizeof(struct apfs_xf_blob) + sizeof(struct apfs_x_field) + ROUND_UP(strlen("private-dir") + 1, 8); info->bt_fixed.bt_flags = cpu_to_le32(APFS_BTREE_KV_NONALIGNED); info->bt_fixed.bt_node_size = cpu_to_le32(param->blocksize); info->bt_longest_key = cpu_to_le32(maxkey); info->bt_longest_val = cpu_to_le32(maxval); info->bt_key_count = cpu_to_le64(4); /* Two records for each dir */ info->bt_node_count = cpu_to_le64(1); /* Only one node: the root */ } /** * make_cat_root - Make the root node of a catalog tree * @bno: block number to use * @oid: object id * * Creates a root node with two records: the root and private directories. */ void make_cat_root(u64 bno, u64 oid) { struct apfs_btree_node_phys *root = get_zeroed_block(bno); struct apfs_kvloc *kvloc; void *key, *key_area, *val_end, *val_area_end; int toc_len, key_len, free_len, val_len; int head_len = sizeof(*root); int info_len = sizeof(struct apfs_btree_info); root->btn_flags = cpu_to_le16(APFS_BTNODE_ROOT | APFS_BTNODE_LEAF); /* The two dentry records and their inodes */ root->btn_nkeys = cpu_to_le32(4); toc_len = min_table_size(APFS_OBJECT_TYPE_FSTREE); root->btn_table_space.off = 0; root->btn_table_space.len = cpu_to_le16(toc_len); kvloc = (void *)root + head_len; key = key_area = (void *)root + head_len + toc_len; val_end = val_area_end = (void *)root + param->blocksize - info_len; make_special_dir_dentry(APFS_PRIV_DIR_INO_NUM, "private-dir", &kvloc, key_area, &key, val_area_end, &val_end); make_special_dir_dentry(APFS_ROOT_DIR_INO_NUM, "root", &kvloc, key_area, &key, val_area_end, &val_end); make_special_dir_inode(APFS_ROOT_DIR_INO_NUM, "root", &kvloc, key_area, &key, val_area_end, &val_end); make_special_dir_inode(APFS_PRIV_DIR_INO_NUM, "private-dir", &kvloc, key_area, &key, val_area_end, &val_end); key_len = key - key_area; val_len = val_area_end - val_end; free_len = param->blocksize - head_len - toc_len - key_len - val_len - info_len; root->btn_free_space.off = cpu_to_le16(key_len); root->btn_free_space.len = cpu_to_le16(free_len); /* No fragmentation */ root->btn_key_free_list.off = cpu_to_le16(APFS_BTOFF_INVALID); root->btn_key_free_list.len = 0; root->btn_val_free_list.off = cpu_to_le16(APFS_BTOFF_INVALID); root->btn_val_free_list.len = 0; set_cat_info((void *)root + param->blocksize - info_len); set_object_header(&root->btn_o, param->blocksize, oid, APFS_OBJECT_TYPE_BTREE | APFS_OBJ_VIRTUAL, APFS_OBJECT_TYPE_FSTREE); munmap(root, param->blocksize); } apfsprogs-0.2.0/mkapfs/btree.h000066400000000000000000000005131471277137200162620ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _BTREE_H #define _BTREE_H #include extern void make_empty_btree_root(u64 bno, u64 oid, u32 subtype); extern void make_omap_btree(u64 bno, bool is_vol); extern void make_cat_root(u64 bno, u64 oid); #endif /* _BTREE_H */ apfsprogs-0.2.0/mkapfs/dir.c000066400000000000000000000150321471277137200157340ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include /* The macros for the inode mode */ #include #include #include #include #include #include "dir.h" #include "mkapfs.h" /** * set_key_header - Set the cnid and type on a catalog key * @ino: inode number * @type: record type * @key: key header to set */ static void set_key_header(u64 ino, u64 type, struct apfs_key_header *key) { u64 obj_id_and_type; obj_id_and_type = type << APFS_OBJ_TYPE_SHIFT; obj_id_and_type |= ino; key->obj_id_and_type = cpu_to_le64(obj_id_and_type); } /** * make_unhashed_dentry_key - Make an unhashed dentry key * @ino: inode number for the parent * @name: directory name * @key: key space to use * * Returns the length of the key. */ static int make_unhashed_dentry_key(u64 ino, char *name, struct apfs_drec_key *key) { u32 len = strlen(name) + 1; /* The null termination is counted */ set_key_header(ino, APFS_TYPE_DIR_REC, &key->hdr); strcpy((char *)key->name, name); key->name_len = cpu_to_le16(len); return sizeof(*key) + len; } /** * make_hashed_dentry_key - Make a hashed dentry key * @ino: inode number for the parent * @name: directory name * @key: key space to use * * Returns the length of the key. */ static int make_hashed_dentry_key(u64 ino, char *name, struct apfs_drec_hashed_key *key) { u32 len; u32 hash = 0xFFFFFFFF; u32 utf32; set_key_header(ino, APFS_TYPE_DIR_REC, &key->hdr); strcpy((char *)key->name, name); len = strlen(name) + 1; /* The null termination is counted */ /* Special directories don't have unicode characters in their names */ for (utf32 = *name; utf32; utf32 = *++name) hash = crc32c(hash, &utf32, sizeof(utf32)); hash = (hash & 0x3FFFFF) << 10; key->name_len_and_hash = cpu_to_le32(hash | len); return sizeof(*key) + len; } /** * make_special_dentry_key - Make the dentry key for a special directory * @ino: inode number for the parent * @name: directory name * @key: key space to use * * Returns the length of the key. */ static int make_special_dentry_key(u64 ino, char *name, void *key) { if (param->norm_sensitive) return make_unhashed_dentry_key(ino, name, key); else return make_hashed_dentry_key(ino, name, key); } /** * make_special_dentry_val - Make the dentry value for a special directory * @ino: inode number for the directory * @val_end: end of the value space to use * * Returns the length of the value. */ static int make_special_dentry_val(u64 ino, struct apfs_drec_val *val_end) { struct apfs_drec_val *val = (void *)val_end - sizeof(*val); val->file_id = cpu_to_le64(ino); val->date_added = cpu_to_le64(get_timestamp()); val->flags = cpu_to_le16(S_IFDIR >> 12); return sizeof(*val); } /** * make_special_inode_key - Make the inode key for a special directory * @ino: inode number for the directory * @key: key space to use * * Returns the length of the key. */ static int make_special_inode_key(u64 ino, struct apfs_inode_key *key) { set_key_header(ino, APFS_TYPE_INODE, &key->hdr); return sizeof(*key); } /** * make_special_inode_val - Make the inode value for a special directory * @ino: inode number for the directory * @name: directory name * @val_end: end of the value space to use * * Returns the length of the value. */ static int make_special_inode_val(u64 ino, char *name, struct apfs_inode_val *val_end) { struct apfs_inode_val *val = (void *)val_end - sizeof(*val); struct apfs_xf_blob *xblob; struct apfs_x_field *name_xfield; int namelen, padded_namelen, i_len; namelen = strlen(name) + 1; padded_namelen = ROUND_UP(namelen, 8); i_len = sizeof(*val) + sizeof(*xblob) + sizeof(*name_xfield) + padded_namelen; val = (void *)val_end - i_len; val->parent_id = cpu_to_le64(APFS_ROOT_DIR_PARENT); val->private_id = cpu_to_le64(ino); /* Should this be the exact same as the dentry timetamp? */ val->create_time = val->mod_time = val->change_time = val->access_time = cpu_to_le64(get_timestamp()); val->default_protection_class = cpu_to_le32(APFS_PROTECTION_CLASS_DIR_NONE); /* TODO: allow the user to override these fields */ val->owner = cpu_to_le32(geteuid()); val->group = cpu_to_le32(getegid()); val->mode = cpu_to_le16(0755 | S_IFDIR); xblob = (struct apfs_xf_blob *)val->xfields; xblob->xf_num_exts = cpu_to_le16(1); /* Just the primary name */ xblob->xf_used_data = cpu_to_le16(padded_namelen); name_xfield = (struct apfs_x_field *)xblob->xf_data; name_xfield->x_type = APFS_INO_EXT_TYPE_NAME; name_xfield->x_flags = APFS_XF_DO_NOT_COPY; name_xfield->x_size = cpu_to_le16(namelen); strcpy((char *)val_end - padded_namelen, name); return i_len; } /** * make_special_dir_inode - Make inode record for an empty special dir * @ino: inode number for the directory * @name: directory name * @next_toc: next available toc entry (updated on return) * @key_area: start of the key area * @key: start of the available key area (updated on return) * @val_area_end: end of the value area * @val_end: end of the available value area (updated on return) */ void make_special_dir_inode(u64 ino, char *name, struct apfs_kvloc **next_toc, void *key_area, void **key, void *val_area_end, void **val_end) { struct apfs_kvloc *kvloc = *next_toc; int len; len = make_special_inode_key(ino, *key); kvloc->k.off = cpu_to_le16(*key - key_area); kvloc->k.len = cpu_to_le16(len); *key += len; len = make_special_inode_val(ino, name, *val_end); kvloc->v.off = cpu_to_le16(val_area_end - *val_end + len); kvloc->v.len = cpu_to_le16(len); *val_end -= len; ++*next_toc; } /** * make_special_dir_dentry - Make dentry record for an empty special dir * @ino: inode number for the directory * @name: directory name * @next_toc: next available toc entry (updated on return) * @key_area: start of the key area * @key: start of the available key area (updated on return) * @val_area_end: end of the value area * @val_end: end of the available value area (updated on return) */ void make_special_dir_dentry(u64 ino, char *name, struct apfs_kvloc **next_toc, void *key_area, void **key, void *val_area_end, void **val_end) { struct apfs_kvloc *kvloc = *next_toc; int len; len = make_special_dentry_key(APFS_ROOT_DIR_PARENT, name, *key); kvloc->k.off = cpu_to_le16(*key - key_area); kvloc->k.len = cpu_to_le16(len); *key += len; len = make_special_dentry_val(ino, *val_end); kvloc->v.off = cpu_to_le16(val_area_end - *val_end + len); kvloc->v.len = cpu_to_le16(len); *val_end -= len; ++*next_toc; } apfsprogs-0.2.0/mkapfs/dir.h000066400000000000000000000007771471277137200157530ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _DIR_H #define _DIR_H #include extern void make_special_dir_dentry(u64 ino, char *name, struct apfs_kvloc **next_toc, void *key_area, void **key, void *val_area_end, void **val_end); extern void make_special_dir_inode(u64 ino, char *name, struct apfs_kvloc **next_toc, void *key_area, void **key, void *val_area_end, void **val_end); #endif /* _DIR_H */ apfsprogs-0.2.0/mkapfs/mkapfs.8000066400000000000000000000030611471277137200163630ustar00rootroot00000000000000.\" mkapfs.8 - manpage for mkapfs .\" .\" Copyright (C) 2019 Ernesto A. Fernández .\" .TH mkapfs 8 "November 2024" "apfsprogs 0.2.0" .SH NAME mkapfs \- create an APFS filesystem .SH SYNOPSIS .B mkapfs [\-sv] [\-L .IR label ] [\-U .IR UUID ] [\-u .IR UUID ] [\-F .IR tier2 ] [\-B .IR tier2-blocks ] .I device .RI [ blocks ] .SH DESCRIPTION .B mkapfs is an experimental tool to create an APFS filesystem with a single volume on the given .IR device . The number of blocks for the device can be specified in .IR blocks , otherwise the whole disk is used. .SH OPTIONS .TP .B \-s Enable case sensitivity for the volume. .TP .B \-z Enable normalization sensitivity for the volume. .TP .BI \-L " label" Set a label for the volume, with a maximum length of 255 bytes. .TP .BI \-U " UUID" Specify a UUID for the container, in the standard format. By default the value is chosen by /proc/sys/kernel/random/uuid. .TP .BI \-u " UUID" Specify a UUID for the volume, in the standard format. By default the value is chosen by /proc/sys/kernel/random/uuid. .TP .B \-v Print the version number of .B mkapfs and exit. .TP .BI \-F " tier2" Specify the tier 2 device for a fusion drive. .TP .BI \-B " tier2-blocks" Specify the size (in blocks) for the tier 2 device given by the .B \-F option. By default the whole disk is used. .SH REPORTING BUGS Please report bugs via email or, if preferred, file a github issue at \%https://github.com/eafer/apfsprogs/issues. .SH AUTHOR Written by Ernesto A. Fernández . .SH SEE ALSO .BR apfsck (8) apfsprogs-0.2.0/mkapfs/mkapfs.c000066400000000000000000000124371471277137200164450ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include #include #include "mkapfs.h" #include "super.h" #include "version.h" int fd_main = -1; int fd_tier2 = -1; struct parameters *param; static char *progname; /** * usage - Print usage information and exit */ static void usage(void) { fprintf(stderr, "usage: %s [-L label] [-U UUID] [-u UUID] [-F tier2] [-B tier2-blocks] [-sv] " "device [blocks]\n", progname); exit(EXIT_FAILURE); } /** * version - Print version information and exit */ static void version(void) { if (*GIT_COMMIT) { printf("mkapfs %s\n", GIT_COMMIT); exit(EXIT_SUCCESS); } else { printf("mkapfs - unknown git commit id\n"); exit(EXIT_FAILURE); } } /** * system_error - Print a system error message and exit */ __attribute__((noreturn)) void system_error(void) { perror(progname); exit(EXIT_FAILURE); } /** * fatal - Print a message and exit with an error code * @message: text to print */ __attribute__((noreturn)) void fatal(const char *message) { fprintf(stderr, "%s: %s\n", progname, message); exit(EXIT_FAILURE); } /** * get_device_size - Get the block count for a given device or image * @device_fd: file descriptor for the device * @blocksize: the filesystem blocksize */ static u64 get_device_size(int device_fd, unsigned int blocksize) { struct stat buf; u64 size; if (fstat(device_fd, &buf)) system_error(); if ((buf.st_mode & S_IFMT) == S_IFREG) return buf.st_size / blocksize; if (ioctl(device_fd, BLKGETSIZE64, &size)) system_error(); return size / blocksize; } static u64 get_main_device_size(unsigned int blocksize) { return get_device_size(fd_main, blocksize); } static u64 get_tier2_device_size(unsigned int blocksize) { if (fd_tier2 == -1) return 0; return get_device_size(fd_tier2, blocksize); } /** * get_random_uuid - Get a random UUID string in standard format * * Returns a pointer to the string. */ static char *get_random_uuid(void) { char *uuid; ssize_t ret; /* Length of a null-terminated UUID standard format string */ uuid = malloc(37); if (!uuid) system_error(); /* Linux provides randomly generated UUIDs at /proc */ do { int uuid_fd; uuid_fd = open("/proc/sys/kernel/random/uuid", O_RDONLY); if (uuid_fd == -1) system_error(); ret = read(uuid_fd, uuid, 36); if (ret == -1) system_error(); close(uuid_fd); } while (ret != 36); /* Put a null-termination, just in case */ uuid[36] = 0; return uuid; } /** * complete_parameters - Set all uninitialized parameters to their defaults * * Also runs any needed checks on the parameters provided by the user. */ static void complete_parameters(void) { u64 main_blkcnt_limit, tier2_blkcnt_limit; if (!param->blocksize) param->blocksize = APFS_NX_DEFAULT_BLOCK_SIZE; main_blkcnt_limit = get_main_device_size(param->blocksize); tier2_blkcnt_limit = get_tier2_device_size(param->blocksize); if (!param->main_blkcnt) param->main_blkcnt = main_blkcnt_limit; if (!param->tier2_blkcnt) param->tier2_blkcnt = tier2_blkcnt_limit; if (param->main_blkcnt > main_blkcnt_limit) fatal("main device is not big enough"); if (param->tier2_blkcnt > tier2_blkcnt_limit) fatal("tier 2 device is not big enough"); param->block_count = param->main_blkcnt + param->tier2_blkcnt; if (param->main_blkcnt * param->blocksize < 512 * 1024) fatal("such tiny containers are not supported"); if (param->tier2_blkcnt && param->tier2_blkcnt * param->blocksize < 512 * 1024) { /* TODO: is this really a problem for tier 2? */ fatal("tier 2 is too small"); } /* Every volume must have a label; use the same default as Apple */ if (!param->label || !*param->label) param->label = "untitled"; /* Make sure the volume label fits, along with its null termination */ if (strlen(param->label) + 1 > APFS_VOLNAME_LEN) fatal("volume label is too long"); if (!param->main_uuid) param->main_uuid = get_random_uuid(); if (!param->vol_uuid) param->vol_uuid = get_random_uuid(); if (fd_tier2 != -1) param->fusion_uuid = get_random_uuid(); } int main(int argc, char *argv[]) { char *filename; progname = argv[0]; param = calloc(1, sizeof(*param)); if (!param) system_error(); while (1) { int opt = getopt(argc, argv, "L:U:u:szvF:B:"); if (opt == -1) break; switch (opt) { case 'L': param->label = optarg; break; case 'U': param->main_uuid = optarg; break; case 'u': param->vol_uuid = optarg; break; case 's': param->case_sensitive = true; break; case 'z': param->norm_sensitive = true; break; case 'v': version(); case 'F': fd_tier2 = open(optarg, O_RDWR); if (fd_tier2 == -1) system_error(); break; case 'B': /* TODO: reject malformed numbers */ param->tier2_blkcnt = atoll(optarg); break; default: usage(); } } if (optind == argc - 2) { filename = argv[optind]; /* TODO: reject malformed numbers? */ param->main_blkcnt = atoll(argv[optind + 1]); } else if (optind == argc - 1) { filename = argv[optind]; } else { usage(); } fd_main = open(filename, O_RDWR); if (fd_main == -1) system_error(); complete_parameters(); make_container(); return 0; } apfsprogs-0.2.0/mkapfs/mkapfs.h000066400000000000000000000156061471277137200164530ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _MKAPFS_H #define _MKAPFS_H #include #include #include #include /* Filesystem parameters */ struct parameters { unsigned long blocksize; /* Block size */ u64 block_count; /* Number of blocks in the container */ u64 main_blkcnt; /* Number of blocks in the main device */ u64 tier2_blkcnt; /* Number of blocks in the tier 2 device */ char *label; /* Volume label */ char *main_uuid; /* Container UUID in standard format */ char *vol_uuid; /* Volume UUID in standard format */ char *fusion_uuid; /* Fusion drive UUID in standard format */ bool case_sensitive; /* Is the filesystem case-sensitive? */ bool norm_sensitive; /* Is it normalization-sensitive? */ }; /* Declarations for global variables */ extern struct parameters *param; /* Filesystem parameters */ extern int fd_main; /* File descriptor for the main device */ extern int fd_tier2; /* File descriptor for the tier 2 device, if any */ /* Hardcoded transaction ids */ #define MKFS_XID 1 /* Hardcoded object ids */ #define SPACEMAN_OID APFS_OID_RESERVED_COUNT #define REAPER_OID (SPACEMAN_OID + 1) #define FIRST_VOL_OID (REAPER_OID + 1) #define FIRST_VOL_CAT_ROOT_OID (FIRST_VOL_OID + 1) #define IP_FREE_QUEUE_OID (FIRST_VOL_CAT_ROOT_OID + 1) #define MAIN_FREE_QUEUE_OID (IP_FREE_QUEUE_OID + 1) #define TIER2_FREE_QUEUE_OID (MAIN_FREE_QUEUE_OID + 1) #define FUSION_WBC_OID (TIER2_FREE_QUEUE_OID + 1) /** * cpoint_desc_blocks - Calculate the number of checkpoint descriptor blocks * * TODO: what if the tier 2 device is much bigger than the main one? */ static inline u32 cpoint_desc_blocks(void) { /* * These numbers are a rough approximation of what I learned from * testing newfs_apfs. I don't think the exact block counts matter. */ if (param->block_count < 512 * 1024 / 4) /* Up to 512M */ return 8; if (param->block_count < 1024 * 1024 / 4) /* Up to 1G */ return 12; if (param->block_count < 50 * 1024 * 1024 / 4) { /* Up to 50G */ u32 off_512M = (param->block_count - 1024 * 1024 / 4) / (512 * 1024 / 4); return 20 + 60 * off_512M / 23; } /* * From here on, newfs_apfs increases the number very slowly. I don't * think it matters... */ return 280; } /** * cpoint_data_blocks - Calculate the number of checkpoint data blocks * * TODO: what if the tier 2 device is much bigger than the main one? */ static inline u32 cpoint_data_blocks(void) { /* I got these numbers from testing newfs_apfs */ if (param->block_count < 4545) return 52; if (param->block_count < 13633) return 124; if (param->block_count < 36353) return 160 + 36 * ((param->block_count - 13633) / 4544); if (param->block_count < 131777) return 308 + 4 * ((param->block_count - 36353) / 4544); if (param->block_count < 262144) return 648 + 4 * ((param->block_count - 131777) / 4544); if (param->block_count == 262144) /* 1G is a special case, odd */ return 992; /* * At this point I got bored and the sizes stop matching exactly. The * official fsck doesn't care. TODO? */ if (param->block_count < 1048576) { /* Up to 4G */ u32 off_512M = (param->block_count - 262144) / 131072; return 1248 + 488 * off_512M + 4 * (((long long)param->block_count - (261280 + off_512M * 131776)) / 2272); } if (param->block_count < 4063232) { /* Up to 10G */ u32 off_512M = (param->block_count - 1048576) / 131072; return 4112 + 256 * off_512M; } if (param->block_count < 13107200) { /* Up to 50G */ u32 off_512M = (param->block_count - 4063232) / 131072; return 10000 + 256 * off_512M; } /* * From here on, newfs_apfs increases the number very slowly. I don't * think it matters... */ return 27672; } /* Description of the checkpoint areas */ #define CPOINT_DESC_BASE (APFS_NX_BLOCK_NUM + 1) #define CPOINT_DESC_BLOCKS cpoint_desc_blocks() #define CPOINT_DATA_BASE (CPOINT_DESC_BASE + CPOINT_DESC_BLOCKS) #define CPOINT_DATA_BLOCKS cpoint_data_blocks() #define CPOINT_END (CPOINT_DATA_BASE + CPOINT_DATA_BLOCKS) /* * Some block numbers for ephemeral objects will need to be calculated on * runtime, so that they can all be kept consecutive and in the right order. */ extern struct ephemeral_info { u64 reaper_bno; u64 spaceman_bno; u32 spaceman_sz; u32 spaceman_blkcnt; u64 ip_free_queue_bno; u64 main_free_queue_bno; u64 tier2_free_queue_bno; u64 fusion_wbc_bno; u32 total_blkcnt; } eph_info; /* Hardcoded block numbers calculated from the checkpoint areas */ #define CPOINT_MAP_BNO CPOINT_DESC_BASE #define CPOINT_SB_BNO (CPOINT_DESC_BASE + 1) #define MAIN_OMAP_BNO CPOINT_END #define MAIN_OMAP_ROOT_BNO (CPOINT_END + 1) #define FIRST_VOL_BNO (CPOINT_END + 2) #define FIRST_VOL_OMAP_BNO (CPOINT_END + 3) #define FIRST_VOL_OMAP_ROOT_BNO (CPOINT_END + 4) #define FIRST_VOL_CAT_ROOT_BNO (CPOINT_END + 5) #define FIRST_VOL_EXTREF_ROOT_BNO (CPOINT_END + 6) #define FIRST_VOL_SNAP_ROOT_BNO (CPOINT_END + 7) #define FUSION_MT_BNO (CPOINT_END + 8) /* Just a single block for now (TODO) */ #define FUSION_WBC_FIRST_BNO (CPOINT_END + 9) /* First ip bitmap comes last because the size is not hardcoded */ #define IP_BMAP_BASE (CPOINT_END + 10) extern __attribute__((noreturn)) void system_error(void); extern __attribute__((noreturn)) void fatal(const char *message); /* Forwards the mmap() call to the proper device of a fusion drive */ static inline void *apfs_mmap(void *addr, size_t length, int prot, int flags, u64 offset) { if (offset >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR) { if (fd_tier2 == -1) fatal("allocation attempted in missing tier 2 device."); offset -= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR; return mmap(addr, length, prot, flags, fd_tier2, (off_t)offset); } return mmap(addr, length, prot, flags, fd_main, (off_t)offset); } /** * get_zeroed_blocks - Map and zero contiguous filesystem blocks * @bno: first block number * @count: number of blocks * * Returns a pointer to the mapped area; the caller must unmap it after use. */ static inline void *get_zeroed_blocks(u64 bno, u64 count) { void *blocks; size_t size = param->blocksize * count; if (size / count != param->blocksize) fatal("overflow detected on disk area mapping"); blocks = apfs_mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, bno * param->blocksize); if (blocks == MAP_FAILED) system_error(); memset(blocks, 0, size); return blocks; } /** * get_zeroed_block - Map and zero a filesystem block * @bno: block number * * Returns a pointer to the mapped block; the caller must unmap it after use. */ static inline void *get_zeroed_block(u64 bno) { return get_zeroed_blocks(bno, 1); } /** * get_timestamp - Get the current time in nanoseconds * * Calls clock_gettime(), so may not work with old versions of glibc. */ static inline u64 get_timestamp(void) { struct timespec time; if (clock_gettime(CLOCK_REALTIME, &time)) system_error(); return (u64)time.tv_sec * NSEC_PER_SEC + time.tv_nsec; } #endif /* _MKAPFS_H */ apfsprogs-0.2.0/mkapfs/object.c000066400000000000000000000017441471277137200164310ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include "mkapfs.h" #include "object.h" /** * set_object_header - Set the header for a filesystem object * @obj: pointer to the on-disk object header * @size: size of the object (in bytes) * @oid: object id * @type: object type * @subtype: object subtype * * All other fields of the object headed by @obj must be set in advance by * the caller, otherwise the checksum won't be correct. */ void set_object_header(struct apfs_obj_phys *obj, u32 size, u64 oid, u32 type, u32 subtype) { char *after_cksum = (char *)obj + APFS_MAX_CKSUM_SIZE; int after_cksum_len = size - APFS_MAX_CKSUM_SIZE; obj->o_oid = cpu_to_le64(oid); obj->o_xid = cpu_to_le64(MKFS_XID); obj->o_type = cpu_to_le32(type); obj->o_subtype = cpu_to_le32(subtype); obj->o_cksum = cpu_to_le64(fletcher64(after_cksum, after_cksum_len)); } apfsprogs-0.2.0/mkapfs/object.h000066400000000000000000000004471471277137200164350ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _OBJECT_H #define _OBJECT_H #include struct apfs_obj_phys; extern void set_object_header(struct apfs_obj_phys *obj, u32 size, u64 oid, u32 type, u32 subtype); #endif /* _OBJECT_H */ apfsprogs-0.2.0/mkapfs/spaceman.c000066400000000000000000000470501471277137200167520ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include "btree.h" #include "mkapfs.h" #include "object.h" #include "spaceman.h" struct device_info { u64 block_count; u64 chunk_count; u32 cib_count; u32 cab_count; u32 cib_addr_base_off; /* Offset of the cib/cab address in spaceman */ u64 first_cib; /* Block number for first chunk-info block */ u64 first_cab; /* Block number for first cib address block */ u64 used_blocks_end; /* Block right after the last one we allocate */ u64 used_chunks_end; /* Chunk right after the last one we allocate */ u64 first_chunk_bmap; /* Block number for the first chunk's bitmap */ }; /* Extra information about the space manager */ static struct spaceman_info { struct device_info dev_info[APFS_SD_COUNT]; u64 total_chunk_count; u32 total_cib_count; u32 total_cab_count; u64 ip_blocks; u32 ip_bm_size; u32 ip_bmap_blocks; u64 ip_base; u32 bm_addr_off; /* Offset of bitmap address in the spaceman */ u32 bm_free_next_off; /* Offset of free_next in the spaceman */ } sm_info = {0}; /** * blocks_per_chunk - Calculate the number of blocks per chunk */ static inline u32 blocks_per_chunk(void) { return 8 * param->blocksize; /* One bitmap block for each chunk */ } /** * chunks_per_cib - Calculate the number of chunks per chunk-info block */ static inline u32 chunks_per_cib(void) { int chunk_info_size = sizeof(struct apfs_chunk_info); int cib_size = sizeof(struct apfs_chunk_info_block); return (param->blocksize - cib_size) / chunk_info_size; } /** * cibs_per_cab - Calculate the count of chunk-info blocks per cib address block */ static inline u32 cibs_per_cab(void) { int cab_size = sizeof(struct apfs_cib_addr_block); return (param->blocksize - cab_size) / sizeof(__le64); } /** * spaceman_size - Calculate the size of the spaceman object in bytes */ u32 spaceman_size(void) { struct device_info *main_dev = NULL, *tier2_dev = NULL; int entry_count; main_dev = &sm_info.dev_info[APFS_SD_MAIN]; tier2_dev = &sm_info.dev_info[APFS_SD_TIER2]; entry_count = 0; if (main_dev->cab_count > 1) entry_count += main_dev->cab_count; else entry_count += main_dev->cib_count; if (tier2_dev->cab_count > 1) entry_count += tier2_dev->cab_count; else entry_count += tier2_dev->cib_count; /* * The spaceman must have room for the addresses of all device cibs (or * cabs) for each of the devices. Some containers require extra blocks * to store this stuff. */ return DIV_ROUND_UP(entry_count * sizeof(__le64) + main_dev->cib_addr_base_off, param->blocksize) * param->blocksize; } #define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) /** * count_used_blocks_in_chunk - Calculate number of allocated blocks in a chunk * @dev: device for the chunk * @chunkno: chunk number to check */ static u32 count_used_blocks_in_chunk(struct device_info *dev, u64 chunkno) { u32 first_chunk_ip_blocks; if (chunkno >= dev->used_chunks_end) return 0; /* The tier 2 device only has a superblock */ if (dev->used_blocks_end == 1) return 1; /* The internal pool may not fit whole in the chunk */ first_chunk_ip_blocks = MIN(sm_info.ip_blocks, blocks_per_chunk() - sm_info.ip_base); if (chunkno == 0) { u32 blocks = 0; /* This stuff always goes in the first chunk */ blocks += 1; /* Block zero */ blocks += CPOINT_DESC_BLOCKS; /* Checkpoint descriptor blocks */ blocks += CPOINT_DATA_BLOCKS; /* Checkpoint data blocks */ blocks += 2; /* Container object map and its root */ blocks += 6; /* Volume superblock and its trees */ blocks += sm_info.ip_bmap_blocks; /* Internal pool bitmap blocks */ if (fd_tier2 != -1) blocks += 2; /* Fusion middle-tree and writeback cache */ blocks += first_chunk_ip_blocks; return blocks; } /* Later chunks are only needed for the rest of the internal pool */ if (chunkno != dev->used_chunks_end - 1) return blocks_per_chunk(); /* Last chunk */ return (sm_info.ip_blocks - first_chunk_ip_blocks) % blocks_per_chunk(); } /** * count_used_blocks - Count the blocks used by the mkfs in a device * @dev: the device */ static u32 count_used_blocks(struct device_info *dev) { u32 blocks = 0; u64 chunkno; for (chunkno = 0; chunkno < dev->used_chunks_end; ++chunkno) blocks += count_used_blocks_in_chunk(dev, chunkno); return blocks; } /** * bmap_mark_as_used - Mark a range as used in the allocation bitmap * @bitmap: allocation bitmap for the first chunk * @paddr: first block number * @length: block count */ static void bmap_mark_as_used(u64 *bitmap, u64 paddr, u64 length) { u64 *byte; u64 flag; u64 i; for (i = paddr; i < paddr + length; ++i) { byte = bitmap + i / 64; flag = 1ULL << i % 64; *byte |= flag; } } /** * make_main_alloc_bitmap - Make the allocation bitmap for the main device */ static void make_main_alloc_bitmap(void) { struct device_info *dev = NULL; void *bmap = NULL; dev = &sm_info.dev_info[APFS_SD_MAIN]; bmap = get_zeroed_blocks(dev->first_chunk_bmap, dev->used_chunks_end); /* Block zero */ bmap_mark_as_used(bmap, 0, 1); /* Checkpoint descriptor blocks */ bmap_mark_as_used(bmap, CPOINT_DESC_BASE, CPOINT_DESC_BLOCKS); /* Checkpoint data blocks */ bmap_mark_as_used(bmap, CPOINT_DATA_BASE, CPOINT_DATA_BLOCKS); /* Container object map and its root */ bmap_mark_as_used(bmap, MAIN_OMAP_BNO, 2); /* Volume superblock and its trees */ bmap_mark_as_used(bmap, FIRST_VOL_BNO, 6); /* Internal pool bitmap blocks */ bmap_mark_as_used(bmap, IP_BMAP_BASE, sm_info.ip_bmap_blocks); /* Internal pool blocks */ bmap_mark_as_used(bmap, sm_info.ip_base, sm_info.ip_blocks); /* Fusion drive stuff */ if (fd_tier2 != -1) { bmap_mark_as_used(bmap, FUSION_MT_BNO, 1); bmap_mark_as_used(bmap, FUSION_WBC_FIRST_BNO, 1); } munmap(bmap, dev->used_chunks_end * param->blocksize); } /** * make_tier2_alloc_bitmap - Make the allocation bitmap for the tier 2 device */ static void make_tier2_alloc_bitmap(void) { struct device_info *dev = NULL; void *bmap = NULL; dev = &sm_info.dev_info[APFS_SD_TIER2]; bmap = get_zeroed_blocks(dev->first_chunk_bmap, dev->used_chunks_end); /* Block zero */ bmap_mark_as_used(bmap, 0, 1); munmap(bmap, dev->used_chunks_end * param->blocksize); } /* * Offsets into the spaceman block for a non-versioned container; the values * have been borrowed from a test image. */ #define BITMAP_XID_OFF 0x150 /* Transaction id for the ip bitmap */ /** * make_chunk_info - Write a chunk info structure * @dev: device getting made * @chunk: pointer to the raw chunk info structure * @start: first block number for the chunk * * Returns the first block number for the next chunk. */ static u64 make_chunk_info(struct device_info *dev, struct apfs_chunk_info *chunk, u64 start) { u64 remaining_blocks = dev->block_count - start; u64 chunkno = start / blocks_per_chunk(); u32 block_count, free_count; chunk->ci_xid = cpu_to_le64(MKFS_XID); chunk->ci_addr = cpu_to_le64(start); /* Later chunks are just holes */ if (start < dev->used_blocks_end) chunk->ci_bitmap_addr = cpu_to_le64(dev->first_chunk_bmap + chunkno); block_count = blocks_per_chunk(); if (remaining_blocks < block_count) /* This is the final chunk */ block_count = remaining_blocks; chunk->ci_block_count = cpu_to_le32(block_count); free_count = block_count - count_used_blocks_in_chunk(dev, chunkno); chunk->ci_free_count = cpu_to_le32(free_count); start += block_count; return start; } /** * make_chunk_info_block - Make a chunk-info block * @dev: device getting made * @bno: block number for the chunk-info block * @index: index of the chunk-info block * @start: first block number for the first chunk * * Returns the first block number for the first chunk of the next cib. */ static u64 make_chunk_info_block(struct device_info *dev, u64 bno, int index, u64 start) { struct apfs_chunk_info_block *cib = get_zeroed_block(bno); int i; cib->cib_index = cpu_to_le32(index); for (i = 0; i < chunks_per_cib(); ++i) { if (start == dev->block_count) /* No more chunks in device */ break; start = make_chunk_info(dev, &cib->cib_chunk_info[i], start); } cib->cib_chunk_info_count = cpu_to_le32(i); set_object_header(&cib->cib_o, param->blocksize, bno, APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_SPACEMAN_CIB, APFS_OBJECT_TYPE_INVALID); munmap(cib, param->blocksize); return start; } /** * make_cib_addr_block - Make a cib address block * @dev: device getting made * @bno: block number for the chunk-info block * @index: index of the chunk-info block * @start: first block number for the first chunk * * Returns the first block number for the first chunk of the next cib. */ static u64 make_cib_addr_block(struct device_info *dev, u64 bno, int index, u64 start) { struct apfs_cib_addr_block *cab = get_zeroed_block(bno); int i; cab->cab_index = cpu_to_le32(index); for (i = 0; i < cibs_per_cab(); ++i) { int cib_index; u64 cib_bno; if (start == dev->block_count) /* No more chunks in device */ break; cib_index = cibs_per_cab() * index + i; cib_bno = dev->first_cib + cib_index; cab->cab_cib_addr[i] = cpu_to_le64(cib_bno); start = make_chunk_info_block(dev, cib_bno, cib_index, start); } cab->cab_cib_count = cpu_to_le32(i); set_object_header(&cab->cab_o, param->blocksize, bno, APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_SPACEMAN_CAB, APFS_OBJECT_TYPE_INVALID); munmap(cab, param->blocksize); return start; } /** * make_single_device - Make a spaceman device structure * @sm: pointer to the on-disk spaceman structure * @which: device to make */ static void make_single_device(struct apfs_spaceman_phys *sm, enum smdev which) { struct apfs_spaceman_device *dev = NULL; struct device_info *devinfo = NULL; u64 start = 0; int i; dev = &sm->sm_dev[which]; devinfo = &sm_info.dev_info[which]; dev->sm_block_count = cpu_to_le64(devinfo->block_count); dev->sm_chunk_count = cpu_to_le64(devinfo->chunk_count); dev->sm_cib_count = cpu_to_le32(devinfo->cib_count); dev->sm_cab_count = cpu_to_le32(devinfo->cab_count); dev->sm_free_count = cpu_to_le64(devinfo->block_count - count_used_blocks(devinfo)); dev->sm_addr_offset = cpu_to_le32(devinfo->cib_addr_base_off); if (!devinfo->cab_count) { __le64 *cib_addr = (void *)sm + devinfo->cib_addr_base_off; for (i = 0; i < devinfo->cib_count; ++i) { cib_addr[i] = cpu_to_le64(devinfo->first_cib + i); start = make_chunk_info_block(devinfo, devinfo->first_cib + i, i, start); } } else { __le64 *cab_addr = (void *)sm + devinfo->cib_addr_base_off; for (i = 0; i < devinfo->cab_count; ++i) { cab_addr[i] = cpu_to_le64(devinfo->first_cab + i); start = make_cib_addr_block(devinfo, devinfo->first_cab + i, i, start); } } } /** * make_devices - Make the spaceman device structures * @sm: pointer to the on-disk spaceman structure */ static void make_devices(struct apfs_spaceman_phys *sm) { make_single_device(sm, APFS_SD_MAIN); make_single_device(sm, APFS_SD_TIER2); } /** * make_ip_free_queue - Make an empty free queue for the internal pool * @fq: free queue structure */ static void make_ip_free_queue(struct apfs_spaceman_free_queue *fq) { fq->sfq_tree_oid = cpu_to_le64(IP_FREE_QUEUE_OID); make_empty_btree_root(eph_info.ip_free_queue_bno, IP_FREE_QUEUE_OID, APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); fq->sfq_oldest_xid = 0; fq->sfq_tree_node_limit = cpu_to_le16(ip_fq_node_limit(sm_info.total_chunk_count)); } /** * make_main_free_queue - Make an empty free queue for the main device * @fq: free queue structure */ static void make_main_free_queue(struct apfs_spaceman_free_queue *fq) { fq->sfq_tree_oid = cpu_to_le64(MAIN_FREE_QUEUE_OID); make_empty_btree_root(eph_info.main_free_queue_bno, MAIN_FREE_QUEUE_OID, APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); fq->sfq_oldest_xid = 0; fq->sfq_tree_node_limit = cpu_to_le16(main_fq_node_limit(param->main_blkcnt)); } /** * make_tier2_free_queue - Make an empty free queue for the tier 2 device * @fq: free queue structure */ static void make_tier2_free_queue(struct apfs_spaceman_free_queue *fq) { fq->sfq_tree_oid = cpu_to_le64(TIER2_FREE_QUEUE_OID); make_empty_btree_root(eph_info.tier2_free_queue_bno, TIER2_FREE_QUEUE_OID, APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); fq->sfq_oldest_xid = 0; fq->sfq_tree_node_limit = cpu_to_le16(main_fq_node_limit(param->tier2_blkcnt)); } /** * make_ip_bitmap - Make the allocation bitmap for the internal pool */ static void make_ip_bitmap(void) { void *bmap = get_zeroed_blocks(IP_BMAP_BASE, sm_info.ip_bm_size); struct device_info *main_dev = NULL, *tier2_dev = NULL; main_dev = &sm_info.dev_info[APFS_SD_MAIN]; tier2_dev = &sm_info.dev_info[APFS_SD_TIER2]; /* Cib address blocks */ bmap_mark_as_used(bmap, main_dev->first_cab - sm_info.ip_base, main_dev->cab_count); bmap_mark_as_used(bmap, tier2_dev->first_cab - sm_info.ip_base, tier2_dev->cab_count); /* Chunk-info blocks */ bmap_mark_as_used(bmap, main_dev->first_cib - sm_info.ip_base, main_dev->cib_count); bmap_mark_as_used(bmap, tier2_dev->first_cib - sm_info.ip_base, tier2_dev->cib_count); /* Allocation bitmap block */ bmap_mark_as_used(bmap, main_dev->first_chunk_bmap - sm_info.ip_base, main_dev->used_chunks_end); bmap_mark_as_used(bmap, tier2_dev->first_chunk_bmap - sm_info.ip_base, tier2_dev->used_chunks_end); munmap(bmap, param->blocksize); } /** * make_ip_bm_free_next - Set up the free_next list for the internal pool * @addr: pointer to the beginning of the field */ static void make_ip_bm_free_next(__le16 *addr) { int i; /* * Free ip bitmap blocks are kept in a linked list. For the mkfs this * just means that they get marked with numbers that are one above * their index, except for the tail block which gets the invalid index * 0xFFFF. Blocks in use are not part of the list, so they also get * 0xFFFF. */ for (i = 0; i < sm_info.ip_bm_size; ++i) addr[i] = cpu_to_le16(APFS_SPACEMAN_IP_BM_INDEX_INVALID); for (i = sm_info.ip_bm_size; i < sm_info.ip_bmap_blocks - 1; i++) addr[i] = cpu_to_le16(i + 1); addr[sm_info.ip_bmap_blocks - 1] = cpu_to_le16(APFS_SPACEMAN_IP_BM_INDEX_INVALID); } /** * make_internal_pool - Make the internal pool of the space manager * @sm: pointer to the on-disk spaceman structure */ static void make_internal_pool(struct apfs_spaceman_phys *sm) { int i; __le64 *addr; __le16 *bm_off_addr; sm->sm_ip_bm_tx_multiplier = cpu_to_le32(APFS_SPACEMAN_IP_BM_TX_MULTIPLIER); sm->sm_ip_block_count = cpu_to_le64(sm_info.ip_blocks); sm->sm_ip_base = cpu_to_le64(sm_info.ip_base); sm->sm_ip_bm_size_in_blocks = cpu_to_le32(sm_info.ip_bm_size); sm->sm_ip_bm_block_count = cpu_to_le32(sm_info.ip_bmap_blocks); sm->sm_ip_bm_base = cpu_to_le64(IP_BMAP_BASE); for (i = 0; i < sm_info.ip_bmap_blocks; ++i) munmap(get_zeroed_block(IP_BMAP_BASE + i), param->blocksize); /* The current bitmaps are the first in the ring */ sm->sm_ip_bitmap_offset = cpu_to_le32(sm_info.bm_addr_off); bm_off_addr = (void *)sm + sm_info.bm_addr_off; for (i = 0; i < sm_info.ip_bm_size; ++i) bm_off_addr[i] = cpu_to_le16(i); sm->sm_ip_bm_free_head = cpu_to_le16(sm_info.ip_bm_size); sm->sm_ip_bm_free_tail = cpu_to_le16(sm_info.ip_bmap_blocks - 1); sm->sm_ip_bm_xid_offset = cpu_to_le32(BITMAP_XID_OFF); addr = (void *)sm + BITMAP_XID_OFF; for (i = 0; i < sm_info.ip_bm_size; ++i) addr[i] = cpu_to_le64(MKFS_XID); sm->sm_ip_bm_free_next_offset = cpu_to_le32(sm_info.bm_free_next_off); make_ip_bm_free_next((void *)sm + sm_info.bm_free_next_off); make_ip_bitmap(); } /** * calculate_dev_info - Precalculate chunk/cib/cab counts for a device * @dev: device info to set * @which: which device is this? */ static void calculate_dev_info(struct device_info *dev, enum smdev which) { dev->block_count = which == APFS_SD_MAIN ? param->main_blkcnt : param->tier2_blkcnt; dev->chunk_count = DIV_ROUND_UP(dev->block_count, blocks_per_chunk()); dev->cib_count = DIV_ROUND_UP(dev->chunk_count, chunks_per_cib()); dev->cab_count = DIV_ROUND_UP(dev->cib_count, cibs_per_cab()); if (dev->cab_count == 1) dev->cab_count = 0; /* Put some limit on cab count to avoid overflow issues */ if (dev->cab_count > 1000) fatal("device is too big"); } /** * set_spaceman_info - Calculate the value of all fields of sm_info */ void set_spaceman_info(void) { struct device_info *main_dev = NULL, *tier2_dev = NULL; main_dev = &sm_info.dev_info[APFS_SD_MAIN]; tier2_dev = &sm_info.dev_info[APFS_SD_TIER2]; calculate_dev_info(main_dev, APFS_SD_MAIN); calculate_dev_info(tier2_dev, APFS_SD_TIER2); sm_info.total_chunk_count = main_dev->chunk_count + tier2_dev->chunk_count; sm_info.total_cib_count = main_dev->cib_count + tier2_dev->cib_count; sm_info.total_cab_count = main_dev->cab_count + tier2_dev->cab_count; sm_info.ip_blocks = (sm_info.total_chunk_count + sm_info.total_cib_count + sm_info.total_cab_count) * 3; /* Just a rough limit in case tier 2 is huge */ if (sm_info.ip_blocks > param->main_blkcnt / 2) fatal("internal pool too big for the main device"); /* * We have 16 ip bitmaps; each of them maps the whole ip and may span * multiple blocks. */ sm_info.ip_bm_size = DIV_ROUND_UP(sm_info.ip_blocks, blocks_per_chunk()); sm_info.ip_bmap_blocks = 16 * sm_info.ip_bm_size; sm_info.ip_base = IP_BMAP_BASE + sm_info.ip_bmap_blocks; /* We have one xid for each of the ip bitmaps */ sm_info.bm_addr_off = BITMAP_XID_OFF + sizeof(__le64) * sm_info.ip_bm_size; sm_info.bm_free_next_off = sm_info.bm_addr_off + ROUND_UP(sizeof(__le16) * sm_info.ip_bm_size, sizeof(__le64)); main_dev->cib_addr_base_off = sm_info.bm_free_next_off + sm_info.ip_bmap_blocks * sizeof(__le16); if (main_dev->cab_count) tier2_dev->cib_addr_base_off = main_dev->cib_addr_base_off + main_dev->cab_count * sizeof(__le64); else tier2_dev->cib_addr_base_off = main_dev->cib_addr_base_off + main_dev->cib_count * sizeof(__le64); /* Only the ip size matters, all other used blocks come before it */ main_dev->used_blocks_end = sm_info.ip_base + sm_info.ip_blocks; main_dev->used_chunks_end = DIV_ROUND_UP(main_dev->used_blocks_end, blocks_per_chunk()); /* Tier 2 is empty except for block zero */ tier2_dev->used_blocks_end = fd_tier2 != -1 ? 1 : 0; tier2_dev->used_chunks_end = fd_tier2 != -1 ? 1 : 0; /* * Put the chunk bitmaps at the beginning of the internal pool, and * the cibs right after them, followed by the cabs if any. Then the * same for the tier 2 device, if it exists. */ main_dev->first_chunk_bmap = sm_info.ip_base; main_dev->first_cib = main_dev->first_chunk_bmap + main_dev->used_chunks_end; main_dev->first_cab = main_dev->first_cib + main_dev->cib_count; tier2_dev->first_chunk_bmap = main_dev->first_cab + main_dev->cab_count; tier2_dev->first_cib = tier2_dev->first_chunk_bmap + tier2_dev->used_chunks_end; tier2_dev->first_cab = tier2_dev->first_cib + tier2_dev->cib_count; } /** * make_spaceman - Make the space manager for the container * @bno: block number to use * @oid: object id */ void make_spaceman(u64 bno, u64 oid) { struct apfs_spaceman_phys *sm = NULL; sm = get_zeroed_blocks(bno, spaceman_size() / param->blocksize); sm->sm_block_size = cpu_to_le32(param->blocksize); sm->sm_blocks_per_chunk = cpu_to_le32(blocks_per_chunk()); sm->sm_chunks_per_cib = cpu_to_le32(chunks_per_cib()); sm->sm_cibs_per_cab = cpu_to_le32(cibs_per_cab()); make_devices(sm); make_ip_free_queue(&sm->sm_fq[APFS_SFQ_IP]); make_main_free_queue(&sm->sm_fq[APFS_SFQ_MAIN]); if (fd_tier2 != -1) make_tier2_free_queue(&sm->sm_fq[APFS_SFQ_TIER2]); make_internal_pool(sm); make_main_alloc_bitmap(); if (fd_tier2 != -1) make_tier2_alloc_bitmap(); set_object_header(&sm->sm_o, spaceman_size(), oid, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_SPACEMAN, APFS_OBJECT_TYPE_INVALID); munmap(sm, spaceman_size()); } apfsprogs-0.2.0/mkapfs/spaceman.h000066400000000000000000000004131471277137200167470ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _SPACEMAN_H #define _SPACEMAN_H extern u32 spaceman_size(void); extern void set_spaceman_info(void); extern void make_spaceman(u64 bno, u64 oid); #endif /* _SPACEMAN_H */ apfsprogs-0.2.0/mkapfs/super.c000066400000000000000000000362571471277137200163300ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include "btree.h" #include "mkapfs.h" #include "object.h" #include "spaceman.h" #include "super.h" #include "version.h" struct ephemeral_info eph_info = {0}; /* String to identify the program and its version */ #define MKFS_ID_STRING "mkapfs by eafer (" GIT_COMMIT ")" /** * set_uuid - Set a UUID field * @field: on-disk field to set * @uuid: pointer to the UUID string in standard format */ static void set_uuid(char *field, char *uuid) { int ret; char *stdformat = "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-" "%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx"; ret = sscanf(uuid, stdformat, &field[0], &field[1], &field[2], &field[3], &field[4], &field[5], &field[6], &field[7], &field[8], &field[9], &field[10], &field[11], &field[12], &field[13], &field[14], &field[15]); if (ret == 16) return; fatal("please provide a UUID in standard format"); } /** * zero_area - Wipe an area on disk * @start: first block of the area * @blocks: block count for the area */ static void zero_area(u64 start, u64 blocks) { u64 bno; void *block; for (bno = start; bno < start + blocks; ++bno) { block = get_zeroed_block(bno); munmap(block, param->blocksize); } } /** * set_checkpoint_areas - Set all sb fields describing the checkpoint areas * @sb: pointer to the superblock copy on disk */ static void set_checkpoint_areas(struct apfs_nx_superblock *sb) { /* First set the checkpoint descriptor area fields */ sb->nx_xp_desc_base = cpu_to_le64(CPOINT_DESC_BASE); sb->nx_xp_desc_blocks = cpu_to_le32(CPOINT_DESC_BLOCKS); /* The first two blocks hold the superblock and the mappings */ sb->nx_xp_desc_len = cpu_to_le32(2); sb->nx_xp_desc_next = cpu_to_le32(2); sb->nx_xp_desc_index = 0; /* Now set the checkpoint data area fields */ sb->nx_xp_data_base = cpu_to_le64(CPOINT_DATA_BASE); sb->nx_xp_data_blocks = cpu_to_le32(CPOINT_DATA_BLOCKS); /* * Room for the space manager, the two free queues and the reaper. Also * for the fusion writeback cache and the tier 2 free queue, which * usually don't exist. */ sb->nx_xp_data_len = cpu_to_le32(eph_info.total_blkcnt); sb->nx_xp_data_next = cpu_to_le32(eph_info.total_blkcnt); sb->nx_xp_data_index = 0; } /** * get_max_volumes - Calculate the maximum number of volumes for the container * @size: the container size, in bytes */ static u32 get_max_volumes(u64 size) { u32 max_vols; /* Divide by 512 MiB and round up, as the reference requires */ max_vols = DIV_ROUND_UP(size, 512 * 1024 * 1024); if (max_vols > APFS_NX_MAX_FILE_SYSTEMS) max_vols = APFS_NX_MAX_FILE_SYSTEMS; return max_vols; } /** * set_ephemeral_info - Set the container's array of ephemeral info * @info: pointer to the nx_ephemeral_info array on the container superblock */ static void set_ephemeral_info(__le64 *info) { u64 container_size; u64 min_block_count; container_size = param->block_count * param->blocksize; if (container_size < 128 * 1024 * 1024) min_block_count = main_fq_node_limit(param->block_count); else min_block_count = APFS_NX_EPH_MIN_BLOCK_COUNT; /* Only the first entry is documented, leave the others as zero */ *info = cpu_to_le64((min_block_count << 32) | (APFS_NX_MAX_FILE_SYSTEM_EPH_STRUCTS << 16) | APFS_NX_EPH_INFO_VERSION_1); } /** * set_meta_crypto - Set a volume's meta_crypto field * @wmcs: the structure to set */ static void set_meta_crypto(struct apfs_wrapped_meta_crypto_state *wmcs) { wmcs->major_version = cpu_to_le16(APFS_WMCS_MAJOR_VERSION); wmcs->minor_version = cpu_to_le16(APFS_WMCS_MINOR_VERSION); wmcs->cpflags = 0; /* No flags exist yet */ wmcs->persistent_class = cpu_to_le32(APFS_PROTECTION_CLASS_F); wmcs->key_os_version = 0; /* Not sure what to do with this on Linux */ wmcs->key_revision = cpu_to_le16(1); } /** * make_volume - Make a volume * @bno: block number for the volume superblock * @oid: object id for the volume superblock */ static void make_volume(u64 bno, u64 oid) { struct apfs_superblock *vsb = get_zeroed_block(bno); vsb->apfs_magic = cpu_to_le32(APFS_MAGIC); vsb->apfs_features = cpu_to_le64(APFS_FEATURE_HARDLINK_MAP_RECORDS); if (param->norm_sensitive) vsb->apfs_incompatible_features = 0; else if (param->case_sensitive) vsb->apfs_incompatible_features = cpu_to_le64(APFS_INCOMPAT_NORMALIZATION_INSENSITIVE); else vsb->apfs_incompatible_features = cpu_to_le64(APFS_INCOMPAT_CASE_INSENSITIVE); /* Encryption is not supported, but this still needs to be set */ set_meta_crypto(&vsb->apfs_meta_crypto); /* We won't create any user inodes */ vsb->apfs_next_obj_id = cpu_to_le64(APFS_MIN_USER_INO_NUM); set_uuid(vsb->apfs_vol_uuid, param->vol_uuid); vsb->apfs_fs_flags = cpu_to_le64(APFS_FS_UNENCRYPTED); assert(strlen(MKFS_ID_STRING) < sizeof(vsb->apfs_formatted_by.id)); strcpy((char *)vsb->apfs_formatted_by.id, MKFS_ID_STRING); vsb->apfs_formatted_by.timestamp = cpu_to_le64(get_timestamp()); vsb->apfs_formatted_by.last_xid = cpu_to_le64(MKFS_XID); strcpy((char *)vsb->apfs_volname, param->label); vsb->apfs_next_doc_id = cpu_to_le32(APFS_MIN_DOC_ID); vsb->apfs_root_tree_type = cpu_to_le32(APFS_OBJ_VIRTUAL | APFS_OBJECT_TYPE_BTREE); vsb->apfs_extentref_tree_type = cpu_to_le32(APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE); vsb->apfs_snap_meta_tree_type = cpu_to_le32(APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_BTREE); vsb->apfs_omap_oid = cpu_to_le64(FIRST_VOL_OMAP_BNO); make_omap_btree(FIRST_VOL_OMAP_BNO, true /* is_vol */); vsb->apfs_root_tree_oid = cpu_to_le64(FIRST_VOL_CAT_ROOT_OID); make_cat_root(FIRST_VOL_CAT_ROOT_BNO, FIRST_VOL_CAT_ROOT_OID); /* The snapshot metadata and extent reference trees are empty */ vsb->apfs_extentref_tree_oid = cpu_to_le64(FIRST_VOL_EXTREF_ROOT_BNO); make_empty_btree_root(FIRST_VOL_EXTREF_ROOT_BNO, FIRST_VOL_EXTREF_ROOT_BNO, APFS_OBJECT_TYPE_BLOCKREFTREE); vsb->apfs_snap_meta_tree_oid = cpu_to_le64(FIRST_VOL_SNAP_ROOT_BNO); make_empty_btree_root(FIRST_VOL_SNAP_ROOT_BNO, FIRST_VOL_SNAP_ROOT_BNO, APFS_OBJECT_TYPE_SNAPMETATREE); /* The root nodes of all four trees, plus the object map structure */ vsb->apfs_fs_alloc_count = cpu_to_le64(5); set_object_header(&vsb->apfs_o, param->blocksize, oid, APFS_OBJ_VIRTUAL | APFS_OBJECT_TYPE_FS, APFS_OBJECT_TYPE_INVALID); munmap(vsb, param->blocksize); } /** * make_cpoint_map_block - Make the mapping block for the one checkpoint * @bno: block number to use */ static void make_cpoint_map_block(u64 bno) { struct apfs_checkpoint_map_phys *block = get_zeroed_block(bno); struct apfs_checkpoint_mapping *map; int idx = 0; block->cpm_flags = cpu_to_le32(APFS_CHECKPOINT_MAP_LAST); /* Set the checkpoint mapping for the reaper */ map = &block->cpm_map[idx++]; map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_REAPER); map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_INVALID); map->cpm_size = cpu_to_le32(param->blocksize); map->cpm_oid = cpu_to_le64(REAPER_OID); map->cpm_paddr = cpu_to_le64(eph_info.reaper_bno); /* Set the checkpoint mapping for the space manager */ map = &block->cpm_map[idx++]; map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_SPACEMAN); map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_INVALID); map->cpm_size = cpu_to_le32(eph_info.spaceman_sz); map->cpm_oid = cpu_to_le64(SPACEMAN_OID); map->cpm_paddr = cpu_to_le64(eph_info.spaceman_bno); /* Set the checkpoint mapping for the internal-pool free queue root */ map = &block->cpm_map[idx++]; map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_BTREE); map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); map->cpm_size = cpu_to_le32(param->blocksize); map->cpm_oid = cpu_to_le64(IP_FREE_QUEUE_OID); map->cpm_paddr = cpu_to_le64(eph_info.ip_free_queue_bno); /* Set the checkpoint mapping for the main device free queue root */ map = &block->cpm_map[idx++]; map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_BTREE); map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); map->cpm_size = cpu_to_le32(param->blocksize); map->cpm_oid = cpu_to_le64(MAIN_FREE_QUEUE_OID); map->cpm_paddr = cpu_to_le64(eph_info.main_free_queue_bno); if (fd_tier2 != -1) { /* Set the checkpoint mapping for the tier 2 free queue root */ map = &block->cpm_map[idx++]; map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_BTREE); map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); map->cpm_size = cpu_to_le32(param->blocksize); map->cpm_oid = cpu_to_le64(TIER2_FREE_QUEUE_OID); map->cpm_paddr = cpu_to_le64(eph_info.tier2_free_queue_bno); /* Set the checkpoint mapping for the fusion wb cache state */ map = &block->cpm_map[idx++]; map->cpm_type = cpu_to_le32(APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_FUSION_WBC); map->cpm_subtype = cpu_to_le32(APFS_OBJECT_TYPE_INVALID); map->cpm_size = cpu_to_le32(param->blocksize); map->cpm_oid = cpu_to_le64(FUSION_WBC_OID); map->cpm_paddr = cpu_to_le64(eph_info.fusion_wbc_bno); } block->cpm_count = cpu_to_le32(idx); set_object_header(&block->cpm_o, param->blocksize, bno, APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_CHECKPOINT_MAP, APFS_OBJECT_TYPE_INVALID); munmap(block, param->blocksize); } /** * make_cpoint_superblock - Make the one checkpoint superblock * @bno: block number to use * @sb_copy: copy of the superblock at block zero * * For now just copies @sb_copy into @bno. TODO: figure out what to do with * the nx_counters array. */ static void make_cpoint_superblock(u64 bno, struct apfs_nx_superblock *sb_copy) { struct apfs_nx_superblock *sb = get_zeroed_block(bno); memcpy(sb, sb_copy, sizeof(*sb)); munmap(sb, param->blocksize); } /** * make_tier2_superblock - Make the superblock copy in the tier 2 device * @sb: copy of the superblock at block zero of the main device */ static void make_tier2_superblock(struct apfs_nx_superblock *sb) { struct apfs_nx_superblock *tier2_sb = NULL; tier2_sb = get_zeroed_block(APFS_FUSION_TIER2_DEVICE_BYTE_ADDR / param->blocksize); memcpy(tier2_sb, sb, sizeof(*sb)); /* The top bit is used to tell apart the main and tier 2 devices */ tier2_sb->nx_fusion_uuid[15] |= 0x01; /* Update the checksum */ set_object_header(&tier2_sb->nx_o, param->blocksize, APFS_OID_NX_SUPERBLOCK, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_SUPERBLOCK, APFS_OBJECT_TYPE_INVALID); munmap(tier2_sb, param->blocksize); } /** * make_empty_reaper - Make an empty reaper * @bno: block number to use * @oid: object id */ static void make_empty_reaper(u64 bno, u64 oid) { struct apfs_nx_reaper_phys *reaper = get_zeroed_block(bno); reaper->nr_next_reap_id = cpu_to_le64(1); reaper->nr_flags = cpu_to_le32(APFS_NR_BHM_FLAG); reaper->nr_state_buffer_size = cpu_to_le32(param->blocksize - sizeof(*reaper)); set_object_header(&reaper->nr_o, param->blocksize, oid, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_REAPER, APFS_OBJECT_TYPE_INVALID); munmap(reaper, param->blocksize); } /** * make_empty_fusion_wbc_state - Make the state struct for en empty fusion wbc * @bno: block number to use * @oid: object id */ static void make_empty_fusion_wbc_state(u64 bno, u64 oid) { struct apfs_fusion_wbc_phys *wbc = get_zeroed_block(bno); wbc->fwp_version = cpu_to_le64(0x70); set_object_header(&wbc->fwp_objHdr, param->blocksize, oid, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_FUSION_WBC, APFS_OBJECT_TYPE_INVALID); munmap(wbc, param->blocksize); } /** * set_ephemeral_bnos - Decide the block numbers for the ephemeral objects */ static void set_ephemeral_bnos(void) { eph_info.reaper_bno = CPOINT_DATA_BASE; eph_info.spaceman_bno = eph_info.reaper_bno + 1; eph_info.spaceman_sz = spaceman_size(); eph_info.spaceman_blkcnt = eph_info.spaceman_sz / param->blocksize; eph_info.ip_free_queue_bno = eph_info.spaceman_bno + eph_info.spaceman_blkcnt; eph_info.main_free_queue_bno = eph_info.ip_free_queue_bno + 1; eph_info.total_blkcnt = eph_info.main_free_queue_bno - eph_info.reaper_bno + 1; if (fd_tier2 != -1) { eph_info.tier2_free_queue_bno = eph_info.main_free_queue_bno + 1; eph_info.fusion_wbc_bno = eph_info.tier2_free_queue_bno + 1; eph_info.total_blkcnt += 2; } /* Just a rough limit in case tier 2 is huge */ if (eph_info.total_blkcnt >= CPOINT_DATA_BLOCKS / 4) fatal("space manager too big for the main device"); } /** * make_container - Make the whole filesystem */ void make_container(void) { struct apfs_nx_superblock *sb_copy = NULL; u64 size = param->blocksize * param->block_count; sb_copy = get_zeroed_block(APFS_NX_BLOCK_NUM); sb_copy->nx_magic = cpu_to_le32(APFS_NX_MAGIC); sb_copy->nx_block_size = cpu_to_le32(param->blocksize); sb_copy->nx_block_count = cpu_to_le64(param->block_count); /* We only support version 2 of APFS */ sb_copy->nx_incompatible_features |= cpu_to_le64(APFS_NX_INCOMPAT_VERSION2); if (param->tier2_blkcnt) sb_copy->nx_incompatible_features |= cpu_to_le64(APFS_NX_INCOMPAT_FUSION); set_uuid(sb_copy->nx_uuid, param->main_uuid); if (param->tier2_blkcnt) { set_uuid(sb_copy->nx_fusion_uuid, param->fusion_uuid); /* This bit will get flipped for the tier 2 superblock */ sb_copy->nx_fusion_uuid[15] &= ~0x01; } /* Leave some room for the objects created by the mkfs */ sb_copy->nx_next_oid = cpu_to_le64(APFS_OID_RESERVED_COUNT + 100); sb_copy->nx_next_xid = cpu_to_le64(MKFS_XID + 1); /* * We need to know the spaceman size before we can decide the location * of the other ephemeral objects. */ set_spaceman_info(); set_ephemeral_bnos(); sb_copy->nx_spaceman_oid = cpu_to_le64(SPACEMAN_OID); make_spaceman(eph_info.spaceman_bno, SPACEMAN_OID); sb_copy->nx_reaper_oid = cpu_to_le64(REAPER_OID); make_empty_reaper(eph_info.reaper_bno, REAPER_OID); sb_copy->nx_omap_oid = cpu_to_le64(MAIN_OMAP_BNO); make_omap_btree(MAIN_OMAP_BNO, false /* is_vol */); if (fd_tier2 != -1) { sb_copy->nx_fusion_mt_oid = cpu_to_le64(FUSION_MT_BNO); make_empty_btree_root(FUSION_MT_BNO, FUSION_MT_BNO, APFS_OBJECT_TYPE_FUSION_MIDDLE_TREE); sb_copy->nx_fusion_wbc_oid = cpu_to_le64(FUSION_WBC_OID); make_empty_fusion_wbc_state(eph_info.fusion_wbc_bno, FUSION_WBC_OID); sb_copy->nx_fusion_wbc.pr_start_paddr = cpu_to_le64(FUSION_WBC_FIRST_BNO); /* TODO: figure out the formula for the block count */ sb_copy->nx_fusion_wbc.pr_block_count = cpu_to_le64(1); } set_checkpoint_areas(sb_copy); sb_copy->nx_max_file_systems = cpu_to_le32(get_max_volumes(size)); sb_copy->nx_fs_oid[0] = cpu_to_le64(FIRST_VOL_OID); make_volume(FIRST_VOL_BNO, FIRST_VOL_OID); set_ephemeral_info(&sb_copy->nx_ephemeral_info[0]); set_object_header(&sb_copy->nx_o, param->blocksize, APFS_OID_NX_SUPERBLOCK, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_SUPERBLOCK, APFS_OBJECT_TYPE_INVALID); /* * If the disk was ever formatted as APFS, valid checkpoint superblocks * may still remain. Wipe the area to avoid mounting them by mistake. */ zero_area(CPOINT_DESC_BASE, CPOINT_DESC_BLOCKS); make_cpoint_map_block(CPOINT_MAP_BNO); make_cpoint_superblock(CPOINT_SB_BNO, sb_copy); if (fd_tier2 != -1) make_tier2_superblock(sb_copy); munmap(sb_copy, param->blocksize); } apfsprogs-0.2.0/mkapfs/super.h000066400000000000000000000002621471277137200163200ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _SUPER_H #define _SUPER_H extern void make_container(void); #endif /* _SUPER_H */ apfsprogs-0.2.0/mkapfs/test.sh000077500000000000000000000042661471277137200163370ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2023 Corellium LLC # # Author: Ernesto A. Fernández # # Call as ./test.sh to test mkfs on various container sizes. Some will be very # big so this should always be done inside a filesystem that supports sparse # files. success=0 set -e cleanup() { rm -f /tmp/sizetest.img rm -f /tmp/sizetest2.img [ $success -eq 1 ] && echo "TEST PASSED" || echo "TEST FAILED" } trap cleanup exit test_size() { truncate -s $1 /tmp/sizetest.img ./mkapfs /tmp/sizetest.img ../apfsck/apfsck -cuw /tmp/sizetest.img } test_fusion_sizes() { truncate -s $1 /tmp/sizetest.img truncate -s $2 /tmp/sizetest2.img ./mkapfs -F /tmp/sizetest2.img /tmp/sizetest.img ../apfsck/apfsck -cuw -F /tmp/sizetest2.img /tmp/sizetest.img } confirm_mkfs_failure() { truncate -s $1 /tmp/sizetest.img truncate -s $2 /tmp/sizetest2.img # Confirm that this fails cleanly, not with sigbus ./mkapfs -F /tmp/sizetest2.img /tmp/sizetest.img >/dev/null 2>&1 || [ $? -eq 1 ] } test_partial() { truncate -s $1 /tmp/sizetest.img ./mkapfs /tmp/sizetest.img $2 ../apfsck/apfsck -cuw /tmp/sizetest.img } # Single block ip bitmap, single block spaceman, no CABs sizes[0]=512K # Minimum size sizes[1]=15G sizes[2]=1454383300608 # Maximum size # Multiblock ip bitmap, single block spaceman, no CABs sizes[3]=1454383304704 # Minimum size sizes[4]=3T sizes[5]=7407207972864 # Maximum size # Multiblock ip bitmap, multiblock spaceman, no CABs sizes[6]=7407207976960 # Minimum size sizes[7]=7T sizes[8]=8574096900096 # Maximum size # Multiblock ip bitmap, single block spaceman, has CABs sizes[9]=8574096904192 # Minimum size sizes[10]=15T # Filesystems > ~113 TiB not yet supported # Regression tests for sizes that caused problems in the past sizes[11]=3G touch /tmp/sizetest.img for sz in ${sizes[@]}; do test_size $sz done touch /tmp/sizetest2.img for sz1 in ${sizes[@]}; do for sz2 in ${sizes[@]}; do # The main device needs to be large enough to map tier 2 if [ "$sz1" = "512K" -a "$sz2" != "512K" ]; then confirm_mkfs_failure $sz1 $sz2 else test_fusion_sizes $sz1 $sz2 fi done done # Regression test for filesystems that don't fill the whole device test_partial 15G 262144 success=1