pax_global_header00006660000000000000000000000064141110774620014515gustar00rootroot0000000000000052 comment=5efac5a701bcb56e23cfc182b5b3901bff27d343 apfsprogs-master/000077500000000000000000000000001411107746200143565ustar00rootroot00000000000000apfsprogs-master/.gitignore000066400000000000000000000000661411107746200163500ustar00rootroot00000000000000*.swp *.d *.o *.a /apfsck/apfsck /mkapfs/mkapfs /tags apfsprogs-master/CONTRIBUTING000066400000000000000000000005151411107746200162110ustar00rootroot00000000000000 If you want to help with development, please submit your patches for review to the project's mailing list at . We try to follow the style guidelines of the Linux kernel. I don't own any Apple devices, so testing is needed and very appreciated. Bug reports should be sent to the mailing list as well. apfsprogs-master/LICENSE000066400000000000000000000431331411107746200153670ustar00rootroot00000000000000 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-master/README000066400000000000000000000022001411107746200152300ustar00rootroot00000000000000 Apfsprogs is a suite of userland software to work with the Apple File System on Linux. For now only mkfs and fsck tools are available, both functional but lacking in features. Compatibility with the Apple implementations has barely been tested; please exercise caution, and report any issues that you find to . A Linux kernel module for mounting the filesystem can be retrieved from . The main purpose of apfsprogs is to assist in testing the upcoming write support. Usage ===== To build either the mkapfs or apfsck tool, 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-master/apfsck/000077500000000000000000000000001411107746200156255ustar00rootroot00000000000000apfsprogs-master/apfsck/Makefile000066400000000000000000000021031411107746200172610ustar00rootroot00000000000000SRCS = apfsck.c btree.c dir.c extents.c htable.c \ inode.c key.c object.c spaceman.c super.c xattr.c OBJS = $(SRCS:.c=.o) DEPS = $(SRCS:.c=.d) LIBDIR = ../lib LIBRARY = $(LIBDIR)/libapfs.a BINDIR = ~/bin MANDIR = ~/share/man/man8 SPARSE_VERSION := $(shell sparse --version 2>/dev/null) override CFLAGS += -Wall -Wno-address-of-packed-member -fno-strict-aliasing -I$(CURDIR)/../include apfsck: $(OBJS) $(LIBRARY) @echo ' Linking...' @gcc $(CFLAGS) -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 $<...' @gcc $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) apfsck install: install -d $(BINDIR) install -t $(BINDIR) apfsck ln -fs -T $(BINDIR)/apfsck $(BINDIR)/fsck.apfs install -d $(MANDIR) install -m 644 -t $(MANDIR) apfsck.8 ln -fs -T $(MANDIR)/apfsck.8 $(MANDIR)/fsck.apfs.8 apfsprogs-master/apfsck/apfsck.8000066400000000000000000000020701411107746200171640ustar00rootroot00000000000000.\" apfsck.8 - manpage for apfsck .\" .\" Copyright (C) 2019 Ernesto A. Fernández .\" .TH apfsck 8 "March 2019" "apfsprogs 0.1" .SH NAME apfsck \- check an APFS filesystem .SH SYNOPSIS .B apfsck [\-cuvw] .I device .SH DESCRIPTION .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. .SH EXIT STATUS The exit status is 0 if there was nothing to report, 1 otherwise. .SH REPORTING BUGS Please submit any issues that you find to the project's mailing list at . .SH AUTHOR Written by Ernesto A. Fernández . .SH SEE ALSO .BR mkapfs (8) apfsprogs-master/apfsck/apfsck.c000066400000000000000000000060641411107746200172460ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include "apfsck.h" #include "super.h" int fd; 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] device\n", progname); exit(1); } /** * version - Print version information and exit */ static void version(void) { printf("apfsck version 0.1\n"); exit(1); } /** * system_error - Print a system error message and exit */ __attribute__((noreturn)) void system_error(void) { perror(progname); exit(1); } /** * 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(1); } /** * 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, "cuvw"); 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(); default: usage(); } } if (optind != argc - 1) usage(); filename = argv[optind]; fd = open(filename, O_RDONLY); if (fd == -1) system_error(); parse_filesystem(); if (weird_state) return 1; return 0; } apfsprogs-master/apfsck/apfsck.h000066400000000000000000000020541411107746200172460ustar00rootroot00000000000000/* * 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; /* File descriptor for the device */ 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); #endif /* _APFSCK_H */ apfsprogs-master/apfsck/btree.c000066400000000000000000001242331411107746200170770ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #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 "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; 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; 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); if (index_size < node_min_table_size(node)) 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); } /** * read_node - Read a node header from disk * @oid: object id for the node * @btree: tree structure, with the omap_table already set * * Returns a pointer to the resulting node structure. */ static struct node *read_node(u64 oid, struct btree *btree) { struct apfs_btree_node_phys *raw; struct node *node; u32 obj_type, obj_subtype; node = calloc(1, sizeof(*node)); if (!node) system_error(); node->btree = btree; /* 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 raw = read_object(oid, btree->omap_table, &node->object); node->raw = raw; 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); } obj_type = node->object.type; if (node_is_root(node) && obj_type != APFS_OBJECT_TYPE_BTREE) report("B-tree node", "wrong object type for root."); if (!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 (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."); 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) { if (node_is_root(node)) return; /* The root nodes are needed by the sb until the end */ munmap(node->raw, sb->s_blocksize); 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 = 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; /* 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; default: report_unknown("Snapshots, encryption, directory statistics"); break; } } /** * free_omap_record - Free an object map record structure after a final check * @entry: the entry to free */ static void free_omap_record(struct htable_entry *entry) { struct omap_record *omap_rec = (struct omap_record *)entry; if (!omap_rec->o_seen) report("Omap record", "object id is never used."); 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); } /** * get_omap_record - Find or create an omap record structure in a hash table * @oid: object id to be mapped * @table: the hash table of omap records to be searched * * Returns the omap record structure, after creating it if necessary. */ struct omap_record *get_omap_record(u64 oid, struct htable_entry **table) { struct htable_entry *entry; entry = get_htable_entry(oid, sizeof(struct omap_record), table); return (struct omap_record *)entry; } /** * 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), vsb->v_omap_table); } else { /* We are parsing the container's object map */ omap_rec = get_omap_record(le64_to_cpu(key->ok_oid), sb->s_omap_table); } if (omap_rec->o_xid) /* More than one omap record for the same oid */ report_unknown("Snapshots"); omap_rec->o_xid = le64_to_cpu(key->ok_xid); omap_rec->o_bno = le64_to_cpu(val->ov_paddr); 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_DELETED) report_unknown("Deleted omap records"); if (flags & APFS_OMAP_VAL_SAVED) report("Omap record", "saved flag is set."); if (flags & APFS_OMAP_VAL_NOHEADER) report_unknown("Virtual objects with no header"); if (flags & (APFS_OMAP_VAL_ENCRYPTED | APFS_OMAP_VAL_CRYPTO_GENERATION)) report_unknown("Encryption"); 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; 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."); /* 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."); 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_snap_meta(btree)) { if (root->records) report_unknown("Snapshots"); if (!node_is_leaf(root)) report("Snap meta tree", "has no root node."); } 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; 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 (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); continue; } 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); 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)) && 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; 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); 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."); } /** * 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; 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)) { 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."); if (le32_to_cpu(info->bt_longest_key) != sizeof(struct apfs_omap_key)) report(ctx, "wrong maximum key size in info footer."); if (le32_to_cpu(info->bt_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."); 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."); /* TODO: could this be zero if all records are ghosts? */ if (le32_to_cpu(info->bt_longest_val) != 8) report(ctx, "wrong maximum value size in info footer."); return; } /* For now, only the omap and free queue 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)) { 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; } if (btree_is_snap_meta(btree)) { /* For now we only support empty snapshot metadata trees */ if (info->bt_longest_key || info->bt_longest_val) report_unknown("Snapshots"); 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; btree->type = BTREE_TYPE_FREE_QUEUE; btree->omap_table = NULL; /* These are ephemeral objects */ btree->root = read_node(oid, btree); 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}; 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); parse_subtree(snap->root, &last_key, NULL /* 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(); cat->type = BTREE_TYPE_CATALOG; cat->omap_table = omap_table; cat->root = read_node(oid, cat); parse_subtree(cat->root, &last_key, name_buf); check_btree_footer(cat); return cat; } /** * 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("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."); } /** * 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_snap_count || raw->om_snapshot_tree_oid || raw->om_most_recent_snap) report_unknown("Snapshots"); /* 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); /* 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, sb->s_blocksize); return omap; } /** * 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); 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; 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); } /** * extentref_lookup - Find the best match for an extent in the 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 */ void extentref_lookup(struct node *tbl, u64 bno, struct extref_record *extref) { struct query *query; struct key key; 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)) { report("Extent reference tree", "record missing for block number 0x%llx.", (unsigned long long)bno); } ongoing_query = false; extref_rec_from_query(query, extref); free_query(query); } /** * 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; 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); 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-master/apfsck/btree.h000066400000000000000000000132551411107746200171050ustar00rootroot00000000000000/* * 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 htable_entry o_htable; /* Hash table entry header */ bool o_seen; /* Has this oid been seen in use? */ u64 o_xid; /* Transaction id (snapshots are not supported) */ u64 o_bno; /* Block number */ }; /* * 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 0007 /* 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_MULTIPLE 0010 /* Search for multiple matches */ #define QUERY_NEXT 0020 /* Find next of multiple matches */ #define QUERY_EXACT 0040 /* Search for an exact match */ #define QUERY_DONE 0100 /* 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 */ /* 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; } /** * 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; } 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 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_omap_record(u64 oid, struct htable_entry **table); extern void extentref_lookup(struct node *tbl, u64 bno, struct extref_record *extref); #endif /* _BTREE_H */ apfsprogs-master/apfsck/dir.c000066400000000000000000000113001411107746200165420ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #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."); } /** * 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; 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); 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."); parent_ino = cat_cnid(key); check_inode_ids(ino, parent_ino); if (parent_ino != APFS_ROOT_DIR_PARENT) { 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++; } if (!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; } dtype = le16_to_cpu(val->flags) & APFS_DREC_TYPE_MASK; if (dtype != le16_to_cpu(val->flags)) report("Dentry record", "reserved flags in use."); /* 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; 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); } apfsprogs-master/apfsck/dir.h000066400000000000000000000003631411107746200165560ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _DIR_H #define _DIR_H #include extern void parse_dentry_record(void *key, struct apfs_drec_val *val, int len); #endif /* _DIR_H */ apfsprogs-master/apfsck/extents.c000066400000000000000000000213601411107746200174650ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include "apfsck.h" #include "btree.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "key.h" #include "super.h" /** * free_extent - Free an extent structure after performing a final check * @entry: the entry to free */ static void free_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."); 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) { free_htable(table, free_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; entry = get_htable_entry(bno, sizeof(struct extent), vsb->v_extent_table); return (struct extent *)entry; } /** * 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."); } else { if (!dstream->d_seen) report("Data stream", "missing reference count."); if (dstream->d_refcnt != dstream->d_references) report("Data stream", "bad reference count."); } 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."); } /** * 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); if (cnid->c_state == CNID_USED) report("Catalog", "a filesystem object id was used twice."); cnid->c_state = CNID_USED; /* Increase the refcount of each physical extent used by the dstream */ while (curr_extent) { struct listed_extent *next_extent; struct extent *extent; extent = get_extent(curr_extent->paddr); if (extent->e_references) { if (extent->e_obj_type != dstream->d_obj_type) report("Physical extent record", "owners have inconsistent types."); /* Only count the extent once for each owner */ if (extent->e_latest_owner != dstream->d_owner) extent->e_references++; } else { extent->e_references++; } extent->e_obj_type = dstream->d_obj_type; extent->e_latest_owner = dstream->d_owner; next_extent = curr_extent->next; free(curr_extent); curr_extent = next_extent; } 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 = &dstream->d_extents; struct listed_extent *ext = *ext_p; struct listed_extent *new; struct extref_record extref; if (paddr + blk_count < paddr) /* Overflow */ report("Extent record", "physical address is too big."); /* Find out which physical extent contains this address range */ extentref_lookup(vsb->v_extent_ref->root, paddr, &extref); if (extref.phys_addr + extref.blocks < paddr + blk_count) report("Extent record", "no entry in extent reference tree."); paddr = extref.phys_addr; /* 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->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; if (len != sizeof(*val)) report("Extent record", "wrong size of value."); if (val->crypto_id) report_unknown("Extent encryption"); 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."); flags = le64_to_cpu(val->len_and_flags) & APFS_FILE_EXTENT_FLAG_MASK; if (flags) report("Extent record", "no flags should be set."); dstream = get_dstream(cat_cnid(&key->hdr)); if (dstream->d_bytes != le64_to_cpu(key->logical_addr)) report("Data stream", "extents 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); } /** * 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; u8 kind; u32 refcnt; u64 length, owner; 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"); if (kind == APFS_KIND_UPDATE) report_unknown("Snapshots"); 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_MIN_USER_INO_NUM) report("Physical extent record", "invalid or 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."); extent = get_extent(cat_cnid(&key->hdr)); extent->e_refcnt = refcnt; vsb->v_block_count += length; container_bmap_mark_as_used(extent->e_bno, length); return extent->e_bno + length - 1; } apfsprogs-master/apfsck/extents.h000066400000000000000000000051611411107746200174730ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _EXTENTS_H #define _EXTENTS_H #include #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 */ }; /* * Physical extent data in memory */ struct extent { struct htable_entry e_htable; /* Hash table entry header */ u8 e_obj_type; /* Type of the owner objects */ /* Extent stats read from the physical extent structure */ u32 e_refcnt; /* Reference count */ /* Extent stats measured by the fsck */ u32 e_references; /* Number of references to extent */ 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 */ struct listed_extent *next; /* Next 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; 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? */ /* 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_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 */ 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); #endif /* _EXTENTS_H */ apfsprogs-master/apfsck/htable.c000066400000000000000000000045261411107746200172370ustar00rootroot00000000000000/* * 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); } /** * 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; } /** * free_cnid_table - Free the cnid hash table and all its entries * @table: table to free */ void free_cnid_table(struct htable_entry **table) { /* No checks needed here, just call free() on each entry */ free_htable(table, (void (*)(struct htable_entry *))free); } /** * 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-master/apfsck/htable.h000066400000000000000000000023611411107746200172370ustar00rootroot00000000000000/* * 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_USED 1 /* The cnid is used, and can't be reused */ #define CNID_DSTREAM_ALLOWED 2 /* The cnid can be reused by one 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; }; extern struct htable_entry **alloc_htable(); extern void free_htable(struct htable_entry **table, void (*free_entry)(struct htable_entry *)); extern struct htable_entry *get_htable_entry(u64 id, int size, 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-master/apfsck/inode.c000066400000000000000000000556511411107746200171030ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include "apfsck.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "key.h" #include "super.h" /** * 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."); } else { if (inode->i_nlink != inode->i_link_count) report("Inode record", "wrong link count."); } dstream = inode->i_dstream; if (dstream) { if (dstream->d_sparse_bytes != inode->i_sparse_bytes) 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."); } /** * 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 (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 { /* No siblings, so the primary link is the first and only */ 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); if (cnid->c_state != CNID_UNUSED) report("Catalog", "a filesystem object id was reused."); cnid->c_state = CNID_USED; 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; } /* 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 listed_cnid *cnid; /* The inodes 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(inode->i_ino); if (cnid->c_state != CNID_UNUSED) report("Catalog", "a filesystem object id was used twice."); if (inode->i_ino != inode->i_private_id) cnid->c_state = CNID_USED; else /* The inode and its dstream share an id */ cnid->c_state = CNID_DSTREAM_ALLOWED; check_inode_stats(inode); free_inode_names(inode); free(entry); } /** * 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) { 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; 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); if (dstream_raw->default_crypto_id) report_unknown("Dstream encryption"); 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_references++; dstream->d_owner = dstream->d_id; inode->i_dstream = dstream; return sizeof(*dstream_raw); } /** * 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."); } /** * 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_xfield_inode_flags - Check that xfields are consistent with inode flags * @bmap: bitmap of xfield types seen in the inode * @flags: inode flags */ 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."); /* Some inodes don't have finder info but still have the flag... */ if (xbmap_test(bmap, APFS_INO_EXT_TYPE_FINDER_INFO) && !(flags & APFS_INODE_HAS_FINDER_INFO)) report("Inode record", "wrong setting for finder info flag."); if (!xbmap_test(bmap, APFS_INO_EXT_TYPE_FINDER_INFO) && (flags & APFS_INODE_HAS_FINDER_INFO)) report_weird("Finder info flag in inode record"); } /** * 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 = 4; 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 = sizeof(struct apfs_dir_stats_val); report_unknown("Directory statistics 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); 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."); } /** * check_inode_internal_flags - Check basic consistency of inode flags * @flags: the flags */ static void check_inode_internal_flags(u64 flags) { 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_TO_MAIN || flags & APFS_INODE_PINNED_TO_TIER2 || flags & APFS_INODE_ALLOCATION_SPILLEDOVER) report_unknown("Fusion drive"); if (flags & APFS_INODE_MAINTAIN_DIR_STATS) report_unknown("Directory statistics"); if (flags & APFS_INODE_IS_APFS_PRIVATE) report_unknown("Private implementation inode"); } /** * 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: 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: /* These are fine */ break; default: report("Inode record", "reserved parent inode number."); } } } /** * 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; 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_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); def_prot_class = le32_to_cpu(val->default_protection_class); /* These protection classes have been seen in unencrypted volumes */ if (def_prot_class != APFS_PROTECTION_CLASS_DIR_NONE && def_prot_class != APFS_PROTECTION_CLASS_D) report_unknown("Encryption"); 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: if ((mode & ~S_IFMT) != 0x1ed) report("Symlink inode", "wrong permissions."); 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); if (le16_to_cpu(val->pad1) || le64_to_cpu(val->pad2)) report("Inode record", "padding should be zeroes."); 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-master/apfsck/inode.h000066400000000000000000000054321411107746200171000ustar00rootroot00000000000000/* * 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 */ /* * 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) */ /* Inode stats measured by the fsck */ u8 i_xattr_bmap; /* Bitmap of system xattrs 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-master/apfsck/key.c000066400000000000000000000226431411107746200165700ustar00rootroot00000000000000/* * 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_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_SNAP_NAME: read_snap_name_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."); 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_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; } apfsprogs-master/apfsck/key.h000066400000000000000000000057601411107746200165760ustar00rootroot00000000000000/* * 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_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); #endif /* _KEY_H */ apfsprogs-master/apfsck/object.c000066400000000000000000000170441411107746200172450ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "htable.h" #include "object.h" #include "super.h" int obj_verify_csum(struct apfs_obj_phys *obj) { return (le64_to_cpu(obj->o_cksum) == fletcher64((char *) obj + APFS_MAX_CKSUM_SIZE, sb->s_blocksize - APFS_MAX_CKSUM_SIZE)); } /** * read_object_nocheck - Read an object header from disk * @bno: block number for the object * @obj: object struct to receive the results * * Returns a pointer to the raw data of the object in memory, without running * any checks other than the Fletcher verification. */ void *read_object_nocheck(u64 bno, struct object *obj) { struct apfs_obj_phys *raw; raw = mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, fd, bno * sb->s_blocksize); if (raw == MAP_FAILED) system_error(); /* This one check is always needed */ if (!obj_verify_csum(raw)) { 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); return raw; } /** * parse_object_flags - Check consistency of object flags * @flags: the flags * * Returns the storage type flags to be checked by the caller. */ u32 parse_object_flags(u32 flags) { 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."); if (flags & APFS_OBJ_ENCRYPTED) report_unknown("Encrypted object"); return flags & APFS_OBJ_STORAGETYPE_MASK; } /** * read_object - 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 * * Returns a pointer to the raw data of the object in memory, after checking * the consistency of some of its fields. */ void *read_object(u64 oid, struct htable_entry **omap_table, struct object *obj) { struct apfs_obj_phys *raw; struct omap_record *omap_rec; u64 bno; u64 xid; u32 storage_type; if (omap_table) { omap_rec = get_omap_record(oid, omap_table); if (omap_rec->o_seen) report("Object map record", "oid was used twice."); omap_rec->o_seen = true; bno = omap_rec->o_bno; if (!bno) report("Object map", "record missing for id 0x%llx.", (unsigned long long)oid); } else { bno = oid; } raw = read_object_nocheck(bno, obj); if (!ongoing_query) { /* Query code will revisit already parsed nodes */ if (vsb) ++vsb->v_block_count; if ((obj->type == APFS_OBJECT_TYPE_SPACEMAN_CIB) || (obj->type == APFS_OBJECT_TYPE_SPACEMAN_CAB)) { ip_bmap_mark_as_used(bno, 1 /* length */); } else { container_bmap_mark_as_used(bno, 1 /* length */); } } if (oid != obj->oid) report("Object header", "wrong object id in block 0x%llx.", (unsigned long long)bno); if (oid < APFS_OID_RESERVED_COUNT) report("Object header", "reserved object id in block 0x%llx.", (unsigned long long)bno); 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 || sb->s_xid < xid) 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->o_xid) report("Object header", "transaction id in omap key doesn't match block 0x%llx.", (unsigned long long)bno); /* Ephemeral objects are handled by read_ephemeral_object() */ storage_type = parse_object_flags(obj->flags); 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; } /** * 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; u32 blk_count = map->m_size >> sb->s_blocksize_bits; u64 obj_start = map->m_paddr; u64 obj_end = map->m_paddr + blk_count; /* Objects can't wrap, right? */ u64 data_start = sb->s_data_base; u64 data_end = sb->s_data_base + sb->s_data_blocks; u64 valid_start; if (!map->m_seen) report("Checkpoint map", "no object for mapping."); if (obj_start < data_start || obj_end > data_end) report("Checkpoint map", "block number is out of range."); /* Not all blocks in the data area belong to the current checkpoint */ valid_start = sb->s_data_base + sb->s_data_index; if (obj_start >= valid_start && obj_end > valid_start + sb->s_data_len) report("Checkpoint map", "block number outside valid range."); if (obj_start < valid_start && obj_end + sb->s_data_blocks > valid_start + sb->s_data_len) report("Checkpoint map", "block number outside valid range."); 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; 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; /* Multiblock ephemeral objects may exist, but are not supported yet */ raw = read_object_nocheck(map->m_paddr, obj); 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); if (storage_type != APFS_OBJ_EPHEMERAL) report("Object header", "wrong flag for ephemeral object."); return raw; } apfsprogs-master/apfsck/object.h000066400000000000000000000015431411107746200172470ustar00rootroot00000000000000/* * 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; }; extern int obj_verify_csum(struct apfs_obj_phys *obj); extern void *read_object_nocheck(u64 bno, struct object *obj); extern u32 parse_object_flags(u32 flags); extern void *read_object(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-master/apfsck/spaceman.c000066400000000000000000000647071411107746200175760ustar00rootroot00000000000000/* * 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) { /* Avoid out-of-bounds writes to the allocation bitmap */ if (paddr + length >= sb->s_block_count || paddr + length < paddr) report(NULL /* context */, "Out-of-range block number."); bmap_mark_as_used(sb->s_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. Also calculates the * total number of chunks and cibs in the container. */ 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."); sm->sm_chunk_count = DIV_ROUND_UP(sb->s_block_count, sm->sm_blocks_per_chunk); sm->sm_cib_count = DIV_ROUND_UP(sm->sm_chunk_count, sm->sm_chunks_per_cib); if ((sm->sm_chunk_count + sm->sm_cib_count) * 3 != sm->sm_ip_block_count) report("Space manager", "wrong size of internal pool."); } /** * 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 * * 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 *sm = &sb->s_spaceman; ssize_t read_bytes; void *buf, *ret; size_t count; off_t offset; u32 chunk_number; assert(sm->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 = sm->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 = pread(fd, 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 * * 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 *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."); sm->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)); 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."); sm->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 * * Returns the first block number for the first chunk of the next cib. */ static u64 parse_chunk_info_block(u64 bno, int index, u64 start) { struct spaceman *sm = &sb->s_spaceman; struct object obj; struct apfs_chunk_info_block *cib; u32 chunk_count; bool last_cib = index == sm->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."); sm->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); 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."); munmap(cib, sb->s_blocksize); 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 >= sb->s_blocksize || offset + sizeof(u64) > sb->s_blocksize) report("Spaceman", "offset is out of bounds."); return *((u64 *)value_p); } /** * spaceman_256_from_off - Get a pointer to the 256 bits on a spaceman offset * @raw: pointer to the raw space manager * @offset: offset of the 256-bit value in @raw * * TODO: check that no values found by this function overlap with each other, * and also with spaceman_val_from_off(). */ static char *spaceman_256_from_off(struct apfs_spaceman_phys *raw, u32 offset) { struct spaceman *sm = &sb->s_spaceman; char *value_p = (char *)raw + offset; int sz_256 = 256 / 8; 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 >= sb->s_blocksize || offset + sz_256 > sb->s_blocksize) report("Spaceman", "offset is out of bounds."); return value_p; } /** * parse_spaceman_main_device - Parse and check the spaceman main device struct * @raw: pointer to the raw space manager */ static void parse_spaceman_main_device(struct apfs_spaceman_phys *raw) { struct spaceman *sm = &sb->s_spaceman; struct apfs_spaceman_device *dev = &raw->sm_dev[APFS_SD_MAIN]; u32 addr_off; u64 start = 0; int i; if (dev->sm_cab_count) report_unknown("Chunk-info address block"); if (le32_to_cpu(dev->sm_cib_count) != sm->sm_cib_count) report("Spaceman device", "wrong count of chunk-info blocks."); if (le64_to_cpu(dev->sm_chunk_count) != sm->sm_chunk_count) report("Spaceman device", "wrong count of chunks."); if (le64_to_cpu(dev->sm_block_count) != sb->s_block_count) report("Spaceman device", "wrong block count."); addr_off = le32_to_cpu(dev->sm_addr_offset); for (i = 0; i < sm->sm_cib_count; ++i) { u64 bno = spaceman_val_from_off(raw, addr_off + i * sizeof(u64)); start = parse_chunk_info_block(bno, i, start); } if (sm->sm_chunk_count != sm->sm_chunks) report("Spaceman device", "bad total number of chunks."); if (sb->s_block_count != sm->sm_blocks) report("Spaceman device", "bad total number of blocks."); if (le64_to_cpu(dev->sm_free_count) != sm->sm_free) report("Spaceman device", "bad total number of free blocks."); if (dev->sm_reserved || dev->sm_reserved2) report("Spaceman device", "non-zero padding."); } /** * check_spaceman_tier2_device - Check that the second-tier device is empty * @raw: pointer to the raw space manager */ static void check_spaceman_tier2_device(struct apfs_spaceman_phys *raw) { struct spaceman *sm = &sb->s_spaceman; struct apfs_spaceman_device *main_dev = &raw->sm_dev[APFS_SD_MAIN]; struct apfs_spaceman_device *dev = &raw->sm_dev[APFS_SD_TIER2]; u32 addr_off, main_addr_off; addr_off = le32_to_cpu(dev->sm_addr_offset); main_addr_off = le32_to_cpu(main_dev->sm_addr_offset); if (addr_off != main_addr_off + sm->sm_cib_count * sizeof(u64)) report("Spaceman device", "not consecutive address offsets."); if (spaceman_val_from_off(raw, addr_off)) /* Empty device has no cib */ report_unknown("Fusion drive"); if (dev->sm_block_count || dev->sm_chunk_count || dev->sm_cib_count || dev->sm_cab_count || dev->sm_free_count) report_unknown("Fusion drive"); if (dev->sm_reserved || dev->sm_reserved2) report("Spaceman device", "non-zero padding."); } /** * check_allocation_boundaries - Check the boundaries of an allocation zone * @azb: pointer to the raw boundaries structure * @dev: index for the device * * Allocation zones are undocumented, so we can't do much more than report them * as unsupported if they are in use. */ static void check_allocation_boundaries( struct apfs_spaceman_allocation_zone_boundaries *azb, int dev) { if (!azb->saz_zone_start && !azb->saz_zone_end) return; if (dev == APFS_SD_MAIN) report_unknown("Allocation zones"); else report_unknown("Fusion drive"); } /** * 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; struct apfs_spaceman_allocation_zone_boundaries *azb; int j; az = &dz->sdz_allocation_zones[dev][i]; if (az->saz_zone_id || az->saz_previous_boundary_index) report_unknown("Allocation zones"); if (az->saz_reserved) report("Datazone", "reserved field in use."); azb = &az->saz_current_boundaries; check_allocation_boundaries(azb, dev); for (j = 0; j < APFS_SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES; ++j) { azb = &az->saz_previous_boundaries[j]; check_allocation_boundaries(azb, dev); } } } } /** * 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; if (sfq[APFS_SFQ_TIER2].sfq_count || sfq[APFS_SFQ_TIER2].sfq_tree_oid || sfq[APFS_SFQ_TIER2].sfq_oldest_xid || sfq[APFS_SFQ_TIER2].sfq_tree_node_limit) report_unknown("Fusion drive"); 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_chunks)) report("Spaceman free queue", "wrong node limit."); sm->sm_main_fq = parse_free_queue_btree( le64_to_cpu(sfq[APFS_SFQ_MAIN].sfq_tree_oid), APFS_SFQ_MAIN); if (le64_to_cpu(sfq[APFS_SFQ_MAIN].sfq_count) != sm->sm_main_fq->sfq_count) report("Spaceman free queue", "wrong block count."); if (le64_to_cpu(sfq[APFS_SFQ_MAIN].sfq_oldest_xid) != sm->sm_main_fq->sfq_oldest_xid) report("Spaceman free queue", "oldest xid is wrong."); if (le16_to_cpu(sfq[APFS_SFQ_MAIN].sfq_tree_node_limit) < sm->sm_main_fq->sfq_btree.node_count) report("Spaceman free queue", "node count above limit."); if (le16_to_cpu(sfq[APFS_SFQ_MAIN].sfq_tree_node_limit) != main_fq_node_limit(sm->sm_blocks)) report("Spaceman free queue", "wrong node limit."); } /** * compare_container_bitmaps - Verify the container's allocation bitmap * @sm_bmap: allocation bitmap reported by the space manager * @real_bmap: allocation bitmap assembled by the fsck * @chunks: container chunk count, i.e., block count for the bitmaps */ static void compare_container_bitmaps(u64 *sm_bmap, u64 *real_bmap, u64 chunks) { unsigned long long bmap_size = sb->s_blocksize * chunks; unsigned long long count64; u64 i; /* * TODO: sometimes the bitmaps don't match; maybe this has something to * do with the file count issue mentioned at check_container()? */ if (!memcmp(sm_bmap, real_bmap, bmap_size)) return; report_weird("Container allocation bitmap"); /* At least verify that all used blocks are marked as such */ count64 = bmap_size / sizeof(count64); for (i = 0; i < count64; ++i) if ((sm_bmap[i] | real_bmap[i]) != sm_bmap[i]) report("Space manager", "bad allocation bitmap."); } /** * check_ip_free_next - Check the free_next field for the internal pool * @free_next: 256-bit field to check * @free_head: first free block in the ip circular buffer * @free_len: number of free blocks in the ip circular buffer */ static void check_ip_free_next(__le16 *free_next, u16 free_head, u16 free_len) { int bmap_count = 16; __le16 *expected; u32 i; expected = calloc(bmap_count, sizeof(*free_next)); if (!expected) system_error(); /* * Ip bitmap blocks are marked with numbers 1,2,3,...,14,15,0 in * free_next, except when they are in use: those get overwritten with * 0xFFFF. */ for (i = 0; i < bmap_count; i++) { u32 index_in_free = (bmap_count + i - free_head) % bmap_count; if (index_in_free < free_len) expected[i] = cpu_to_le16((1 + i) % bmap_count); else expected[i] = cpu_to_le16(0xFFFF); } if (memcmp(free_next, expected, bmap_count * sizeof(*free_next))) report("Space manager", "Bad ip_bm_free_next bitmap."); } /** * parse_ip_bitmap_list - Check consistency of the internal pool bitmap list * @raw: pointer to the raw space manager * * Returns the block number for the current bitmap. */ static u64 parse_ip_bitmap_list(struct apfs_spaceman_phys *raw) { u64 bmap_base = le64_to_cpu(raw->sm_ip_bm_base); u64 bmap_off; u32 bmap_length = le32_to_cpu(raw->sm_ip_bm_block_count); u16 free_head, free_tail, free_length; char *free_next; /* * So far all internal pool bitmaps encountered had only one block; the * bitmap area is larger than that because it keeps some old versions. */ bmap_off = spaceman_val_from_off(raw, le32_to_cpu(raw->sm_ip_bitmap_offset)); if (bmap_off >= bmap_length) report("Internal pool", "bitmap block is out-of-bounds."); if (le32_to_cpu(raw->sm_ip_bm_size_in_blocks) != 1) report_unknown("Multiblock bitmap in internal pool"); /* The head and tail fit in 16-bit fields, so the length also should */ if (bmap_length > (u16)(~0U)) report("Internal pool", "bitmap list is too long."); /* This may be wrong for huge containers, I haven't tested those yet */ if (bmap_length != 16) report("Space manager", "ip doesn't have 16 bitmaps."); free_head = le16_to_cpu(raw->sm_ip_bm_free_head); free_tail = le16_to_cpu(raw->sm_ip_bm_free_tail); free_length = (bmap_length + free_tail - free_head) % bmap_length; if (free_head >= bmap_length || free_tail >= bmap_length) report("Internal pool", "free bitmaps are out-of-bounds."); if ((bmap_length + bmap_off - free_head) % bmap_length < free_length) report("Internal pool", "current bitmap listed as free."); if (free_length != bmap_length - 2) report_unknown("Internal pool bitmaps in use are not two"); free_next = spaceman_256_from_off(raw, le32_to_cpu(raw->sm_ip_bm_free_next_offset)); check_ip_free_next((__le16 *)free_next, free_head, free_length); container_bmap_mark_as_used(bmap_base, bmap_length); return bmap_base + bmap_off; } /** * 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; for (i = 0; i < bmap_length; ++i) { char *bmap; int edge, j; bmap = mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, fd, (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) { u64 *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 = DIV_ROUND_UP(pool_blocks, 8 * sb->s_blocksize); u64 xid; pool_bmap = mmap(NULL, sb->s_blocksize, PROT_READ, MAP_PRIVATE, fd, parse_ip_bitmap_list(raw) * sb->s_blocksize); if (pool_bmap == MAP_FAILED) system_error(); 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); munmap(pool_bmap, sb->s_blocksize); 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."); xid = spaceman_val_from_off(raw, le32_to_cpu(raw->sm_ip_bm_xid_offset)); 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_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); 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); /* All bitmaps will need to be read into memory */ sm->sm_bitmap = calloc(sm->sm_chunk_count, sb->s_blocksize); if (!sm->sm_bitmap) system_error(); parse_spaceman_main_device(raw); check_spaceman_tier2_device(raw); check_spaceman_free_queues(raw->sm_fq); check_internal_pool(raw); free(sb->s_ip_bitmap); if (raw->sm_fs_reserve_block_count || raw->sm_fs_reserve_alloc_count) report_unknown("Reserved allocation blocks"); compare_container_bitmaps(sm->sm_bitmap, sb->s_bitmap, sm->sm_chunk_count); munmap(raw, sb->s_blocksize); } /** * 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-master/apfsck/spaceman.h000066400000000000000000000033311411107746200175650ustar00rootroot00000000000000/* * 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; /* Space manager data in memory */ struct spaceman { void *sm_bitmap; /* Allocation bitmap for the whole container */ struct free_queue *sm_ip_fq; /* Free queue for internal pool */ struct free_queue *sm_main_fq; /* Free queue for main device */ int sm_struct_size; /* Size of the spaceman structure on disk */ /* 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; u32 sm_cib_count; u64 sm_chunk_count; u64 sm_ip_base; u64 sm_ip_block_count; /* Spaceman info measured by the fsck */ u64 sm_chunks; /* Number of chunks */ u64 sm_blocks; /* Number of blocks */ u64 sm_free; /* Number of free blocks */ }; /* * 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-master/apfsck/super.c000066400000000000000000001017561411107746200171410ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include #include #include #include #include "apfsck.h" #include "btree.h" #include "extents.h" #include "htable.h" #include "inode.h" #include "object.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, APFS_NX_BLOCK_NUM * bsize_tmp); if (msb_raw == MAP_FAILED) system_error(); 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, APFS_NX_BLOCK_NUM * sb->s_blocksize); if (msb_raw == MAP_FAILED) system_error(); } if (le32_to_cpu(msb_raw->nx_magic) != APFS_NX_MAGIC) report("Block zero", "wrong magic."); 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, 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; } /** * 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 of the device or image being checked * @blocksize: the filesystem blocksize */ static u64 get_device_size(unsigned int blocksize) { struct stat buf; u64 size; if (fstat(fd, &buf)) system_error(); if ((buf.st_mode & S_IFMT) == S_IFREG) return buf.st_size / blocksize; if (ioctl(fd, BLKGETSIZE64, &size)) system_error(); return size / blocksize; } /** * 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("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) report_unknown("Low-capacity 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 (flags & APFS_NX_INCOMPAT_FUSION) report_unknown("Fusion drive"); } /** * 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, sb->s_blocksize); } /** * 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; /* TODO: support for small containers is very important */ if (container_size < 128 * 1024 * 1024) report_unknown("Small container size"); 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, and only null * characters afterwards. */ static int software_strlen(u8 *str) { int length = strnlen((char *)str, APFS_MODIFIED_NAMELEN); u8 *end = str + APFS_MODIFIED_NAMELEN; if (length == APFS_MODIFIED_NAMELEN) report("Volume software id", "no NULL-termination."); for (str += length + 1; str != end; ++str) { if (*str) report("Volume software id", "goes on after NULL."); } 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 */ 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); } length = software_strlen(formatted_by->id); if (!length) report("Volume superblock", "creation information is missing."); vsb->v_first_xid = le64_to_cpu(formatted_by->last_xid); 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) { 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)) report_unknown("Encryption"); 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"); if (flags & APFS_FS_ALWAYS_CHECK_EXTENTREF) report_unknown("Forced extent reference checks"); } /** * 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_DATALESS_SNAPS) report_unknown("Dataless snapshots"); if (flags & APFS_INCOMPAT_ENC_ROLLED) report_unknown("Change of encryption keys"); if ((bool)(flags & APFS_INCOMPAT_CASE_INSENSITIVE) && (bool)(flags & APFS_INCOMPAT_NORMALIZATION_INSENSITIVE)) report("Volume superblock", "redundant flag for case sensitivity."); } /** * 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 & APFS_VOL_ROLES_VALID_MASK) != role) report("Volume superblock", "invalid role in use."); if (role & APFS_VOL_ROLE_RESERVED_200) report("Volume superblock", "reserved role in use."); } /** * 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) { 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("Encryption"); if (le16_to_cpu(wmcs->key_revision) != 1) /* Key has been changed */ report_unknown("Encryption"); if (wmcs->unused) report("Volume meta_crypto", "reserved field in use."); } /** * 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; char *vol_name; u64 vol_id; vol_id = le64_to_cpu(msb_raw->nx_fs_oid[vol]); if (vol_id == 0) { if (vol > sb->s_max_vols) report("Container superblock", "too many volumes."); for (++vol; vol < APFS_NX_MAX_FILE_SYSTEMS; ++vol) if (msb_raw->nx_fs_oid[vol]) report("Container superblock", "volume array goes on after NULL."); return NULL; } vsb->v_raw = read_object(vol_id, sb->s_omap_table, &vsb->v_obj); 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."); if (le32_to_cpu(vsb->v_raw->apfs_fs_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)); if (vsb->v_raw->apfs_fs_reserve_block_count) report_unknown("Volume block reservation"); if (vsb->v_raw->apfs_fs_quota_block_count) report_unknown("Volume block 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."); 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"); if (le64_to_cpu(vsb->v_raw->apfs_num_snapshots) != 0) report_unknown("Snapshots"); return vsb->v_raw; } static struct object *parse_reaper(u64 oid); /** * check_container - Check the whole container for a given checkpoint * @sb: checkpoint superblock */ static void check_container(struct super_block *sb) { int vol; 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)); for (vol = 0; vol < APFS_NX_MAX_FILE_SYSTEMS; ++vol) { struct apfs_superblock *vsb_raw; vsb = calloc(1, sizeof(*vsb)); if (!vsb) system_error(); vsb->v_omap_table = alloc_htable(); vsb->v_extent_table = alloc_htable(); vsb->v_cnid_table = alloc_htable(); vsb->v_dstream_table = alloc_htable(); vsb->v_inode_table = alloc_htable(); vsb_raw = map_volume_super(vol, vsb); if (!vsb_raw) { free(vsb); break; } /* Check for corruption in the volume object map... */ vsb->v_omap = parse_omap_btree( le64_to_cpu(vsb_raw->apfs_omap_oid)); /* ...in the extent reference tree... */ vsb->v_extent_ref = parse_extentref_btree( le64_to_cpu(vsb_raw->apfs_extentref_tree_oid)); /* ...in the catalog... */ vsb->v_cat = parse_cat_btree( le64_to_cpu(vsb_raw->apfs_root_tree_oid), vsb->v_omap_table); /* ...and in the snapshot metadata tree */ vsb->v_snap_meta = parse_snap_meta_btree( le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid)); 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; free_extent_table(vsb->v_extent_table); vsb->v_extent_table = NULL; free_omap_table(vsb->v_omap_table); vsb->v_omap_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."); if (le64_to_cpu(vsb_raw->apfs_fs_alloc_count) != vsb->v_block_count - 1) /* The volume superblock itself does not count */ report("Volume superblock", "bad block count."); sb->s_volumes[vol] = vsb; } vsb = NULL; free_omap_table(sb->s_omap_table); sb->s_omap_table = NULL; check_spaceman(le64_to_cpu(sb->s_raw->nx_spaceman_oid)); } /** * 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 chunk_count; u64 keybag_bno, keybag_blocks; int i; 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."); if (sb->s_block_count > get_device_size(sb->s_blocksize)) report("Container superblock", "too many blocks for device."); /* * A chunk is the disk section covered by a single block in the * allocation bitmap. */ chunk_count = DIV_ROUND_UP(sb->s_block_count, 8 * sb->s_blocksize); sb->s_bitmap = calloc(chunk_count, sb->s_blocksize); if (!sb->s_bitmap) system_error(); ((char *)sb->s_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]); for (i = 0; i < 16; ++i) { if (sb->s_raw->nx_fusion_uuid[i]) report_unknown("Fusion drive"); } /* Containers with no encryption may still have a value here, why? */ keybag_bno = le64_to_cpu(sb->s_raw->nx_keylocker.pr_start_paddr); keybag_blocks = le64_to_cpu(sb->s_raw->nx_keylocker.pr_block_count); if (keybag_bno || keybag_blocks) report_weird("Container keybag"); container_bmap_mark_as_used(keybag_bno, keybag_blocks); if (sb->s_raw->nx_fusion_mt_oid || sb->s_raw->nx_fusion_wbc_oid || sb->s_raw->nx_fusion_wbc.pr_start_paddr || sb->s_raw->nx_fusion_wbc.pr_block_count) report_unknown("Fusion drive"); 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."); } /** * parse_cpoint_map - Parse and verify a checkpoint mapping * @raw: the raw checkpoint mapping */ static void parse_cpoint_map(struct apfs_checkpoint_mapping *raw) { struct cpoint_map *map; 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); map->m_size = le32_to_cpu(raw->cpm_size); if (map->m_size != sb->s_blocksize) report_unknown("Ephemeral objects with more than one block"); map->m_type = le32_to_cpu(raw->cpm_type); map->m_subtype = le32_to_cpu(raw->cpm_subtype); if (raw->cpm_pad) report("Checkpoint map", "non-zero padding."); if (raw->cpm_fs_oid) report_unknown("Ephemeral object belonging to a volume"); } /** * parse_cpoint_map_blocks - Parse and verify a checkpoint's mapping blocks * @desc_base: first block of the checkpoint descriptor area * @desc_blocks: block count of the checkpoint descriptor area * @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(u64 desc_base, u32 desc_blocks, u32 *index) { struct object obj; struct apfs_checkpoint_map_phys *raw; u32 blk_count = 0; u32 cpm_count; /* * 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(); while (1) { u64 bno = desc_base + *index; u32 flags; int i; raw = read_object_nocheck(bno, &obj); if (obj.oid != bno) report("Checkpoint map", "wrong object id."); if (parse_object_flags(obj.flags) != 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) parse_cpoint_map(&raw->cpm_map[i]); flags = le32_to_cpu(raw->cpm_flags); munmap(raw, sb->s_blocksize); blk_count++; *index = (*index + 1) % desc_blocks; if ((flags & APFS_CHECKPOINT_MAP_LAST) != flags) report("Checkpoint map", "invalid flag in use."); if (flags & APFS_CHECKPOINT_MAP_LAST) return blk_count; if (blk_count == desc_blocks) report("Checkpoint", "no mapping block marked last."); } } /** * parse_filesystem - Parse the whole filesystem looking for corruption */ void parse_filesystem(void) { struct apfs_nx_superblock *msb_raw_copy, *msb_raw_latest; u64 desc_base; u32 desc_blocks; long long valid_blocks; u32 desc_next, desc_index, 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 want to mount the latest valid checkpoint among the descriptors */ desc_base = le64_to_cpu(msb_raw_copy->nx_xp_desc_base); if (desc_base >> 63 != 0) { /* The highest bit is set when checkpoints are not contiguous */ report("Block zero", "checkpoint descriptor tree not yet supported."); } desc_blocks = le32_to_cpu(msb_raw_copy->nx_xp_desc_blocks); if (desc_blocks > 10000) /* Arbitrary loop limit, is it enough? */ report("Block zero", "too many checkpoint descriptors?"); /* Find the valid range, as reported by the latest descriptor */ msb_raw_latest = read_latest_super(desc_base, desc_blocks); desc_next = le32_to_cpu(msb_raw_latest->nx_xp_desc_next); desc_index = le32_to_cpu(msb_raw_latest->nx_xp_desc_index); if (desc_next >= desc_blocks || desc_index >= desc_blocks) report("Checkpoint superblock", "out of range checkpoint descriptors."); munmap(msb_raw_latest, sb->s_blocksize); msb_raw_latest = NULL; /* * Now go through the valid checkpoints one by one, though it seems * that cleanly unmounted filesystems only preserve the last one. */ index = desc_index; valid_blocks = (desc_blocks + desc_next - desc_index) % 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_bitmap); /* The checkpoint-mapping blocks come before the superblock */ map_blocks = parse_cpoint_map_blocks(desc_base, desc_blocks, &index); valid_blocks -= map_blocks; bno = desc_base + index; raw = read_object_nocheck(bno, &obj); if (parse_object_flags(obj.flags) != 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(desc_base, 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) % 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); 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."); /* Docs on the reaper are very incomplete, so let's hope it's empty */ if (raw->nr_completed_id || raw->nr_head || raw->nr_tail || raw->nr_rlcount || raw->nr_type || raw->nr_size || raw->nr_oid || raw->nr_xid || raw->nr_nrle_flags) report_unknown("Nonempty reaper"); if (le64_to_cpu(raw->nr_next_reap_id) != 1) report_unknown("Nonempty reaper"); 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("Nonempty reaper"); if (raw->nr_fs_oid) report_unknown("Reaper belonging to a volume"); buffer_size = le32_to_cpu(raw->nr_state_buffer_size); if (buffer_size != sb->s_blocksize - sizeof(*raw)) report("Reaper", "wrong state buffer size."); for (i = 0; i < buffer_size; ++i) { if (raw->nr_state_buffer[i]) report_unknown("Nonempty reaper"); } munmap(raw, sb->s_blocksize); return reaper; } apfsprogs-master/apfsck/super.h000066400000000000000000000071541411107746200171430ustar00rootroot00000000000000/* * 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_superblock { struct apfs_superblock *v_raw; struct btree *v_omap; struct btree *v_cat; struct btree *v_extent_ref; struct btree *v_snap_meta; 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 */ /* 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 */ 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_first_xid; /* Transaction that created the volume */ u64 v_next_obj_id; /* Next cnid to be assigned */ u32 v_next_doc_id; /* Next document identifier to be assigned */ struct object v_obj; /* Object holding the volume sb */ }; /* Superblock data in memory */ struct super_block { struct apfs_nx_superblock *s_raw; void *s_bitmap; /* Allocation bitmap for the whole container */ void *s_ip_bitmap; /* Allocation bitmap for the internal pool */ 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_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 */ /* 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 */ /* 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; } extern void parse_filesystem(void); #endif /* _SUPER_H */ apfsprogs-master/apfsck/xattr.c000066400000000000000000000103411411107746200171320ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include "apfsck.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 ((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; size = le64_to_cpu(dstream_raw->size); alloced_size = le64_to_cpu(dstream_raw->alloced_size); if (dstream_raw->default_crypto_id) report_unknown("Dstream encryption"); 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_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; 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 apfs_xattr_dstream *dstream_raw; struct dstream *dstream; 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; } 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."); } 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; } } apfsprogs-master/apfsck/xattr.h000066400000000000000000000004771411107746200171500ustar00rootroot00000000000000/* * 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-master/include/000077500000000000000000000000001411107746200160015ustar00rootroot00000000000000apfsprogs-master/include/apfs/000077500000000000000000000000001411107746200167325ustar00rootroot00000000000000apfsprogs-master/include/apfs/checksum.h000066400000000000000000000004371411107746200207110ustar00rootroot00000000000000/* * 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-master/include/apfs/parameters.h000066400000000000000000000004021411107746200212420ustar00rootroot00000000000000/* * 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-master/include/apfs/raw.h000066400000000000000000000702751411107746200177070ustar00rootroot00000000000000/* * 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_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; /* B-tree node flags */ #define APFS_BTNODE_ROOT 0x0001 #define APFS_BTNODE_LEAF 0x0002 #define APFS_BTNODE_FIXED_KV_SIZE 0x0004 #define APFS_BTNODE_CHECK_KOFF_INVAL 0x8000 #define APFS_BTNODE_MASK 0x0007 /* 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_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) /* * 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 */ }; /* * 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 /* * 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; /* 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 */ /* Smallest inode number available for user content */ #define APFS_MIN_USER_INO_NUM 16 /* 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 /* Masks for internal flags */ #define APFS_VALID_INTERNAL_INODE_FLAGS 0x0001ffdf #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) /* * 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 pad2; /*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 /* 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_MAX_VALID = 12, 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; /* 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 }; /* * 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 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 { 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[]; }; /* 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[]; }; /* 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; } __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_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) #define APFS_FS_CRYPTOFLAGS (APFS_FS_UNENCRYPTED \ | APFS_FS_EFFACEABLE \ | APFS_FS_ONEKEY) /* Volume roles */ #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 0x0040 #define APFS_VOL_ROLE_BASEBAND 0x0080 #define APFS_VOL_ROLE_RESERVED_200 0x0200 #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_RESERVED_200) /* Optional volume feature flags */ #define APFS_FEATURE_DEFRAG_PRERELEASE 0x00000001LL #define APFS_FEATURE_HARDLINK_MAP_RECORDS 0x00000002LL #define APFS_FEATURE_DEFRAG 0x00000004LL #define APFS_SUPPORTED_FEATURES_MASK (APFS_FEATURE_DEFRAG \ | APFS_FEATURE_DEFRAG_PRERELEASE \ | APFS_FEATURE_HARDLINK_MAP_RECORDS) /* 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_SUPPORTED_INCOMPAT_MASK (APFS_INCOMPAT_CASE_INSENSITIVE \ | APFS_INCOMPAT_DATALESS_SNAPS \ | APFS_INCOMPAT_ENC_ROLLED \ | APFS_INCOMPAT_NORMALIZATION_INSENSITIVE) #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 */ /* * 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; } __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" /* 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, }; #define APFS_XATTR_VALID_FLAGS 0x0000000f /* * 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; #endif /* _RAW_H */ apfsprogs-master/include/apfs/types.h000066400000000000000000000023301411107746200202450ustar00rootroot00000000000000/* * 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 #define EAGAIN 1 #define ENODATA 2 #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)) #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 NSEC_PER_SEC 1000000000L typedef u32 unicode_t; #endif /* _TYPES_H */ apfsprogs-master/include/apfs/unicode.h000066400000000000000000000013611411107746200205320ustar00rootroot00000000000000/* * 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-master/lib/000077500000000000000000000000001411107746200151245ustar00rootroot00000000000000apfsprogs-master/lib/Makefile000066400000000000000000000007241411107746200165670ustar00rootroot00000000000000SRCS = checksum.c parameters.c unicode.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 $<...' @gcc $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) libapfs.a apfsprogs-master/lib/checksum.c000066400000000000000000000105561411107746200171010ustar00rootroot00000000000000/* * 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-master/lib/parameters.c000066400000000000000000000021241411107746200174320ustar00rootroot00000000000000/* * 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-master/lib/unicode.c000066400000000000000000004651561411107746200167370ustar00rootroot00000000000000/* * 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-master/mkapfs/000077500000000000000000000000001411107746200156375ustar00rootroot00000000000000apfsprogs-master/mkapfs/Makefile000066400000000000000000000020211411107746200172720ustar00rootroot00000000000000SRCS = 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 BINDIR = ~/bin MANDIR = ~/share/man/man8 SPARSE_VERSION := $(shell sparse --version 2>/dev/null) override CFLAGS += -Wall -Wno-address-of-packed-member -fno-strict-aliasing -I$(CURDIR)/../include mkapfs: $(OBJS) $(LIBRARY) @echo ' Linking...' @gcc $(CFLAGS) -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 $<...' @gcc $(CFLAGS) -o $@ -MMD -MP -c $< ifdef SPARSE_VERSION @sparse $(CFLAGS) $< endif -include $(DEPS) clean: rm -f $(OBJS) $(DEPS) mkapfs install: install -d $(BINDIR) install -t $(BINDIR) mkapfs ln -fs -T $(BINDIR)/mkapfs $(BINDIR)/mkfs.apfs install -d $(MANDIR) install -m 644 -t $(MANDIR) mkapfs.8 ln -fs -T $(MANDIR)/mkapfs.8 $(MANDIR)/mkfs.apfs.8 apfsprogs-master/mkapfs/btree.c000066400000000000000000000251411411107746200171070ustar00rootroot00000000000000/* * 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 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); if (subtype == APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE) { /* The other two trees don't have fixed key/value sizes */ 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); } 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; 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) 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, 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, 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, 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, oid, APFS_OBJECT_TYPE_BTREE | APFS_OBJ_VIRTUAL, APFS_OBJECT_TYPE_FSTREE); munmap(root, param->blocksize); } apfsprogs-master/mkapfs/btree.h000066400000000000000000000005131411107746200171100ustar00rootroot00000000000000/* * 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-master/mkapfs/dir.c000066400000000000000000000150321411107746200165620ustar00rootroot00000000000000/* * 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-master/mkapfs/dir.h000066400000000000000000000007771411107746200166010ustar00rootroot00000000000000/* * 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-master/mkapfs/mkapfs.8000066400000000000000000000024461411107746200172170ustar00rootroot00000000000000.\" mkapfs.8 - manpage for mkapfs .\" .\" Copyright (C) 2019 Ernesto A. Fernández .\" .TH mkapfs 8 "May 2019" "apfsprogs 0.1" .SH NAME mkapfs \- create an APFS filesystem .SH SYNOPSIS .B mkapfs [\-sv] [\-L .IR label ] [\-U .IR UUID ] [\-u .IR UUID ] .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 in the container 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. .SH REPORTING BUGS Please submit any issues that you find to the project's mailing list at . .SH AUTHOR Written by Ernesto A. Fernández . .SH SEE ALSO .BR apfsck (8) apfsprogs-master/mkapfs/mkapfs.c000066400000000000000000000101741411107746200172670ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include #include #include #include #include #include "mkapfs.h" #include "super.h" int fd; 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] [-sv] " "device [blocks]\n", progname); exit(1); } /** * version - Print version information and exit */ static void version(void) { printf("mkapfs version 0.1\n"); exit(1); } /** * system_error - Print a system error message and exit */ __attribute__((noreturn)) void system_error(void) { perror(progname); exit(1); } /** * 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(1); } /** * get_device_size - Get the block count of the device or image being checked * @blocksize: the filesystem blocksize */ static u64 get_device_size(unsigned int blocksize) { struct stat buf; u64 size; if (fstat(fd, &buf)) system_error(); if ((buf.st_mode & S_IFMT) == S_IFREG) return buf.st_size / blocksize; if (ioctl(fd, BLKGETSIZE64, &size)) system_error(); return size / 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 dev_block_count; if (!param->blocksize) param->blocksize = APFS_NX_DEFAULT_BLOCK_SIZE; dev_block_count = get_device_size(param->blocksize); if (!param->block_count) param->block_count = dev_block_count; if (param->block_count > dev_block_count) { fprintf(stderr, "%s: device is not big enough\n", progname); exit(1); } if (param->block_count * param->blocksize < 128 * 1024 * 1024) { fprintf(stderr, "%s: small containers are not supported\n", progname); exit(1); } /* 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) { fprintf(stderr, "%s: volume label is too long\n", progname); exit(1); } if (!param->main_uuid) param->main_uuid = get_random_uuid(); if (!param->vol_uuid) param->vol_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:szv"); 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(); default: usage(); } } if (optind == argc - 2) { filename = argv[optind]; /* TODO: reject malformed numbers? */ param->block_count = atoll(argv[optind + 1]); } else if (optind == argc - 1) { filename = argv[optind]; } else { usage(); } fd = open(filename, O_RDWR); if (fd == -1) system_error(); complete_parameters(); make_container(); return 0; } apfsprogs-master/mkapfs/mkapfs.h000066400000000000000000000073171411107746200173010ustar00rootroot00000000000000/* * 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 */ char *label; /* Volume label */ char *main_uuid; /* Container UUID in standard format */ char *vol_uuid; /* Volume UUID in standard format */ bool case_sensitive; /* Is the filesystem case-sensitive? */ bool norm_sensitive; /* Is it normalization-sensitive? */ }; /* String to identify the program and its version */ #define MKFS_ID_STRING "mkapfs for linux, version 0.1" /* 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) /* * Constants describing the checkpoint areas; these are hardcoded for now, but * should actually change with the container size. */ #define CPOINT_DESC_BASE (APFS_NX_BLOCK_NUM + 1) #define CPOINT_DESC_BLOCKS 64 #define CPOINT_DATA_BASE (CPOINT_DESC_BASE + CPOINT_DESC_BLOCKS) #define CPOINT_DATA_BLOCKS 5904 /* * Constants describing the internal pool; these are hardcoded for now, but * should actually change with the container size. */ #define IP_BMAP_BASE 21000 /* First ip bitmap */ #define IP_BMAP_BLOCKS 16 /* Ip bitmap count */ #define IP_BASE (IP_BMAP_BASE + IP_BMAP_BLOCKS) /* Start of pool */ /* Hardcoded block numbers */ #define CPOINT_MAP_BNO CPOINT_DESC_BASE #define CPOINT_SB_BNO (CPOINT_DESC_BASE + 1) #define REAPER_BNO CPOINT_DATA_BASE #define SPACEMAN_BNO (CPOINT_DATA_BASE + 1) #define IP_FREE_QUEUE_BNO (CPOINT_DATA_BASE + 2) #define MAIN_FREE_QUEUE_BNO (CPOINT_DATA_BASE + 3) #define MAIN_OMAP_BNO 20000 #define MAIN_OMAP_ROOT_BNO 20001 #define FIRST_VOL_BNO 20002 #define FIRST_VOL_OMAP_BNO 20003 #define FIRST_VOL_OMAP_ROOT_BNO 20004 #define FIRST_VOL_CAT_ROOT_BNO 20005 #define FIRST_VOL_EXTREF_ROOT_BNO 20006 #define FIRST_VOL_SNAP_ROOT_BNO 20007 /* Declarations for global variables */ extern struct parameters *param; /* Filesystem parameters */ extern int fd; /* File descriptor for the device */ extern __attribute__((noreturn)) void system_error(void); extern __attribute__((noreturn)) void fatal(const char *message); /** * 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 = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 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-master/mkapfs/object.c000066400000000000000000000017071411107746200172560ustar00rootroot00000000000000/* * 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 * @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, u64 oid, u32 type, u32 subtype) { char *after_cksum = (char *)obj + APFS_MAX_CKSUM_SIZE; int after_cksum_len = param->blocksize - 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-master/mkapfs/object.h000066400000000000000000000004461411107746200172620ustar00rootroot00000000000000/* * 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, u64 oid, u32 type, u32 subtype); #endif /* _OBJECT_H */ apfsprogs-master/mkapfs/spaceman.c000066400000000000000000000277061411107746200176060ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include "btree.h" #include "mkapfs.h" #include "object.h" #include "spaceman.h" /* Extra information about the space manager */ static struct spaceman_info { u64 chunk_count; u32 cib_count; u64 ip_blocks; 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 */ u64 first_cib; /* Block number for first chunk-info block */ } sm_info; /** * 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); } #define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) /** * count_used_blocks_in_chunk - Calculate number of allocated blocks in a chunk * @chunkno: chunk number to check */ static u32 count_used_blocks_in_chunk(u64 chunkno) { u32 first_chunk_ip_blocks; if (chunkno >= sm_info.used_chunks_end) return 0; /* The internal pool may not fit whole in the chunk */ first_chunk_ip_blocks = MIN(sm_info.ip_blocks, blocks_per_chunk() - 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 += IP_BMAP_BLOCKS; /* Internal pool bitmap blocks */ blocks += first_chunk_ip_blocks; return blocks; } /* Later chunks are only needed for the rest of the internal pool */ if (chunkno != sm_info.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 - Calculate the number of blocks used by the mkfs */ static u32 count_used_blocks(void) { u32 blocks = 0; u64 chunkno; for (chunkno = 0; chunkno < sm_info.used_chunks_end; ++chunkno) blocks += count_used_blocks_in_chunk(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_alloc_bitmap - Make the allocation bitmap for the first chunk */ static void make_alloc_bitmap(void) { void *bmap = get_zeroed_blocks(sm_info.first_chunk_bmap, sm_info.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, IP_BMAP_BLOCKS); /* Internal pool blocks */ bmap_mark_as_used(bmap, IP_BASE, sm_info.ip_blocks); munmap(bmap, sm_info.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 */ #define BITMAP_OFF 0x158 /* Address of the ip bitmap */ #define BITMAP_FREE_NEXT_OFF 0x160 /* No idea */ #define CIB_ADDR_BASE_OFF 0x180 /* First cib address for main device */ /** * make_chunk_info - Write a chunk info structure * @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 apfs_chunk_info *chunk, u64 start) { u64 remaining_blocks = param->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 < sm_info.used_blocks_end) chunk->ci_bitmap_addr = cpu_to_le64(sm_info.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(chunkno); chunk->ci_free_count = cpu_to_le32(free_count); start += block_count; return start; } /** * make_chunk_info_block - Make a chunk-info block * @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(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 == param->block_count) /* No more chunks in device */ break; start = make_chunk_info(&cib->cib_chunk_info[i], start); } cib->cib_chunk_info_count = cpu_to_le32(i); set_object_header(&cib->cib_o, bno, APFS_OBJ_PHYSICAL | APFS_OBJECT_TYPE_SPACEMAN_CIB, APFS_OBJECT_TYPE_INVALID); munmap(cib, param->blocksize); return 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) { struct apfs_spaceman_device *dev = &sm->sm_dev[APFS_SD_MAIN]; u64 chunk_count = DIV_ROUND_UP(param->block_count, blocks_per_chunk()); u32 cib_count = DIV_ROUND_UP(chunk_count, chunks_per_cib()); u64 start = 0; __le64 *cib_addr; int i; /* * We must have room for the addresses of all main device cibs, plus * an extra offset for tier 2. */ if (cib_count + 1 > (param->blocksize - CIB_ADDR_BASE_OFF) / sizeof(__le64)) { printf("Large containers are not yet supported.\n"); exit(1); } dev->sm_block_count = cpu_to_le64(param->block_count); dev->sm_chunk_count = cpu_to_le64(chunk_count); dev->sm_cib_count = cpu_to_le32(cib_count); dev->sm_cab_count = 0; /* Not supported, hence the block count limit */ dev->sm_free_count = cpu_to_le64(param->block_count - count_used_blocks()); dev->sm_addr_offset = cpu_to_le32(CIB_ADDR_BASE_OFF); cib_addr = (void *)sm + CIB_ADDR_BASE_OFF; for (i = 0; i < cib_count; ++i) { cib_addr[i] = cpu_to_le64(sm_info.first_cib + i); start = make_chunk_info_block(sm_info.first_cib + i, i, start); } /* For the tier2 device, just set the offset; the address is null */ dev = &sm->sm_dev[APFS_SD_TIER2]; dev->sm_addr_offset = cpu_to_le32(CIB_ADDR_BASE_OFF + cib_count * sizeof(__le64)); } /** * 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(IP_FREE_QUEUE_BNO, IP_FREE_QUEUE_OID, APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); fq->sfq_oldest_xid = 0; /* Is this correct? */ fq->sfq_tree_node_limit = cpu_to_le16(ip_fq_node_limit(sm_info.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(MAIN_FREE_QUEUE_BNO, MAIN_FREE_QUEUE_OID, APFS_OBJECT_TYPE_SPACEMAN_FREE_QUEUE); fq->sfq_oldest_xid = 0; /* Is this correct? */ fq->sfq_tree_node_limit = cpu_to_le16(main_fq_node_limit(param->block_count)); } /** * make_ip_bitmap - Make the allocation bitmap for the internal pool */ static void make_ip_bitmap(void) { void *bmap = get_zeroed_block(IP_BMAP_BASE); /* Chunk-info blocks */ bmap_mark_as_used(bmap, sm_info.first_cib - IP_BASE, sm_info.cib_count); /* Allocation bitmap block */ bmap_mark_as_used(bmap, sm_info.first_chunk_bmap - IP_BASE, sm_info.used_chunks_end); munmap(bmap, param->blocksize); } /** * make_ip_bm_free_next - Fill the free_next field for the internal pool * @addr: pointer to the beginning of the field */ static void make_ip_bm_free_next(__le16 *addr) { int i; /* * Ip bitmap blocks are marked with numbers 1,2,3,...,14,15,0 in * free_next, except when they are in use: those get overwritten with * 0xFFFF. */ addr[0] = cpu_to_le16(0xFFFF); for (i = 1; i < IP_BMAP_BLOCKS - 1; i++) addr[i] = cpu_to_le16(i + 1); addr[IP_BMAP_BLOCKS - 1] = cpu_to_le16(0xFFFF); } /** * 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; 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(IP_BASE); /* No support for multiblock bitmaps */ sm->sm_ip_bm_size_in_blocks = cpu_to_le32(1); sm->sm_ip_bm_block_count = cpu_to_le32(IP_BMAP_BLOCKS); sm->sm_ip_bm_base = cpu_to_le64(IP_BMAP_BASE); for (i = 0; i < IP_BMAP_BLOCKS; ++i) /* We use no blocks from the ip */ munmap(get_zeroed_block(IP_BMAP_BASE + i), param->blocksize); /* Current bitmap is the first, so the offset is left at zero */ sm->sm_ip_bitmap_offset = cpu_to_le32(BITMAP_OFF); sm->sm_ip_bm_free_head = cpu_to_le16(1); sm->sm_ip_bm_free_tail = cpu_to_le16(IP_BMAP_BLOCKS - 1); sm->sm_ip_bm_xid_offset = cpu_to_le32(BITMAP_XID_OFF); addr = (void *)sm + BITMAP_XID_OFF; *addr = cpu_to_le64(MKFS_XID); sm->sm_ip_bm_free_next_offset = cpu_to_le32(BITMAP_FREE_NEXT_OFF); make_ip_bm_free_next((void *)sm + BITMAP_FREE_NEXT_OFF); make_ip_bitmap(); } /** * 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 = get_zeroed_block(bno); sm_info.chunk_count = DIV_ROUND_UP(param->block_count, blocks_per_chunk()); sm_info.cib_count = DIV_ROUND_UP(sm_info.chunk_count, chunks_per_cib()); sm_info.ip_blocks = (sm_info.chunk_count + sm_info.cib_count) * 3; /* Only the ip size matters, all other used blocks come before it */ sm_info.used_blocks_end = IP_BASE + sm_info.ip_blocks; sm_info.used_chunks_end = DIV_ROUND_UP(sm_info.used_blocks_end, blocks_per_chunk()); /* * Put the chunk bitmaps at the beginning of the internal pool, and * the cibs right after them. */ sm_info.first_chunk_bmap = IP_BASE; sm_info.first_cib = sm_info.first_chunk_bmap + sm_info.used_chunks_end; 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]); make_internal_pool(sm); make_alloc_bitmap(); set_object_header(&sm->sm_o, oid, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_SPACEMAN, APFS_OBJECT_TYPE_INVALID); munmap(sm, param->blocksize); } apfsprogs-master/mkapfs/spaceman.h000066400000000000000000000003061411107746200175760ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _SPACEMAN_H #define _SPACEMAN_H extern void make_spaceman(u64 bno, u64 oid); #endif /* _SPACEMAN_H */ apfsprogs-master/mkapfs/super.c000066400000000000000000000253521411107746200171500ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #include #include #include #include #include #include "btree.h" #include "mkapfs.h" #include "object.h" #include "spaceman.h" #include "super.h" /** * 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; printf("Please provide a UUID in standard format.\n"); exit(1); } /** * 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 */ sb->nx_xp_data_len = cpu_to_le32(4); sb->nx_xp_data_next = cpu_to_le32(4); 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) { /* TODO: add support for small containers */ u64 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, 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; block->cpm_flags = cpu_to_le32(APFS_CHECKPOINT_MAP_LAST); block->cpm_count = cpu_to_le32(4); /* Reaper, spaceman, free queues */ /* Set the checkpoint mapping for the reaper */ map = &block->cpm_map[0]; 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(REAPER_BNO); /* Set the checkpoint mapping for the space manager */ map = &block->cpm_map[1]; 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(param->blocksize); map->cpm_oid = cpu_to_le64(SPACEMAN_OID); map->cpm_paddr = cpu_to_le64(SPACEMAN_BNO); /* Set the checkpoint mapping for the internal-pool free queue root */ map = &block->cpm_map[2]; 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(IP_FREE_QUEUE_BNO); /* Set the checkpoint mapping for the main device free queue root */ map = &block->cpm_map[3]; 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(MAIN_FREE_QUEUE_BNO); set_object_header(&block->cpm_o, 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_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, oid, APFS_OBJ_EPHEMERAL | APFS_OBJECT_TYPE_NX_REAPER, APFS_OBJECT_TYPE_INVALID); munmap(reaper, param->blocksize); } /** * make_container - Make the whole filesystem */ void make_container(void) { struct apfs_nx_superblock *sb_copy; 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); set_uuid(sb_copy->nx_uuid, param->main_uuid); /* 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); set_checkpoint_areas(sb_copy); sb_copy->nx_spaceman_oid = cpu_to_le64(SPACEMAN_OID); make_spaceman(SPACEMAN_BNO, SPACEMAN_OID); sb_copy->nx_reaper_oid = cpu_to_le64(REAPER_OID); make_empty_reaper(REAPER_BNO, REAPER_OID); sb_copy->nx_omap_oid = cpu_to_le64(MAIN_OMAP_BNO); make_omap_btree(MAIN_OMAP_BNO, false /* is_vol */); 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, 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); munmap(sb_copy, param->blocksize); } apfsprogs-master/mkapfs/super.h000066400000000000000000000002621411107746200171460ustar00rootroot00000000000000/* * Copyright (C) 2019 Ernesto A. Fernández */ #ifndef _SUPER_H #define _SUPER_H extern void make_container(void); #endif /* _SUPER_H */