commit 72e6425af6b8ed6fa55f0aa0b178b1fe130e8031 Author: Ernesto A. Fernández Date: Sat Mar 2 23:50:42 2019 -0300 Set up a standalone repository for the APFS module Copy the code of the APFS module into its own repository, without the rest of the kernel tree. Development will continue upstream, but the intention is to make life easier for potential users. To get the module to build independently, rewrite the Makefile and add a definition for the APFS_SUPER_MAGIC macro. Since the intention is to support a range of kernel versions, use preprocessor checks to handle kernels without statx, without iversion, and without SB_RDONLY. Provide a README file based on the upstream documentation, but with additional build and mount instructions. Add a LICENSE file as well. Signed-off-by: Ernesto A. Fernández diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf3e7d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.* +*.swp +*.d +*.o +*.symvers +*.mod.c +*.order +*.ko +tags diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ffa1269 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the out-of-tree Linux APFS module. +# + +obj-m = apfs.o +apfs-y = btree.o dir.o extents.o file.o inode.o key.o message.o \ + namei.o node.o object.o super.o symlink.o unicode.o xattr.o + +default: + make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(shell pwd) +clean: + make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(shell pwd) clean diff --git a/README b/README new file mode 100644 index 0000000..4cfa030 --- /dev/null +++ b/README @@ -0,0 +1,101 @@ + +Apple File System +================= + +The Apple File System (APFS) is a copy-on-write filesystem apparently intended +to replace HFS+ on all Apple devices in the near future. This module provides +a degree of experimental read-only support on Linux. + +This repo is a standalone version of +for the purpose of making it easier to build the module out-of-tree. It is +supposed to work with a range of kernel versions starting at 4.9 or before, but +only a few of those have actually been tested. If you run into any problem, +please send a report to . + +The long-term goal is to support writes as well. To help with testing, a set +of userland tools is under development. The git tree can be retrieved from +. + +Known limitations +================= + +This module is the result of reverse engineering. As it is read-only there +should be no risk of data corruption, but do not expect everything to be read +correctly. Testing in general has been limited so far, so you may experience +crashes. Please report any issues that you find. + +Apple has released other versions of the filesystem to the public before the +current one. I would not expect them to be compatible with this module at all, +but I am open to fixing that if requested. + +Many features are not yet supported: + + o Encryption. + o Compression, though the compressed contents of a file can be read from the + 'com.apple.decmpfs' and 'com.apple.ResourceFork' xattrs as long as they are + under 64k. + o Restoring to a snapshot. + o Access control lists. This is not a priority. + +Build +===== + +In order to build a module out-of-tree, you will first need the Linux kernel +headers. On Debian, you can get them by running (as root): + + apt-get install linux-headers-$(uname -r) + +Now you can just cd to the linux-apfs-oot directory and run + + make + +The resulting module is the apfs.ko file. Before you can use it you must insert +it into the kernel, as well as its dependencies. Again as root: + + modprobe libcrc32c + insmod apfs.ko + +Mount +===== + +Like all filesystems, apfs is mounted with + + mount [-o options] device dir + +where 'device' is the path to your device file or filesystem image, and 'dir' +is the mount point. The following options are accepted: + + vol=n + Volume number to mount. The default is volume 0. + + uid=n, gid=n + Override on-disk inode ownership data with given uid/gid. + + cknodes + Verify the checksum on all metadata nodes. Right now this has + a severe performance cost, so it's not recommended. + +So for instance, if you want to mount volume number 2, and you want the metadata +to be checked, you should run (as root): + + mount -o cknodes,vol=2 device dir + +To unmount it, run + + umount dir + +Credits +======= + +Originally written by Ernesto A. Fernández , +with several contributions from Gabriel Krisman Bertazi +and Arnaud Ferraris . + +Work was first based on reverse engineering done by others [1][2], and later +on the (very incomplete) official specification [3]. Some parts of the code +imitate the ext2 module, and to a lesser degree xfs, udf, gfs2 and hfsplus. + +[1] Hansen, K.H., Toolan, F., Decoding the APFS file system, Digital + Investigation (2017), http://dx.doi.org/10.1016/j.diin.2017.07.003 +[2] https://github.com/sgan81/apfs-fuse +[3] https://developer.apple.com/support/apple-file-system/Apple-File-System-Reference.pdf diff --git a/apfs.h b/apfs.h new file mode 100644 index 0000000..4df3399 --- /dev/null +++ b/apfs.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/apfs.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_H +#define _APFS_H + +#define EFSBADCRC EBADMSG /* Bad CRC detected */ +#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ + +/* + * Inode and file operations + */ + +/* file.c */ +extern const struct file_operations apfs_file_operations; +extern const struct inode_operations apfs_file_inode_operations; + +/* namei.c */ +extern const struct inode_operations apfs_dir_inode_operations; +extern const struct inode_operations apfs_special_inode_operations; +extern const struct dentry_operations apfs_dentry_operations; + +/* symlink.c */ +extern const struct inode_operations apfs_symlink_inode_operations; + +#endif /* _APFS_H */ diff --git a/btree.c b/btree.c new file mode 100644 index 0000000..30214f2 --- /dev/null +++ b/btree.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/btree.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include "apfs.h" +#include "btree.h" +#include "key.h" +#include "message.h" +#include "node.h" +#include "super.h" + +/** + * apfs_child_from_query - Read the child id found by a successful nonleaf query + * @query: the query that found the record + * @child: Return parameter. The child id found. + * + * Reads the child id in the nonleaf node record into @child and performs a + * basic sanity check as a protection against crafted filesystems. Returns 0 + * on success or -EFSCORRUPTED otherwise. + */ +static int apfs_child_from_query(struct apfs_query *query, u64 *child) +{ + char *raw = query->node->object.bh->b_data; + + if (query->len != 8) /* The data on a nonleaf node is the child id */ + return -EFSCORRUPTED; + + *child = le64_to_cpup((__le64 *)(raw + query->off)); + return 0; +} + +/** + * apfs_omap_lookup_block - Find the block number of a b-tree node from its id + * @sb: filesystem superblock + * @tbl: Root of the object map to be searched + * @id: id of the node + * @block: on return, the found block number + * + * Returns 0 on success or a negative error code in case of failure. + */ +int apfs_omap_lookup_block(struct super_block *sb, struct apfs_node *tbl, + u64 id, u64 *block) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_query *query; + struct apfs_key key; + int ret = 0; + + query = apfs_alloc_query(tbl, NULL /* parent */); + if (!query) + return -ENOMEM; + + apfs_init_omap_key(id, sbi->s_xid, &key); + query->key = &key; + query->flags |= APFS_QUERY_OMAP; + + ret = apfs_btree_query(sb, &query); + if (ret) + goto fail; + + ret = apfs_bno_from_query(query, block); + if (ret) + apfs_alert(sb, "bad object map leaf block: 0x%llx", + query->node->object.block_nr); + +fail: + apfs_free_query(sb, query); + return ret; +} + +/** + * apfs_alloc_query - Allocates a query structure + * @node: node to be searched + * @parent: query for the parent node + * + * Callers other than apfs_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, or NULL in case of failure. + */ +struct apfs_query *apfs_alloc_query(struct apfs_node *node, + struct apfs_query *parent) +{ + struct apfs_query *query; + + query = kmalloc(sizeof(*query), GFP_KERNEL); + if (!query) + return NULL; + + /* To be released by free_query. */ + apfs_node_get(node); + query->node = node; + query->key = parent ? parent->key : NULL; + query->flags = parent ? + parent->flags & ~(APFS_QUERY_DONE | APFS_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; +} + +/** + * apfs_free_query - Free a query structure + * @sb: filesystem superblock + * @query: query to free + * + * Also frees the ancestor queries, if they are kept. + */ +void apfs_free_query(struct super_block *sb, struct apfs_query *query) +{ + while (query) { + struct apfs_query *parent = query->parent; + + apfs_node_put(query->node); + kfree(query); + query = parent; + } +} + +/** + * apfs_btree_query - Execute a query on a b-tree + * @sb: filesystem superblock + * @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 an appropriate error code. + */ +int apfs_btree_query(struct super_block *sb, struct apfs_query **query) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_node *node; + struct apfs_query *parent; + u64 child_id, child_blk; + int err; + +next_node: + if ((*query)->depth >= 12) { + /* + * We need a maximum depth for the tree so we can't loop + * forever if the filesystem is damaged. 12 should be more + * than enough to map every block. + */ + apfs_alert(sb, "b-tree is corrupted"); + return -EFSCORRUPTED; + } + + err = apfs_node_query(sb, *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 */ + apfs_free_query(sb, *query); + *query = parent; + goto next_node; + } + if (err) + return err; + if (apfs_node_is_leaf((*query)->node)) /* All done */ + return 0; + + err = apfs_child_from_query(*query, &child_id); + if (err) { + apfs_alert(sb, "bad index block: 0x%llx", + (*query)->node->object.block_nr); + return err; + } + + /* + * The omap maps a node id into a block number. The nodes + * of the omap itself do not need this translation. + */ + if ((*query)->flags & APFS_QUERY_OMAP) { + child_blk = child_id; + } else { + /* + * we are always performing lookup from omap root. Might + * need improvement in the future. + */ + err = apfs_omap_lookup_block(sb, sbi->s_omap_root, + child_id, &child_blk); + if (err) + return err; + } + + /* Now go a level deeper and search the child */ + node = apfs_read_node(sb, child_blk); + if (IS_ERR(node)) + return PTR_ERR(node); + + if (node->object.oid != child_id) + apfs_debug(sb, "corrupt b-tree"); + + if ((*query)->flags & APFS_QUERY_MULTIPLE) { + /* + * We are looking for multiple entries, so we must remember + * the parent node and index to continue the search later. + */ + *query = apfs_alloc_query(node, *query); + apfs_node_put(node); + } else { + /* Reuse the same query structure to search the child */ + apfs_node_put((*query)->node); + (*query)->node = node; + (*query)->index = node->records; + (*query)->depth++; + } + goto next_node; +} + +/** + * apfs_omap_read_node - Find and read a node from a b-tree + * @id: id for the seeked node + * + * Returns NULL is case of failure, otherwise a pointer to the resulting + * apfs_node structure. + */ +struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_node *result; + u64 block; + int err; + + err = apfs_omap_lookup_block(sb, sbi->s_omap_root, id, &block); + if (err) + return ERR_PTR(err); + + result = apfs_read_node(sb, block); + if (IS_ERR(result)) + return result; + + if (result->object.oid != id) + apfs_debug(sb, "corrupt b-tree"); + + return result; +} diff --git a/btree.h b/btree.h new file mode 100644 index 0000000..9e2b50b --- /dev/null +++ b/btree.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/btree.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_BTREE_H +#define _APFS_BTREE_H + +#include + +struct super_block; + +/* Flags for the query structure */ +#define APFS_QUERY_TREE_MASK 0007 /* Which b-tree we query */ +#define APFS_QUERY_OMAP 0001 /* This is a b-tree object map query */ +#define APFS_QUERY_CAT 0002 /* This is a catalog tree query */ +#define APFS_QUERY_NEXT 0010 /* Find next of multiple matches */ +#define APFS_QUERY_EXACT 0020 /* Search for an exact match */ +#define APFS_QUERY_DONE 0040 /* The search at this level is over */ +#define APFS_QUERY_ANY_NAME 0100 /* Multiple search for any name */ +#define APFS_QUERY_ANY_NUMBER 0200 /* Multiple search for any number */ +#define APFS_QUERY_MULTIPLE (APFS_QUERY_ANY_NAME | APFS_QUERY_ANY_NUMBER) + +/* + * Structure used to retrieve data from an APFS B-Tree. For now only used + * on the calalog and the object map. + */ +struct apfs_query { + struct apfs_node *node; /* Node being searched */ + struct apfs_key *key; /* What the query is looking for */ + + struct apfs_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 */ +}; + +extern struct apfs_query *apfs_alloc_query(struct apfs_node *node, + struct apfs_query *parent); +extern void apfs_free_query(struct super_block *sb, struct apfs_query *query); +extern int apfs_btree_query(struct super_block *sb, struct apfs_query **query); +extern struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id); +extern int apfs_omap_lookup_block(struct super_block *sb, + struct apfs_node *tbl, u64 id, u64 *block); + +#endif /* _APFS_BTREE_H */ diff --git a/dir.c b/dir.c new file mode 100644 index 0000000..631a7fc --- /dev/null +++ b/dir.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/dir.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include "apfs.h" +#include "btree.h" +#include "dir.h" +#include "key.h" +#include "message.h" +#include "node.h" +#include "super.h" + +/** + * apfs_drec_from_query - Read the directory record found by a successful query + * @query: the query that found the record + * @drec: Return parameter. The directory record found. + * + * Reads the directory record into @drec and performs some basic sanity checks + * as a protection against crafted filesystems. Returns 0 on success or + * -EFSCORRUPTED otherwise. + * + * The caller must not free @query while @drec is in use, because @drec->name + * points to data on disk. + */ +int apfs_drec_from_query(struct apfs_query *query, struct apfs_drec *drec) +{ + char *raw = query->node->object.bh->b_data; + struct apfs_drec_hashed_key *de_key; + struct apfs_drec_val *de; + int namelen = query->key_len - sizeof(*de_key); + + if (namelen < 1) + return -EFSCORRUPTED; + if (query->len < sizeof(*de)) + return -EFSCORRUPTED; + + de = (struct apfs_drec_val *)(raw + query->off); + de_key = (struct apfs_drec_hashed_key *)(raw + query->key_off); + + if (namelen != (le32_to_cpu(de_key->name_len_and_hash) & + APFS_DREC_LEN_MASK)) + return -EFSCORRUPTED; + + /* Filename must be NULL-terminated */ + if (de_key->name[namelen - 1] != 0) + return -EFSCORRUPTED; + + drec->name = de_key->name; + drec->name_len = namelen - 1; /* Don't count the NULL termination */ + drec->ino = le64_to_cpu(de->file_id); + + drec->type = le16_to_cpu(de->flags) & APFS_DREC_TYPE_MASK; + if (drec->type != DT_FIFO && drec->type & 1) /* Invalid file type */ + drec->type = DT_UNKNOWN; + return 0; +} + +/** + * apfs_inode_by_name - Find the cnid for a given filename + * @dir: parent directory + * @child: filename + * @ino: on return, the inode number found + * + * Returns 0 and the inode number (which is the cnid of the file + * record); otherwise, return the appropriate error code. + */ +int apfs_inode_by_name(struct inode *dir, const struct qstr *child, u64 *ino) +{ + struct super_block *sb = dir->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_key key; + struct apfs_query *query; + struct apfs_drec drec; + u64 cnid = dir->i_ino; + int err; + + apfs_init_drec_hashed_key(sb, cnid, child->name, &key); + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + query->key = &key; + + /* + * Distinct filenames in the same directory may (rarely) share the same + * hash. The query code cannot handle that because their order in the + * b-tree would depend on their unnormalized original names. Just get + * all the candidates and check them one by one. + */ + query->flags |= APFS_QUERY_CAT | APFS_QUERY_ANY_NAME | APFS_QUERY_EXACT; + do { + err = apfs_btree_query(sb, &query); + if (err) + goto out; + err = apfs_drec_from_query(query, &drec); + if (err) + goto out; + } while (unlikely(apfs_filename_cmp(sb, child->name, drec.name))); + + *ino = drec.ino; +out: + apfs_free_query(sb, query); + return err; +} + +static int apfs_readdir(struct file *file, struct dir_context *ctx) +{ + struct inode *inode = file_inode(file); + struct super_block *sb = inode->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_key key; + struct apfs_query *query; + u64 cnid = inode->i_ino; + loff_t pos; + int err = 0; + + if (ctx->pos == 0) { + if (!dir_emit_dot(file, ctx)) + return 0; + ctx->pos++; + } + if (ctx->pos == 1) { + if (!dir_emit_dotdot(file, ctx)) + return 0; + ctx->pos++; + } + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + + /* We want all the children for the cnid, regardless of the name */ + apfs_init_drec_hashed_key(sb, cnid, NULL /* name */, &key); + query->key = &key; + query->flags = APFS_QUERY_CAT | APFS_QUERY_MULTIPLE | APFS_QUERY_EXACT; + + pos = ctx->pos - 2; + while (1) { + struct apfs_drec drec; + /* + * We query for the matching records, one by one. After we + * pass ctx->pos we begin to emit them. + * + * TODO: Faster approach for large directories? + */ + + err = apfs_btree_query(sb, &query); + if (err == -ENODATA) { /* Got all the records */ + err = 0; + break; + } + if (err) + break; + + err = apfs_drec_from_query(query, &drec); + if (err) { + apfs_alert(sb, "bad dentry record in directory 0x%llx", + cnid); + break; + } + + err = 0; + if (pos <= 0) { + if (!dir_emit(ctx, drec.name, drec.name_len, + drec.ino, drec.type)) + break; + ctx->pos++; + } + pos--; + } + + if (pos < 0) + ctx->pos -= pos; + apfs_free_query(sb, query); + return err; +} + +const struct file_operations apfs_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate_shared = apfs_readdir, +}; diff --git a/dir.h b/dir.h new file mode 100644 index 0000000..ee979f7 --- /dev/null +++ b/dir.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/dir.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_DIR_H +#define _APFS_DIR_H + +#include + +struct inode; +struct qstr; +struct apfs_query; + +/* + * 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; + +/* + * Directory entry record in memory + */ +struct apfs_drec { + u8 *name; + u64 ino; + int name_len; + unsigned int type; +}; + +extern int apfs_drec_from_query(struct apfs_query *query, + struct apfs_drec *drec); +extern int apfs_inode_by_name(struct inode *dir, const struct qstr *child, + u64 *ino); + +extern const struct file_operations apfs_dir_operations; + +#endif /* _APFS_DIR_H */ diff --git a/extents.c b/extents.c new file mode 100644 index 0000000..4698c2a --- /dev/null +++ b/extents.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/extents.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include "apfs.h" +#include "btree.h" +#include "extents.h" +#include "inode.h" +#include "key.h" +#include "message.h" +#include "node.h" +#include "super.h" + +/** + * apfs_extent_from_query - Read the extent found by a successful query + * @query: the query that found the record + * @extent: Return parameter. The extent found. + * + * Reads the extent record into @extent and performs some basic sanity checks + * as a protection against crafted filesystems. Returns 0 on success or + * -EFSCORRUPTED otherwise. + */ +int apfs_extent_from_query(struct apfs_query *query, + struct apfs_file_extent *extent) +{ + struct super_block *sb = query->node->object.sb; + struct apfs_file_extent_val *ext; + struct apfs_file_extent_key *ext_key; + char *raw = query->node->object.bh->b_data; + u64 ext_len; + + if (query->len != sizeof(*ext) || query->key_len != sizeof(*ext_key)) + return -EFSCORRUPTED; + + ext = (struct apfs_file_extent_val *)(raw + query->off); + ext_key = (struct apfs_file_extent_key *)(raw + query->key_off); + ext_len = le64_to_cpu(ext->len_and_flags) & APFS_FILE_EXTENT_LEN_MASK; + + /* Extent length must be a multiple of the block size */ + if (ext_len & (sb->s_blocksize - 1)) + return -EFSCORRUPTED; + + extent->logical_addr = le64_to_cpu(ext_key->logical_addr); + extent->phys_block_num = le64_to_cpu(ext->phys_block_num); + extent->len = ext_len; + return 0; +} + +/** + * apfs_extent_read - Read the extent record that covers a block + * @inode: inode that owns the record + * @iblock: logical number of the wanted block + * @extent: Return parameter. The extent found. + * + * Finds and caches the extent record. On success, returns a pointer to the + * cache record; on failure, returns an error code. + */ +static int apfs_extent_read(struct inode *inode, sector_t iblock, + struct apfs_file_extent *extent) +{ + struct super_block *sb = inode->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_inode_info *ai = APFS_I(inode); + struct apfs_key key; + struct apfs_query *query; + struct apfs_file_extent *cache = &ai->i_cached_extent; + u64 iaddr = iblock << inode->i_blkbits; + int ret = 0; + + spin_lock(&ai->i_extent_lock); + if (iaddr >= cache->logical_addr && + iaddr < cache->logical_addr + cache->len) { + *extent = *cache; + spin_unlock(&ai->i_extent_lock); + return 0; + } + spin_unlock(&ai->i_extent_lock); + + /* We will search for the extent that covers iblock */ + apfs_init_file_extent_key(ai->i_extent_id, iaddr, &key); + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + query->key = &key; + query->flags = APFS_QUERY_CAT; + + ret = apfs_btree_query(sb, &query); + if (ret) + goto done; + + ret = apfs_extent_from_query(query, extent); + if (ret) { + apfs_alert(sb, "bad extent record for inode 0x%llx", + (unsigned long long) inode->i_ino); + goto done; + } + + spin_lock(&ai->i_extent_lock); + *cache = *extent; + spin_unlock(&ai->i_extent_lock); + +done: + apfs_free_query(sb, query); + return ret; +} + +int apfs_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + struct apfs_file_extent ext; + u64 blk_off, bno, map_len; + int ret; + + ret = apfs_extent_read(inode, iblock, &ext); + if (ret) + return ret; + + /* Find the block offset of iblock within the extent */ + blk_off = iblock - (ext.logical_addr >> inode->i_blkbits); + + /* Make sure we don't read past the extent boundaries */ + map_len = ext.len - (blk_off << inode->i_blkbits); + if (bh_result->b_size > map_len) + bh_result->b_size = map_len; + + /* + * Save the requested mapping length as map_bh() replaces it with + * the filesystem block size + */ + map_len = bh_result->b_size; + /* Extents representing holes have block number 0 */ + if (ext.phys_block_num != 0) { + /* Find the block number of iblock within the disk */ + bno = ext.phys_block_num + blk_off; + map_bh(bh_result, sb, bno); + } + + bh_result->b_size = map_len; + return 0; +} diff --git a/extents.h b/extents.h new file mode 100644 index 0000000..cbfa029 --- /dev/null +++ b/extents.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/extents.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _EXTENTS_H +#define _EXTENTS_H + +#include + +struct inode; +struct buffer_head; +struct apfs_query; + +/* 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; + +/* + * Extent record data in memory + */ +struct apfs_file_extent { + u64 logical_addr; + u64 phys_block_num; + u64 len; +}; + +extern int apfs_extent_from_query(struct apfs_query *query, + struct apfs_file_extent *extent); +extern int apfs_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create); + +#endif /* _EXTENTS_H */ diff --git a/file.c b/file.c new file mode 100644 index 0000000..c00aafb --- /dev/null +++ b/file.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/file.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include "apfs.h" +#include "inode.h" +#include "xattr.h" + +const struct file_operations apfs_file_operations = { + .llseek = generic_file_llseek, + .read_iter = generic_file_read_iter, + .mmap = generic_file_readonly_mmap, + .open = generic_file_open, +}; + +const struct inode_operations apfs_file_inode_operations = { + .getattr = apfs_getattr, + .listxattr = apfs_listxattr, +}; diff --git a/inode.c b/inode.c new file mode 100644 index 0000000..e17fa6e --- /dev/null +++ b/inode.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/inode.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include +#include +#include "apfs.h" +#include "btree.h" +#include "dir.h" +#include "extents.h" +#include "inode.h" +#include "key.h" +#include "message.h" +#include "node.h" +#include "super.h" +#include "xattr.h" + +static int apfs_readpage(struct file *file, struct page *page) +{ + return mpage_readpage(page, apfs_get_block); +} + +static int apfs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned int nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, apfs_get_block); +} + +static sector_t apfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping, block, apfs_get_block); +} + +static const struct address_space_operations apfs_aops = { + .readpage = apfs_readpage, + .readpages = apfs_readpages, + .bmap = apfs_bmap, +}; + +/** + * apfs_inode_from_query - Read the inode found by a successful query + * @query: the query that found the record + * @inode: vfs inode to be filled with the read data + * + * Reads the inode record into @inode and performs some basic sanity checks, + * mostly as a protection against crafted filesystems. Returns 0 on success + * or a negative error code otherwise. + */ +static int apfs_inode_from_query(struct apfs_query *query, struct inode *inode) +{ + struct apfs_inode_info *ai = APFS_I(inode); + struct apfs_inode_val *inode_val; + struct apfs_dstream *dstream = NULL; + struct apfs_xf_blob *xblob; + struct apfs_x_field *xfield; + char *raw = query->node->object.bh->b_data; + int rest, i; + u64 secs; + + if (query->len < sizeof(*inode_val)) + return -EFSCORRUPTED; + + inode_val = (struct apfs_inode_val *)(raw + query->off); + + ai->i_extent_id = le64_to_cpu(inode_val->private_id); + inode->i_mode = le16_to_cpu(inode_val->mode); + i_uid_write(inode, (uid_t)le32_to_cpu(inode_val->owner)); + i_gid_write(inode, (gid_t)le32_to_cpu(inode_val->group)); + + if (S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) { + /* + * It seems that hard links are only allowed for regular files, + * and perhaps for symlinks. + * + * Directory inodes don't store their link count, so to provide + * it we would have to actually count the subdirectories. The + * HFS/HFS+ modules just leave it at 1, and so do we, for now. + */ + set_nlink(inode, le32_to_cpu(inode_val->nlink)); + } + + /* APFS stores the time as unsigned nanoseconds since the epoch */ + secs = le64_to_cpu(inode_val->access_time); + inode->i_atime.tv_nsec = do_div(secs, NSEC_PER_SEC); + inode->i_atime.tv_sec = secs; + secs = le64_to_cpu(inode_val->change_time); + inode->i_ctime.tv_nsec = do_div(secs, NSEC_PER_SEC); + inode->i_ctime.tv_sec = secs; + secs = le64_to_cpu(inode_val->mod_time); + inode->i_mtime.tv_nsec = do_div(secs, NSEC_PER_SEC); + inode->i_mtime.tv_sec = secs; + secs = le64_to_cpu(inode_val->create_time); + ai->i_crtime.tv_nsec = do_div(secs, NSEC_PER_SEC); + ai->i_crtime.tv_sec = secs; + + xblob = (struct apfs_xf_blob *) inode_val->xfields; + xfield = (struct apfs_x_field *) xblob->xf_data; + rest = query->len - (sizeof(*inode_val) + sizeof(*xblob)); + rest -= le16_to_cpu(xblob->xf_num_exts) * sizeof(xfield[0]); + if (rest < 0) + return -EFSCORRUPTED; + for (i = 0; i < le16_to_cpu(xblob->xf_num_exts); ++i) { + int attrlen; + + /* Attribute length is padded to a multiple of 8 */ + attrlen = round_up(le16_to_cpu(xfield[i].x_size), 8); + if (attrlen > rest) + break; + if (xfield[i].x_type == APFS_INO_EXT_TYPE_DSTREAM) { + /* The only optional attr we care about, for now */ + dstream = (struct apfs_dstream *) + ((char *)inode_val + query->len - rest); + break; + } + rest -= attrlen; + } + + if (dstream) { + inode->i_size = le64_to_cpu(dstream->size); + inode->i_blocks = le64_to_cpu(dstream->alloced_size) >> 9; + } else { + /* + * This inode is "empty", but it may actually hold compressed + * data in the named attribute com.apple.decmpfs, and sometimes + * in com.apple.ResourceFork + */ + inode->i_size = inode->i_blocks = 0; + } + + return 0; +} + +/** + * apfs_inode_lookup - Lookup an inode record in the b-tree and read its data + * @inode: vfs inode to lookup and fill + * + * Queries the b-tree for the @inode->i_ino inode record and reads its data to + * @inode. Returns 0 on success or a negative error code otherwise. + */ +static int apfs_inode_lookup(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_key key; + struct apfs_query *query; + u64 cnid = inode->i_ino; + int ret; + + apfs_init_inode_key(cnid, &key); + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + query->key = &key; + query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT; + + ret = apfs_btree_query(sb, &query); + if (ret) + goto done; + + ret = apfs_inode_from_query(query, inode); + if (ret) + apfs_alert(sb, "bad inode record for inode 0x%llx", cnid); + +done: + apfs_free_query(sb, query); + return ret; +} + +#if BITS_PER_LONG == 64 +#define apfs_iget_locked iget_locked +#else /* 64-bit inode numbers may not fit in the vfs inode */ + +/** + * apfs_test_inode - Check if the inode matches a 64-bit inode number + * @inode: inode to test + * @cnid: pointer to the inode number + */ +static int apfs_test_inode(struct inode *inode, void *cnid) +{ + struct apfs_inode_info *ai = APFS_I(inode); + u64 *ino = cnid; + + return ai->i_ino == *ino; +} + +/** + * apfs_set_inode - Set a 64-bit inode number on the given inode + * @inode: inode to set + * @cnid: pointer to the inode number + */ +static int apfs_set_inode(struct inode *inode, void *cnid) +{ + struct apfs_inode_info *ai = APFS_I(inode); + u64 *ino = cnid; + + ai->i_ino = *ino; + inode->i_ino = *ino; /* Just discard the higher bits here... */ + return 0; +} + +/** + * apfs_iget_locked - Wrapper for iget5_locked() + * @sb: filesystem superblock + * @cnid: 64-bit inode number + * + * Works the same as iget_locked(), but supports 64-bit inode numbers. + */ +static struct inode *apfs_iget_locked(struct super_block *sb, u64 cnid) +{ + return iget5_locked(sb, cnid, apfs_test_inode, apfs_set_inode, &cnid); +} + +#endif /* BITS_PER_LONG == 64 */ + +/** + * apfs_iget - Populate inode structures with metadata from disk + * @sb: filesystem superblock + * @cnid: inode number + * + * Populates the vfs inode and the corresponding apfs_inode_info structure. + * Returns a pointer to the vfs inode in case of success, or an appropriate + * error pointer otherwise. + */ +struct inode *apfs_iget(struct super_block *sb, u64 cnid) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct inode *inode; + int err; + + inode = apfs_iget_locked(sb, cnid); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + err = apfs_inode_lookup(inode); + if (err) { + iget_failed(inode); + return ERR_PTR(err); + } + + /* Allow the user to override the ownership */ + if (sbi->s_flags & APFS_UID_OVERRIDE) + inode->i_uid = sbi->s_uid; + if (sbi->s_flags & APFS_GID_OVERRIDE) + inode->i_gid = sbi->s_gid; + + /* A lot of operations still missing, of course */ + if (S_ISREG(inode->i_mode)) { + inode->i_op = &apfs_file_inode_operations; + inode->i_fop = &apfs_file_operations; + inode->i_mapping->a_ops = &apfs_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &apfs_dir_inode_operations; + inode->i_fop = &apfs_dir_operations; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &apfs_symlink_inode_operations; + } else { + inode->i_op = &apfs_special_inode_operations; + } + + /* Inode flags are not important for now, leave them at 0 */ + unlock_new_inode(inode); + return inode; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* No statx yet... */ + +int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct inode *inode = d_inode(dentry); + + generic_fillattr(inode, stat); +#if BITS_PER_LONG == 32 + stat->ino = ai->i_ino; +#endif /* BITS_PER_LONG == 32 */ + return 0; +} + +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */ + +int apfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) +{ + struct inode *inode = d_inode(path->dentry); + struct apfs_inode_info *ai = APFS_I(inode); + + stat->result_mask |= STATX_BTIME; + stat->btime = ai->i_crtime; + + if (apfs_xattr_get(inode, APFS_XATTR_NAME_COMPRESSED, NULL, 0) >= 0) + stat->attributes |= STATX_ATTR_COMPRESSED; + + stat->attributes_mask |= STATX_ATTR_COMPRESSED; + + generic_fillattr(inode, stat); +#if BITS_PER_LONG == 32 + stat->ino = ai->i_ino; +#endif /* BITS_PER_LONG == 32 */ + return 0; +} + +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */ diff --git a/inode.h b/inode.h new file mode 100644 index 0000000..10ee369 --- /dev/null +++ b/inode.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/inode.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_INODE_H +#define _APFS_INODE_H + +#include +#include +#include +#include "extents.h" + +/* 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 + +/* + * 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 */ +#define APFS_DREC_EXT_TYPE_SIBLING_ID 1 + +#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 + +/* + * Structure used to store the number and size of extended fields of an inode + */ +struct apfs_xf_blob { + __le16 xf_num_exts; + __le16 xf_used_data; + u8 xf_data[]; +} __packed; + +/* + * Structure used to store an inode's extended field + */ +struct apfs_x_field { + u8 x_type; + u8 x_flags; + __le16 x_size; +} __packed; + +/* + * Structure of a data stream record + */ +struct apfs_dstream_id_val { + __le32 refcnt; +} __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; + +/* + * APFS inode data in memory + */ +struct apfs_inode_info { + u64 i_extent_id; /* ID of the extent records */ + struct apfs_file_extent i_cached_extent; /* Latest extent record */ + spinlock_t i_extent_lock; /* Protects i_cached_extent */ + struct timespec64 i_crtime; /* Time of creation */ + +#if BITS_PER_LONG == 32 + /* This is the actual inode number; vfs_inode.i_ino could overflow */ + u64 i_ino; +#endif + struct inode vfs_inode; +}; + +static inline struct apfs_inode_info *APFS_I(struct inode *inode) +{ + return container_of(inode, struct apfs_inode_info, vfs_inode); +} + +extern struct inode *apfs_iget(struct super_block *sb, u64 cnid); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) /* No statx yet... */ +extern int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); +#else +extern int apfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags); +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) */ + +#endif /* _APFS_INODE_H */ diff --git a/key.c b/key.c new file mode 100644 index 0000000..17a821a --- /dev/null +++ b/key.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/key.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include "apfs.h" +#include "key.h" +#include "super.h" +#include "unicode.h" + +/** + * apfs_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 apfs_cat_type(struct apfs_key_header *key) +{ + return (le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_TYPE_MASK) + >> APFS_OBJ_TYPE_SHIFT; +} + +/** + * apfs_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 apfs_cat_cnid(struct apfs_key_header *key) +{ + return le64_to_cpu(key->obj_id_and_type) & APFS_OBJ_ID_MASK; +} + +/** + * apfs_filename_cmp - Normalize and compare two APFS filenames + * @sb: filesystem superblock + * @name1, @name2: names to compare + * + * returns 0 if @name1 and @name2 are equal + * < 0 if @name1 comes before @name2 + * > 0 if @name1 comes after @name2 + */ +int apfs_filename_cmp(struct super_block *sb, + const char *name1, const char *name2) +{ + struct apfs_unicursor cursor1, cursor2; + bool case_fold = apfs_is_case_insensitive(sb); + + apfs_init_unicursor(&cursor1, name1); + apfs_init_unicursor(&cursor2, name2); + + while (1) { + unicode_t uni1, uni2; + + uni1 = apfs_normalize_next(&cursor1, case_fold); + uni2 = apfs_normalize_next(&cursor2, case_fold); + + if (uni1 != uni2) + return uni1 < uni2 ? -1 : 1; + if (!uni1) + return 0; + } +} + +/** + * apfs_keycmp - Compare two keys + * @sb: filesystem superblock + * @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 apfs_keycmp(struct super_block *sb, + struct apfs_key *k1, struct apfs_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) + return 0; + + /* Normalization seems to be ignored here, even for directory records */ + return strcmp(k1->name, k2->name); +} + +/** + * apfs_read_cat_key - Parse an on-disk catalog key + * @raw: pointer to the raw key + * @size: size of the raw key + * @key: apfs_key structure to store the result + * + * Returns 0 on success, or a negative error code otherwise. + */ +int apfs_read_cat_key(void *raw, int size, struct apfs_key *key) +{ + if (size < sizeof(struct apfs_key_header)) + return -EFSCORRUPTED; + key->id = apfs_cat_cnid((struct apfs_key_header *)raw); + key->type = apfs_cat_type((struct apfs_key_header *)raw); + + switch (key->type) { + case APFS_TYPE_DIR_REC: + if (size < sizeof(struct apfs_drec_hashed_key) + 1 || + *((char *)raw + size - 1) != 0) { + /* Filename must have NULL-termination */ + return -EFSCORRUPTED; + } + + /* Name length is not used in key comparisons, only the hash */ + key->number = le32_to_cpu( + ((struct apfs_drec_hashed_key *)raw)->name_len_and_hash) & + APFS_DREC_HASH_MASK; + + key->name = ((struct apfs_drec_hashed_key *)raw)->name; + break; + case APFS_TYPE_XATTR: + if (size < sizeof(struct apfs_xattr_key) + 1 || + *((char *)raw + size - 1) != 0) { + /* xattr name must have NULL-termination */ + return -EFSCORRUPTED; + } + key->number = 0; + key->name = ((struct apfs_xattr_key *)raw)->name; + break; + case APFS_TYPE_FILE_EXTENT: + if (size != sizeof(struct apfs_file_extent_key)) + return -EFSCORRUPTED; + key->number = le64_to_cpu( + ((struct apfs_file_extent_key *)raw)->logical_addr); + key->name = NULL; + break; + default: + key->number = 0; + key->name = NULL; + break; + } + + return 0; +} + +/** + * apfs_read_omap_key - Parse an on-disk object map key + * @raw: pointer to the raw key + * @size: size of the raw key + * @key: apfs_key structure to store the result + * + * Returns 0 on success, or a negative error code otherwise. + */ +int apfs_read_omap_key(void *raw, int size, struct apfs_key *key) +{ + if (size < sizeof(struct apfs_omap_key)) + return -EFSCORRUPTED; + + key->id = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_oid); + key->type = 0; + key->number = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_xid); + key->name = NULL; + + return 0; +} + +/** + * apfs_init_drec_hashed_key - Initialize an in-memory key for a dentry query + * @sb: filesystem superblock + * @ino: inode number of the parent directory + * @name: filename (NULL for a multiple query) + * @key: apfs_key structure to initialize + */ +void apfs_init_drec_hashed_key(struct super_block *sb, u64 ino, + const char *name, struct apfs_key *key) +{ + struct apfs_unicursor cursor; + bool case_fold = apfs_is_case_insensitive(sb); + u32 hash = 0xFFFFFFFF; + + key->id = ino; + key->type = APFS_TYPE_DIR_REC; + + /* To respect normalization, queries can only consider the hash */ + key->name = NULL; + + if (!name) { + key->number = 0; + return; + } + + apfs_init_unicursor(&cursor, name); + + while (1) { + unicode_t utf32; + + utf32 = apfs_normalize_next(&cursor, case_fold); + if (!utf32) + break; + + hash = crc32c(hash, &utf32, sizeof(utf32)); + } + + /* The filename length doesn't matter, so it's left as zero */ + key->number = hash << APFS_DREC_HASH_SHIFT; +} diff --git a/key.h b/key.h new file mode 100644 index 0000000..4eb1756 --- /dev/null +++ b/key.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/key.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_KEY_H +#define _APFS_KEY_H + +#include + +struct super_block; + +/* + * Structure of a key in an object map B-tree + */ +struct apfs_omap_key { + __le64 ok_oid; + __le64 ok_xid; +} __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 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, 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; + +/* + * In-memory representation of a key, as relevant for a b-tree query. + */ +struct apfs_key { + u64 id; + u64 number; /* Extent offset, name hash or transaction id */ + const char *name; /* On-disk name string */ + u8 type; /* Record type (0 for the omap) */ +}; + +/** + * apfs_init_omap_key - Initialize an in-memory key for an omap query + * @oid: object id + * @xid: latest transaction id + * @key: apfs_key structure to initialize + */ +static inline void apfs_init_omap_key(u64 oid, u64 xid, struct apfs_key *key) +{ + key->id = oid; + key->type = 0; + key->number = xid; + key->name = NULL; +} + +/** + * apfs_init_inode_key - Initialize an in-memory key for an inode query + * @ino: inode number + * @key: apfs_key structure to initialize + */ +static inline void apfs_init_inode_key(u64 ino, struct apfs_key *key) +{ + key->id = ino; + key->type = APFS_TYPE_INODE; + key->number = 0; + key->name = NULL; +} + +/** + * apfs_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: apfs_key structure to initialize + */ +static inline void apfs_init_file_extent_key(u64 id, u64 offset, + struct apfs_key *key) +{ + key->id = id; + key->type = APFS_TYPE_FILE_EXTENT; + key->number = offset; + key->name = NULL; +} + +extern void apfs_init_drec_hashed_key(struct super_block *sb, u64 ino, + const char *name, struct apfs_key *key); + +/** + * apfs_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: apfs_key structure to initialize + */ +static inline void apfs_init_xattr_key(u64 ino, const char *name, + struct apfs_key *key) +{ + key->id = ino; + key->type = APFS_TYPE_XATTR; + key->number = 0; + key->name = name; +} + +extern int apfs_filename_cmp(struct super_block *sb, + const char *name1, const char *name2); +extern int apfs_keycmp(struct super_block *sb, + struct apfs_key *k1, struct apfs_key *k2); +extern int apfs_read_cat_key(void *raw, int size, struct apfs_key *key); +extern int apfs_read_omap_key(void *raw, int size, struct apfs_key *key); + +#endif /* _APFS_KEY_H */ diff --git a/message.c b/message.c new file mode 100644 index 0000000..d2a08c3 --- /dev/null +++ b/message.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/message.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include "message.h" + +void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + printk("%sAPFS (%s): %pV\n", prefix, sb->s_id, &vaf); + + va_end(args); +} diff --git a/message.h b/message.h new file mode 100644 index 0000000..6c5a778 --- /dev/null +++ b/message.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/message.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_MESSAGE_H +#define _APFS_MESSAGE_H + +struct super_block; + +extern __printf(3, 4) +void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...); + +#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, fmt, ##__VA_ARGS__) +#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, fmt, ##__VA_ARGS__) +#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, fmt, ##__VA_ARGS__) +#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__) +#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__) +#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, fmt, ##__VA_ARGS__) +#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__) + +#ifdef CONFIG_APFS_DEBUG +#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, fmt, ##__VA_ARGS__) +#else +#define apfs_debug(sb, fmt, ...) no_printk(fmt, ##__VA_ARGS__) +#endif + +#endif /* _APFS_MESSAGE_H */ diff --git a/namei.c b/namei.c new file mode 100644 index 0000000..11a00f2 --- /dev/null +++ b/namei.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/namei.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include "apfs.h" +#include "dir.h" +#include "inode.h" +#include "key.h" +#include "super.h" +#include "unicode.h" +#include "xattr.h" + +static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct inode *inode = NULL; + u64 ino = 0; + int err; + + if (dentry->d_name.len > APFS_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + err = apfs_inode_by_name(dir, &dentry->d_name, &ino); + if (err && err != -ENODATA) + return ERR_PTR(err); + + if (!err) { + inode = apfs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + } + + return d_splice_alias(inode, dentry); +} + +const struct inode_operations apfs_dir_inode_operations = { + .lookup = apfs_lookup, + .getattr = apfs_getattr, + .listxattr = apfs_listxattr, +}; + +const struct inode_operations apfs_special_inode_operations = { + .getattr = apfs_getattr, + .listxattr = apfs_listxattr, +}; + +static int apfs_dentry_hash(const struct dentry *dir, struct qstr *child) +{ + struct apfs_unicursor cursor; + unsigned long hash; + bool case_fold = apfs_is_case_insensitive(dir->d_sb); + + apfs_init_unicursor(&cursor, child->name); + hash = init_name_hash(dir); + + while (1) { + int i; + unicode_t utf32; + + utf32 = apfs_normalize_next(&cursor, case_fold); + if (!utf32) + break; + + /* Hash the unicode character one byte at a time */ + for (i = 0; i < 4; ++i) { + hash = partial_name_hash((u8)utf32, hash); + utf32 = utf32 >> 8; + } + } + child->hash = end_name_hash(hash); + + /* TODO: return error instead of truncating invalid UTF-8? */ + return 0; +} + +static int apfs_dentry_compare(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + return apfs_filename_cmp(dentry->d_sb, name->name, str); +} + +const struct dentry_operations apfs_dentry_operations = { + .d_hash = apfs_dentry_hash, + .d_compare = apfs_dentry_compare, +}; diff --git a/node.c b/node.c new file mode 100644 index 0000000..bcdbf05 --- /dev/null +++ b/node.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/node.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include "apfs.h" +#include "btree.h" +#include "key.h" +#include "message.h" +#include "node.h" +#include "object.h" +#include "super.h" + +/** + * apfs_node_is_valid - Check basic sanity of the node index + * @sb: filesystem superblock + * @node: node to check + * + * Verifies that the node index fits in a single block, and that the number + * of records fits in the index. Without this check a crafted filesystem could + * pretend to have too many records, and calls to apfs_node_locate_key() and + * apfs_node_locate_data() would read beyond the limits of the node. + */ +static bool apfs_node_is_valid(struct super_block *sb, + struct apfs_node *node) +{ + int records = node->records; + int index_size = node->key - sizeof(struct apfs_btree_node_phys); + int entry_size; + + if (!records) /* Empty nodes could keep a multiple query spinning */ + return false; + + if (node->key > sb->s_blocksize) + return false; + + entry_size = (apfs_node_has_fixed_kv_size(node)) ? + sizeof(struct apfs_kvoff) : sizeof(struct apfs_kvloc); + + return records * entry_size <= index_size; +} + +static void apfs_node_release(struct kref *kref) +{ + struct apfs_node *node = + container_of(kref, struct apfs_node, refcount); + + brelse(node->object.bh); + kfree(node); +} + +void apfs_node_get(struct apfs_node *node) +{ + kref_get(&node->refcount); +} + +void apfs_node_put(struct apfs_node *node) +{ + kref_put(&node->refcount, apfs_node_release); +} + +/** + * apfs_read_node - Read a node header from disk + * @sb: filesystem superblock + * @block: number of the block where the node is stored + * + * Returns ERR_PTR in case of failure, otherwise return a pointer to the + * resulting apfs_node structure with the initial reference taken. + * + * For now we assume the node has not been read before. + */ +struct apfs_node *apfs_read_node(struct super_block *sb, u64 block) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct buffer_head *bh; + struct apfs_btree_node_phys *raw; + struct apfs_node *node; + + bh = sb_bread(sb, block); + if (!bh) { + apfs_err(sb, "unable to read node"); + return ERR_PTR(-EINVAL); + } + raw = (struct apfs_btree_node_phys *) bh->b_data; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + brelse(bh); + return ERR_PTR(-ENOMEM); + } + + node->flags = le16_to_cpu(raw->btn_flags); + node->records = le32_to_cpu(raw->btn_nkeys); + node->key = sizeof(*raw) + le16_to_cpu(raw->btn_table_space.off) + + 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); + + node->object.sb = sb; + node->object.block_nr = block; + node->object.oid = le64_to_cpu(raw->btn_o.o_oid); + node->object.bh = bh; + + kref_init(&node->refcount); + + if (sbi->s_flags & APFS_CHECK_NODES && + !apfs_obj_verify_csum(sb, &raw->btn_o)) { + apfs_alert(sb, "bad checksum for node in block 0x%llx", block); + apfs_node_put(node); + return ERR_PTR(-EFSBADCRC); + } + if (!apfs_node_is_valid(sb, node)) { + apfs_alert(sb, "bad node in block 0x%llx", block); + apfs_node_put(node); + return ERR_PTR(-EFSCORRUPTED); + } + + return node; +} + +/** + * apfs_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, or 0 in case of failure. 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. + */ +static int apfs_node_locate_key(struct apfs_node *node, int index, int *off) +{ + struct super_block *sb = node->object.sb; + struct apfs_btree_node_phys *raw; + int len; + + if (index >= node->records) + return 0; + + raw = (struct apfs_btree_node_phys *)node->object.bh->b_data; + if (apfs_node_has_fixed_kv_size(node)) { + struct apfs_kvoff *entry; + + entry = (struct apfs_kvoff *)raw->btn_data + index; + len = 16; + /* Translate offset in key area to offset in block */ + *off = node->key + 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); + /* Translate offset in key area to offset in block */ + *off = node->key + le16_to_cpu(entry->k.off); + } + + if (*off + len > sb->s_blocksize) { + /* Avoid out-of-bounds read if corrupted */ + return 0; + } + return len; +} + +/** + * apfs_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, or 0 in case of failure. 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. + */ +static int apfs_node_locate_data(struct apfs_node *node, int index, int *off) +{ + struct super_block *sb = node->object.sb; + struct apfs_btree_node_phys *raw; + int len; + + if (index >= node->records) + return 0; + + raw = (struct apfs_btree_node_phys *)node->object.bh->b_data; + if (apfs_node_has_fixed_kv_size(node)) { + /* These node types have fixed length keys and data */ + struct apfs_kvoff *entry; + + entry = (struct apfs_kvoff *)raw->btn_data + index; + /* Node type decides length */ + len = apfs_node_is_leaf(node) ? 16 : 8; + /* + * Data offsets are counted backwards from the end of the + * block, or from the beginning of the footer when it exists + */ + if (apfs_node_is_root(node)) /* has footer */ + *off = sb->s_blocksize - sizeof(struct apfs_btree_info) + - le16_to_cpu(entry->v); + else + *off = sb->s_blocksize - 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); + /* + * Data offsets are counted backwards from the end of the + * block, or from the beginning of the footer when it exists + */ + if (apfs_node_is_root(node)) /* has footer */ + *off = sb->s_blocksize - sizeof(struct apfs_btree_info) + - le16_to_cpu(entry->v.off); + else + *off = sb->s_blocksize - le16_to_cpu(entry->v.off); + } + + if (*off < 0 || *off + len > sb->s_blocksize) { + /* Avoid out-of-bounds read if corrupted */ + return 0; + } + return len; +} + +/** + * apfs_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 and performs some basic sanity checks as a + * protection against crafted filesystems. Returns 0 on success or a + * negative error code otherwise. + */ +static int apfs_key_from_query(struct apfs_query *query, struct apfs_key *key) +{ + struct super_block *sb = query->node->object.sb; + char *raw = query->node->object.bh->b_data; + void *raw_key = (void *)(raw + query->key_off); + int err = 0; + + switch (query->flags & APFS_QUERY_TREE_MASK) { + case APFS_QUERY_CAT: + err = apfs_read_cat_key(raw_key, query->key_len, key); + break; + case APFS_QUERY_OMAP: + err = apfs_read_omap_key(raw_key, query->key_len, key); + break; + default: + /* Not implemented yet */ + err = -EINVAL; + break; + } + if (err) { + apfs_alert(sb, "bad node key in block 0x%llx", + query->node->object.block_nr); + } + + /* A multiple query must ignore some of these fields */ + if (query->flags & APFS_QUERY_ANY_NAME) + key->name = NULL; + if (query->flags & APFS_QUERY_ANY_NUMBER) + key->number = 0; + + return err; +} + +/** + * apfs_node_next - Find the next matching record in the current node + * @sb: filesystem superblock + * @query: multiple query in execution + * + * Returns 0 on success, -EAGAIN if the next record is in another node, + * -ENODATA if no more matching records exist, or another negative error + * code in case of failure. + */ +static int apfs_node_next(struct super_block *sb, struct apfs_query *query) +{ + struct apfs_node *node = query->node; + struct apfs_key curr_key; + int cmp, err; + + if (query->flags & APFS_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 = apfs_node_locate_key(node, query->index, + &query->key_off); + err = apfs_key_from_query(query, &curr_key); + if (err) + return err; + + cmp = apfs_keycmp(sb, &curr_key, query->key); + + if (cmp > 0) /* Records are out of order */ + return -EFSCORRUPTED; + + if (cmp != 0 && apfs_node_is_leaf(node) && + query->flags & APFS_QUERY_EXACT) + return -ENODATA; + + query->len = apfs_node_locate_data(node, query->index, &query->off); + if (query->len == 0) + return -EFSCORRUPTED; + + 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 |= APFS_QUERY_DONE; + } + + return 0; +} + +/** + * apfs_node_query - Execute a query on a single node + * @sb: filesystem superblock + * @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 apfs_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 APFS_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, -EFSCORRUPTED + * in case of corruption. + */ +int apfs_node_query(struct super_block *sb, struct apfs_query *query) +{ + struct apfs_node *node = query->node; + int left, right; + int cmp; + int err; + + if (query->flags & APFS_QUERY_NEXT) + return apfs_node_next(sb, query); + + /* Search by bisection */ + cmp = 1; + left = 0; + do { + struct apfs_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 = apfs_node_locate_key(node, query->index, + &query->key_off); + err = apfs_key_from_query(query, &curr_key); + if (err) + return err; + + cmp = apfs_keycmp(sb, &curr_key, query->key); + if (cmp == 0 && !(query->flags & APFS_QUERY_MULTIPLE)) + break; + } while (left != right); + + if (cmp > 0) + return -ENODATA; + + if (cmp != 0 && apfs_node_is_leaf(query->node) && + query->flags & APFS_QUERY_EXACT) + return -ENODATA; + + if (query->flags & APFS_QUERY_MULTIPLE) { + if (cmp != 0) /* Last relevant entry in level */ + query->flags |= APFS_QUERY_DONE; + query->flags |= APFS_QUERY_NEXT; + } + + query->len = apfs_node_locate_data(node, query->index, &query->off); + if (query->len == 0) + return -EFSCORRUPTED; + return 0; +} + +/** + * apfs_bno_from_query - Read the block number found by a successful omap query + * @query: the query that found the record + * @bno: Return parameter. The block number found. + * + * Reads the block number in the omap record into @bno and performs a basic + * sanity check as a protection against crafted filesystems. Returns 0 on + * success or -EFSCORRUPTED otherwise. + */ +int apfs_bno_from_query(struct apfs_query *query, u64 *bno) +{ + struct apfs_omap_val *omap_val; + char *raw = query->node->object.bh->b_data; + + if (query->len != sizeof(*omap_val)) + return -EFSCORRUPTED; + + omap_val = (struct apfs_omap_val *)(raw + query->off); + *bno = le64_to_cpu(omap_val->ov_paddr); + return 0; +} diff --git a/node.h b/node.h new file mode 100644 index 0000000..5759ba6 --- /dev/null +++ b/node.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/node.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_NODE_H +#define _APFS_NODE_H + +#include +#include +#include "object.h" + +struct apfs_query; + +/* + * 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; + +/* + * 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 + +/* 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; + +/* + * 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; + __le32 bt_longest_val; + __le64 bt_key_count; + __le64 bt_node_count; +} __packed; + +/* + * In-memory representation of an APFS node + */ +struct apfs_node { + u16 flags; /* Node flags */ + u32 records; /* Number of records in the node */ + + 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 */ + + struct apfs_object object; /* Object holding the node */ + + struct kref refcount; +}; + +/** + * apfs_node_is_leaf - Check if a b-tree node is a leaf + * @node: the node to check + */ +static inline bool apfs_node_is_leaf(struct apfs_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 apfs_node_is_root(struct apfs_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 apfs_node_has_fixed_kv_size(struct apfs_node *node) +{ + return (node->flags & APFS_BTNODE_FIXED_KV_SIZE) != 0; +} + +extern struct apfs_node *apfs_read_node(struct super_block *sb, u64 block); +extern int apfs_node_query(struct super_block *sb, struct apfs_query *query); +extern int apfs_bno_from_query(struct apfs_query *query, u64 *bno); + +extern void apfs_node_get(struct apfs_node *node); +extern void apfs_node_put(struct apfs_node *node); + +#endif /* _APFS_NODE_H */ diff --git a/object.c b/object.c new file mode 100644 index 0000000..e5eaa24 --- /dev/null +++ b/object.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/object.c + * + * Checksum routines for an APFS object + */ + +#include +#include "object.h" + +/* + * Note that this is not a generic implementation of fletcher64, as it assumes + * a message length that doesn't overflow sum1 and sum2. This constraint is ok + * for apfs, though, since the block size is limited to 2^16. For a more + * generic optimized implementation, see Nakassis (1988). + */ +static u64 apfs_fletcher64(void *addr, size_t 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 - do_div(c1, 0xFFFFFFFF); + c2 = sum1 + c1; + c2 = 0xFFFFFFFF - do_div(c2, 0xFFFFFFFF); + + return (c2 << 32) | c1; +} + +int apfs_obj_verify_csum(struct super_block *sb, struct apfs_obj_phys *obj) +{ + return (le64_to_cpu(obj->o_cksum) == + apfs_fletcher64((char *) obj + APFS_MAX_CKSUM_SIZE, + sb->s_blocksize - APFS_MAX_CKSUM_SIZE)); +} diff --git a/object.h b/object.h new file mode 100644 index 0000000..9bffa3f --- /dev/null +++ b/object.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/object.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_OBJECT_H +#define _APFS_OBJECT_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 + +/* + * 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; + +/* + * In-memory representation of an APFS object + */ +struct apfs_object { + struct super_block *sb; + u64 block_nr; + u64 oid; /* Often the same as the block number */ + + /* + * Buffer head containing the one block of the object. TODO: support + * objects with more than one block. + */ + struct buffer_head *bh; +}; + +#define APFS_MAX_CKSUM_SIZE 8 + +extern int apfs_obj_verify_csum(struct super_block *sb, + struct apfs_obj_phys *obj); + +#endif /* _APFS_OBJECT_H */ diff --git a/super.c b/super.c new file mode 100644 index 0000000..126aa5b --- /dev/null +++ b/super.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/super.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "apfs.h" +#include "btree.h" +#include "inode.h" +#include "message.h" +#include "node.h" +#include "object.h" +#include "super.h" +#include "xattr.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) /* iversion came in 4.16 */ +#include +#endif + +/** + * apfs_read_super_copy - Read the copy of the container superblock in block 0 + * @sb: superblock structure + * + * Returns a pointer to the buffer head, or an error pointer in case of failure. + */ +static struct buffer_head *apfs_read_super_copy(struct super_block *sb) +{ + struct buffer_head *bh; + struct apfs_nx_superblock *msb_raw; + int blocksize; + int err = -EINVAL; + + /* + * For now assume a small blocksize, we only need it so that we can + * read the actual blocksize from disk. + */ + if (!sb_set_blocksize(sb, APFS_NX_DEFAULT_BLOCK_SIZE)) { + apfs_err(sb, "unable to set blocksize"); + return ERR_PTR(err); + } + bh = sb_bread(sb, APFS_NX_BLOCK_NUM); + if (!bh) { + apfs_err(sb, "unable to read superblock"); + return ERR_PTR(err); + } + msb_raw = (struct apfs_nx_superblock *)bh->b_data; + blocksize = le32_to_cpu(msb_raw->nx_block_size); + + if (sb->s_blocksize != blocksize) { + brelse(bh); + + if (!sb_set_blocksize(sb, blocksize)) { + apfs_err(sb, "bad blocksize %d", blocksize); + return ERR_PTR(err); + } + bh = sb_bread(sb, APFS_NX_BLOCK_NUM); + if (!bh) { + apfs_err(sb, "unable to read superblock 2nd time"); + return ERR_PTR(err); + } + msb_raw = (struct apfs_nx_superblock *)bh->b_data; + } + + sb->s_magic = le32_to_cpu(msb_raw->nx_magic); + if (sb->s_magic != APFS_NX_MAGIC) { + apfs_err(sb, "not an apfs filesystem"); + goto fail; + } + if (!apfs_obj_verify_csum(sb, &msb_raw->nx_o)) { + apfs_err(sb, "inconsistent container superblock"); + err = -EFSBADCRC; + goto fail; + } + return bh; + +fail: + brelse(bh); + return ERR_PTR(err); +} + +/** + * apfs_map_main_super - Find the container superblock and map it into memory + * @sb: superblock structure + * + * Returns a negative error code in case of failure. On success, returns 0 + * and sets the s_msb_raw, s_mobject and s_xid fields of APFS_SB(@sb). + */ +static int apfs_map_main_super(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct buffer_head *bh; + struct buffer_head *desc_bh = NULL; + struct apfs_nx_superblock *msb_raw; + u64 xid, bno = APFS_NX_BLOCK_NUM; + u64 desc_base; + u32 desc_blocks; + int err = -EINVAL; + int i; + + /* Read the superblock from the last clean unmount */ + bh = apfs_read_super_copy(sb); + if (IS_ERR(bh)) + return PTR_ERR(bh); + msb_raw = (struct apfs_nx_superblock *)bh->b_data; + + /* We want to mount the latest valid checkpoint among the descriptors */ + desc_base = le64_to_cpu(msb_raw->nx_xp_desc_base); + if (desc_base >> 63 != 0) { + /* The highest bit is set when checkpoints are not contiguous */ + apfs_err(sb, "checkpoint descriptor tree not yet supported"); + goto fail; + } + desc_blocks = le32_to_cpu(msb_raw->nx_xp_desc_blocks); + if (desc_blocks > 10000) { /* Arbitrary loop limit, is it enough? */ + apfs_err(sb, "too many checkpoint descriptors?"); + err = -EFSCORRUPTED; + goto fail; + } + + /* Now we go through the checkpoints one by one */ + xid = le64_to_cpu(msb_raw->nx_o.o_xid); + for (i = 0; i < desc_blocks; ++i) { + struct apfs_nx_superblock *desc_raw; + + brelse(desc_bh); + desc_bh = sb_bread(sb, desc_base + i); + if (!desc_bh) { + apfs_err(sb, "unable to read checkpoint descriptor"); + goto fail; + } + desc_raw = (struct apfs_nx_superblock *)desc_bh->b_data; + + if (le32_to_cpu(desc_raw->nx_magic) != APFS_NX_MAGIC) + continue; /* Not a superblock */ + if (le64_to_cpu(desc_raw->nx_o.o_xid) <= xid) + continue; /* Old */ + if (!apfs_obj_verify_csum(sb, &desc_raw->nx_o)) + continue; /* Corrupted */ + + xid = le64_to_cpu(desc_raw->nx_o.o_xid); + msb_raw = desc_raw; + bno = desc_base + i; + brelse(bh); + bh = desc_bh; + desc_bh = NULL; + } + + sbi->s_xid = xid; + sbi->s_msb_raw = msb_raw; + sbi->s_mobject.sb = sb; + sbi->s_mobject.block_nr = bno; + sbi->s_mobject.oid = le64_to_cpu(msb_raw->nx_o.o_oid); + sbi->s_mobject.bh = bh; + return 0; + +fail: + brelse(bh); + return err; +} + +/** + * apfs_unmap_main_super - Clean up apfs_map_main_super() + * @sb: filesystem superblock + */ +static inline void apfs_unmap_main_super(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + + brelse(sbi->s_mobject.bh); +} + +/** + * apfs_map_volume_super - Find the volume superblock and map it into memory + * @sb: superblock structure + * + * Returns a negative error code in case of failure. On success, returns 0 + * and sets APFS_SB(@sb)->s_vsb_raw and APFS_SB(@sb)->s_vobject. + */ +static int apfs_map_volume_super(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_nx_superblock *msb_raw = sbi->s_msb_raw; + struct apfs_superblock *vsb_raw; + struct apfs_omap_phys *msb_omap_raw; + struct apfs_node *vnode; + struct buffer_head *bh; + u64 vol_id; + u64 msb_omap, vb, vsb; + int err; + + /* Get the id for the requested volume number */ + if (sbi->s_vol_nr >= APFS_NX_MAX_FILE_SYSTEMS) { + apfs_err(sb, "volume number out of range"); + return -EINVAL; + } + vol_id = le64_to_cpu(msb_raw->nx_fs_oid[sbi->s_vol_nr]); + if (vol_id == 0) { + apfs_err(sb, "requested volume does not exist"); + return -EINVAL; + } + + /* Get the container's object map */ + msb_omap = le64_to_cpu(msb_raw->nx_omap_oid); + bh = sb_bread(sb, msb_omap); + if (!bh) { + apfs_err(sb, "unable to read container object map"); + return -EINVAL; + } + msb_omap_raw = (struct apfs_omap_phys *)bh->b_data; + if (!apfs_obj_verify_csum(sb, &msb_omap_raw->om_o)) { + apfs_err(sb, "bad checksum for the container object map"); + err = -EFSBADCRC; + goto fail; + } + + /* Get the Volume Block */ + vb = le64_to_cpu(msb_omap_raw->om_tree_oid); + msb_omap_raw = NULL; + brelse(bh); + + vnode = apfs_read_node(sb, vb); + if (IS_ERR(vnode)) { + apfs_err(sb, "unable to read volume block"); + return PTR_ERR(vnode); + } + + err = apfs_omap_lookup_block(sb, vnode, vol_id, &vsb); + apfs_node_put(vnode); + if (err) { + apfs_err(sb, "volume not found, likely corruption"); + return err; + } + + bh = sb_bread(sb, vsb); + if (!bh) { + apfs_err(sb, "unable to read volume superblock"); + return -EINVAL; + } + + vsb_raw = (struct apfs_superblock *)bh->b_data; + if (le32_to_cpu(vsb_raw->apfs_magic) != APFS_MAGIC) { + apfs_err(sb, "wrong magic in volume superblock"); + err = -EINVAL; + goto fail; + } + if (!apfs_obj_verify_csum(sb, &vsb_raw->apfs_o)) { + apfs_err(sb, "inconsistent volume superblock"); + err = -EFSBADCRC; + goto fail; + } + + sbi->s_vsb_raw = vsb_raw; + sbi->s_vobject.sb = sb; + sbi->s_vobject.block_nr = vsb; + sbi->s_vobject.oid = le64_to_cpu(vsb_raw->apfs_o.o_oid); + sbi->s_vobject.bh = bh; + return 0; + +fail: + brelse(bh); + return err; +} + +/** + * apfs_unmap_volume_super - Clean up apfs_map_volume_super() + * @sb: filesystem superblock + */ +static inline void apfs_unmap_volume_super(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + + brelse(sbi->s_vobject.bh); +} + +/** + * apfs_read_omap - Find and read the omap root node + * @sb: superblock structure + * + * On success, returns 0 and sets APFS_SB(@sb)->s_omap_root; on failure returns + * a negative error code. + */ +static int apfs_read_omap(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_superblock *vsb_raw = sbi->s_vsb_raw; + struct apfs_omap_phys *omap_raw; + struct apfs_node *omap_root; + struct buffer_head *bh; + u64 omap_blk, omap_root_blk; + + /* Get the block holding the volume omap information */ + omap_blk = le64_to_cpu(vsb_raw->apfs_omap_oid); + bh = sb_bread(sb, omap_blk); + if (!bh) { + apfs_err(sb, "unable to read the volume object map"); + return -EINVAL; + } + omap_raw = (struct apfs_omap_phys *)bh->b_data; + if (!apfs_obj_verify_csum(sb, &omap_raw->om_o)) { + apfs_err(sb, "bad checksum for the volume object map"); + brelse(bh); + return -EFSBADCRC; + } + + /* Get the volume's object map */ + omap_root_blk = le64_to_cpu(omap_raw->om_tree_oid); + brelse(bh); + omap_root = apfs_read_node(sb, omap_root_blk); + if (IS_ERR(omap_root)) { + apfs_err(sb, "unable to read the omap root node"); + return PTR_ERR(omap_root); + } + + sbi->s_omap_root = omap_root; + return 0; +} + +/** + * apfs_read_catalog - Find and read the catalog root node + * @sb: superblock structure + * + * On success, returns 0 and sets APFS_SB(@sb)->s_cat_root; on failure returns + * a negative error code. + */ +static int apfs_read_catalog(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_superblock *vsb_raw = sbi->s_vsb_raw; + struct apfs_node *root_node; + u64 root_id; + + root_id = le64_to_cpu(vsb_raw->apfs_root_tree_oid); + root_node = apfs_omap_read_node(sb, root_id); + if (IS_ERR(root_node)) { + apfs_err(sb, "unable to read catalog root node"); + return PTR_ERR(root_node); + } + sbi->s_cat_root = root_node; + return 0; +} + +static void apfs_put_super(struct super_block *sb) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + + apfs_node_put(sbi->s_cat_root); + apfs_node_put(sbi->s_omap_root); + + apfs_unmap_main_super(sb); + apfs_unmap_volume_super(sb); + + sb->s_fs_info = NULL; + kfree(sbi); +} + +static struct kmem_cache *apfs_inode_cachep; + +static struct inode *apfs_alloc_inode(struct super_block *sb) +{ + struct apfs_inode_info *ai; + + ai = kmem_cache_alloc(apfs_inode_cachep, GFP_KERNEL); + if (!ai) + return NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) /* iversion came in 4.16 */ + inode_set_iversion(&ai->vfs_inode, 1); +#else + ai->vfs_inode.i_version = 1; +#endif + return &ai->vfs_inode; +} + +static void apfs_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + + kmem_cache_free(apfs_inode_cachep, APFS_I(inode)); +} + +static void apfs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, apfs_i_callback); +} + +static void init_once(void *p) +{ + struct apfs_inode_info *ai = (struct apfs_inode_info *)p; + + spin_lock_init(&ai->i_extent_lock); + ai->i_cached_extent.len = 0; + inode_init_once(&ai->vfs_inode); +} + +static int __init init_inodecache(void) +{ + apfs_inode_cachep = kmem_cache_create("apfs_inode_cache", + sizeof(struct apfs_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD|SLAB_ACCOUNT), + init_once); + if (apfs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(apfs_inode_cachep); +} + +/** + * apfs_count_used_blocks - Count the blocks in use across all volumes + * @sb: filesystem superblock + * @count: on return it will store the block count + * + * This function probably belongs in a separate file, but for now it is + * only called by statfs. + */ +static int apfs_count_used_blocks(struct super_block *sb, u64 *count) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_nx_superblock *msb_raw = sbi->s_msb_raw; + struct apfs_node *vnode; + struct apfs_omap_phys *msb_omap_raw; + struct buffer_head *bh; + u64 msb_omap, vb; + int i; + int err = 0; + + /* Get the container's object map */ + msb_omap = le64_to_cpu(msb_raw->nx_omap_oid); + bh = sb_bread(sb, msb_omap); + if (!bh) { + apfs_err(sb, "unable to read container object map"); + return -EIO; + } + msb_omap_raw = (struct apfs_omap_phys *)bh->b_data; + + /* Get the Volume Block */ + vb = le64_to_cpu(msb_omap_raw->om_tree_oid); + msb_omap_raw = NULL; + brelse(bh); + bh = NULL; + vnode = apfs_read_node(sb, vb); + if (IS_ERR(vnode)) { + apfs_err(sb, "unable to read volume block"); + return PTR_ERR(vnode); + } + + /* Iterate through the checkpoint superblocks and add the used blocks */ + *count = 0; + for (i = 0; i < APFS_NX_MAX_FILE_SYSTEMS; i++) { + struct apfs_superblock *vsb_raw; + u64 vol_id; + u64 vol_bno; + + vol_id = le64_to_cpu(msb_raw->nx_fs_oid[i]); + if (vol_id == 0) /* All volumes have been checked */ + break; + err = apfs_omap_lookup_block(sb, vnode, vol_id, &vol_bno); + if (err) + break; + + bh = sb_bread(sb, vol_bno); + if (!bh) { + err = -EIO; + apfs_err(sb, "unable to read volume superblock"); + break; + } + vsb_raw = (struct apfs_superblock *)bh->b_data; + *count += le64_to_cpu(vsb_raw->apfs_fs_alloc_count); + brelse(bh); + } + + apfs_node_put(vnode); + return err; +} + +static int apfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_nx_superblock *msb_raw = sbi->s_msb_raw; + struct apfs_superblock *vol = sbi->s_vsb_raw; + u64 fsid, used_blocks = 0; + int err; + + buf->f_type = APFS_SUPER_MAGIC; + /* Nodes are assumed to fit in a page, for now */ + buf->f_bsize = sb->s_blocksize; + + /* Volumes share the whole disk space */ + buf->f_blocks = le64_to_cpu(msb_raw->nx_block_count); + err = apfs_count_used_blocks(sb, &used_blocks); + if (err) + return err; + buf->f_bfree = buf->f_blocks - used_blocks; + buf->f_bavail = buf->f_bfree; /* I don't know any better */ + + /* The file count is only for the mounted volume */ + buf->f_files = le64_to_cpu(vol->apfs_num_files) + + le64_to_cpu(vol->apfs_num_directories) + + le64_to_cpu(vol->apfs_num_symlinks) + + le64_to_cpu(vol->apfs_num_other_fsobjects); + + /* + * buf->f_ffree is left undefined for now. Maybe it should report the + * number of available cnids, like hfsplus attempts to do. + */ + + buf->f_namelen = 255; /* Again, I don't know any better */ + + /* There are no clear rules for the fsid, so we follow ext2 here */ + fsid = le64_to_cpup((void *)vol->apfs_vol_uuid) ^ + le64_to_cpup((void *)vol->apfs_vol_uuid + sizeof(u64)); + buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL; + buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL; + + return 0; +} + +static int apfs_show_options(struct seq_file *seq, struct dentry *root) +{ + struct apfs_sb_info *sbi = APFS_SB(root->d_sb); + + if (sbi->s_vol_nr != 0) + seq_printf(seq, ",vol=%u", sbi->s_vol_nr); + if (sbi->s_flags & APFS_UID_OVERRIDE) + seq_printf(seq, ",uid=%u", from_kuid(&init_user_ns, + sbi->s_uid)); + if (sbi->s_flags & APFS_GID_OVERRIDE) + seq_printf(seq, ",gid=%u", from_kgid(&init_user_ns, + sbi->s_gid)); + if (sbi->s_flags & APFS_CHECK_NODES) + seq_puts(seq, ",cknodes"); + + return 0; +} + +static const struct super_operations apfs_sops = { + .alloc_inode = apfs_alloc_inode, + .destroy_inode = apfs_destroy_inode, + .put_super = apfs_put_super, + .statfs = apfs_statfs, + .show_options = apfs_show_options, +}; + +enum { + Opt_cknodes, Opt_uid, Opt_gid, Opt_vol, Opt_err, +}; + +static const match_table_t tokens = { + {Opt_cknodes, "cknodes"}, + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_vol, "vol=%u"}, + {Opt_err, NULL} +}; + +/* + * Many of the parse_options() functions in other file systems return 0 + * on error. This one returns an error code, and 0 on success. + */ +static int parse_options(struct super_block *sb, char *options) +{ + struct apfs_sb_info *sbi = APFS_SB(sb); + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + int err = 0; + + /* Set default values before parsing */ + sbi->s_vol_nr = 0; + sbi->s_flags = 0; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + + if (!*p) + continue; + token = match_token(p, tokens, args); + switch (token) { + case Opt_cknodes: + /* + * Right now, node checksums are too costly to enable + * by default. TODO: try to improve this. + */ + sbi->s_flags |= APFS_CHECK_NODES; + break; + case Opt_uid: + err = match_int(&args[0], &option); + if (err) + return err; + sbi->s_uid = make_kuid(current_user_ns(), option); + if (!uid_valid(sbi->s_uid)) { + apfs_err(sb, "invalid uid"); + return -EINVAL; + } + sbi->s_flags |= APFS_UID_OVERRIDE; + break; + case Opt_gid: + err = match_int(&args[0], &option); + if (err) + return err; + sbi->s_gid = make_kgid(current_user_ns(), option); + if (!gid_valid(sbi->s_gid)) { + apfs_err(sb, "invalid gid"); + return -EINVAL; + } + sbi->s_flags |= APFS_GID_OVERRIDE; + break; + case Opt_vol: + err = match_int(&args[0], &sbi->s_vol_nr); + if (err) + return err; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static int apfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct apfs_sb_info *sbi; + struct inode *root; + int err; + + apfs_notice(sb, "this module is read-only"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) /* SB_RDONLY came in 4.14 */ + sb->s_flags |= SB_RDONLY; +#else + sb->s_flags |= MS_RDONLY; +#endif + + sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + + err = apfs_map_main_super(sb); + if (err) + goto failed_main_super; + + /* For now we only support blocksize < PAGE_SIZE */ + sbi->s_blocksize = sb->s_blocksize; + sbi->s_blocksize_bits = sb->s_blocksize_bits; + + err = parse_options(sb, data); + if (err) + goto failed_volume_super; + + err = apfs_map_volume_super(sb); + if (err) + goto failed_volume_super; + + /* The omap needs to be set before the call to apfs_read_catalog() */ + err = apfs_read_omap(sb); + if (err) + goto failed_omap; + + err = apfs_read_catalog(sb); + if (err) + goto failed_cat; + + sb->s_op = &apfs_sops; + sb->s_d_op = &apfs_dentry_operations; + sb->s_xattr = apfs_xattr_handlers; + sb->s_maxbytes = MAX_LFS_FILESIZE; + + root = apfs_iget(sb, APFS_ROOT_DIR_INO_NUM); + if (IS_ERR(root)) { + apfs_err(sb, "unable to get root inode"); + err = PTR_ERR(root); + goto failed_mount; + } + sb->s_root = d_make_root(root); + if (!sb->s_root) { + apfs_err(sb, "unable to get root dentry"); + err = -ENOMEM; + goto failed_mount; + } + return 0; + +failed_mount: + apfs_node_put(sbi->s_cat_root); +failed_cat: + apfs_node_put(sbi->s_omap_root); +failed_omap: + apfs_unmap_volume_super(sb); +failed_volume_super: + apfs_unmap_main_super(sb); +failed_main_super: + sb->s_fs_info = NULL; + kfree(sbi); + return err; +} + +static struct dentry *apfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_bdev(fs_type, flags, dev_name, data, apfs_fill_super); +} + +static struct file_system_type apfs_fs_type = { + .owner = THIS_MODULE, + .name = "apfs", + .mount = apfs_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +MODULE_ALIAS_FS("apfs"); + +static int __init init_apfs_fs(void) +{ + int err = 0; + + err = init_inodecache(); + if (err) + return err; + err = register_filesystem(&apfs_fs_type); + if (err) + destroy_inodecache(); + return err; +} + +static void __exit exit_apfs_fs(void) +{ + unregister_filesystem(&apfs_fs_type); + destroy_inodecache(); +} + +MODULE_AUTHOR("Ernesto A. Fernández"); +MODULE_DESCRIPTION("Apple File System"); +MODULE_LICENSE("GPL"); +module_init(init_apfs_fs) +module_exit(exit_apfs_fs) diff --git a/super.h b/super.h new file mode 100644 index 0000000..0da6591 --- /dev/null +++ b/super.h @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/super.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_SUPER_H +#define _APFS_SUPER_H + +#include +#include +#include "object.h" + +/* + * Structure used to store a range of physical blocks + */ +struct apfs_prange { + __le64 pr_start_paddr; + __le64 pr_block_count; +} __packed; + +/* Main container */ + +/* Container constants */ +#define APFS_SUPER_MAGIC 0x4253584E + +#define APFS_NX_MAGIC APFS_SUPER_MAGIC +#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 + +/* 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; + +/* 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_FLAGS_VALID_MASK (APFS_FS_UNENCRYPTED \ + | APFS_FS_EFFACEABLE \ + | APFS_FS_RESERVED_4 \ + | APFS_FS_ONEKEY \ + | APFS_FS_SPILLEDOVER \ + | APFS_FS_RUN_SPILLOVER_CLEANER) + +#define APFS_FS_CRYPTOFLAGS (APFS_FS_UNENCRYPTED \ + | APFS_FS_EFFACEABLE \ + | APFS_FS_ONEKEY) + +/* 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 { + char id[APFS_MODIFIED_NAMELEN]; + __le64 timestamp; + __le64 last_xid; +} __packed; + +/* + * 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; + +/* Mount option flags */ +#define APFS_UID_OVERRIDE 1 +#define APFS_GID_OVERRIDE 2 +#define APFS_CHECK_NODES 4 + +/* + * Superblock data in memory, both from the main superblock and the volume + * checkpoint superblock. + */ +struct apfs_sb_info { + struct apfs_nx_superblock *s_msb_raw; /* On-disk main sb */ + struct apfs_superblock *s_vsb_raw; /* On-disk volume sb */ + + u64 s_xid; /* Latest transaction id */ + struct apfs_node *s_cat_root; /* Root of the catalog tree */ + struct apfs_node *s_omap_root; /* Root of the object map tree */ + + struct apfs_object s_mobject; /* Main superblock object */ + struct apfs_object s_vobject; /* Volume superblock object */ + + /* Mount options */ + unsigned int s_flags; + unsigned int s_vol_nr; /* Index of the volume in the sb list */ + kuid_t s_uid; /* uid to override on-disk uid */ + kgid_t s_gid; /* gid to override on-disk gid */ + + /* TODO: handle block sizes above the maximum of PAGE_SIZE? */ + unsigned long s_blocksize; + unsigned char s_blocksize_bits; +}; + +static inline struct apfs_sb_info *APFS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline bool apfs_is_case_insensitive(struct super_block *sb) +{ + return (APFS_SB(sb)->s_vsb_raw->apfs_incompatible_features & + cpu_to_le64(APFS_INCOMPAT_CASE_INSENSITIVE)) != 0; +} + +#endif /* _APFS_SUPER_H */ diff --git a/symlink.c b/symlink.c new file mode 100644 index 0000000..f8c8071 --- /dev/null +++ b/symlink.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/symlink.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include "apfs.h" +#include "message.h" +#include "xattr.h" + +/** + * apfs_get_link - Follow a symbolic link + * @dentry: dentry for the link + * @inode: inode for the link + * @done: delayed call to free the returned buffer after use + * + * Returns a pointer to a buffer containing the target path, or an appropriate + * error pointer in case of failure. + */ +static const char *apfs_get_link(struct dentry *dentry, struct inode *inode, + struct delayed_call *done) +{ + struct super_block *sb = inode->i_sb; + char *target, *err; + int size; + + if (!dentry) + return ERR_PTR(-ECHILD); + + size = apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK, + NULL /* buffer */, 0 /* size */); + if (size < 0) /* TODO: return a better error code */ + return ERR_PTR(size); + + target = kmalloc(size, GFP_KERNEL); + if (!target) + return ERR_PTR(-ENOMEM); + + size = apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK, target, size); + if (size < 0) { + err = ERR_PTR(size); + goto fail; + } + if (size == 0 || *(target + size - 1) != 0) { + /* Target path must be NULL-terminated */ + apfs_alert(sb, "bad link target in inode 0x%llx", + (unsigned long long) inode->i_ino); + err = ERR_PTR(-EFSCORRUPTED); + goto fail; + } + + set_delayed_call(done, kfree_link, target); + return target; + +fail: + kfree(target); + return err; +} + +const struct inode_operations apfs_symlink_inode_operations = { + .get_link = apfs_get_link, + .getattr = apfs_getattr, + .listxattr = apfs_listxattr, +}; diff --git a/unicode.c b/unicode.c new file mode 100644 index 0000000..d04935b --- /dev/null +++ b/unicode.c @@ -0,0 +1,3149 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/unicode.c + * + * Copyright (C) 2018 Ernesto A. Fernández + * + * Routines and data for the normalization of unicode strings. + * Somewhat based on linux/fs/hfsplus/unicode.c + */ + +#include +#include +#include +#include +#include +#include +#include "unicode.h" + +/* The arrays of unicode data are defined at the bottom of the file */ +/* TODO: would a single trie with all the data be more efficient? */ +static u16 apfs_nfd_trie[]; +static unicode_t apfs_nfd[]; +static u16 apfs_cf_trie[]; +static unicode_t apfs_cf[]; +static u8 apfs_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) + +/** + * apfs_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 apfs_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; +} + +/** + * apfs_init_unicursor - Initialize an apfs_unicursor structure + * @cursor: cursor to initialize + * @utf8str: string to normalize + */ +void apfs_init_unicursor(struct apfs_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) + +/** + * apfs_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 apfs_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) + +/** + * apfs_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 apfs_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; +} + +/** + * apfs_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 apfs_normalize_char(unicode_t utf32char, int off, + bool case_fold) +{ + int nfd_len; + unicode_t *nfd, *cf; + u16 pos; + int ret; + + if (apfs_is_precomposed_hangul(utf32char)) /* Hangul has no case */ + return apfs_decompose_hangul(utf32char, off); + + ret = apfs_trie_find(apfs_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 = &apfs_nfd[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 = apfs_trie_find(apfs_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 = &apfs_cf[pos]; + } + + if (off < cf_len) + return cf[off]; + off -= cf_len; + } + + return NORM_END; +} + +/** + * apfs_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 apfs_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(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 = apfs_normalize_char(utf32char, pos, + case_fold); + if (utf32norm == NORM_END) + break; + + apfs_trie_find(apfs_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; + } +} + +/** + * apfs_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 apfs_normalize_next(struct apfs_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 = apfs_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(utf8str, 4, &utf32char); + for (pos = 0;; pos++, str_pos++) { + unicode_t utf32norm; + u8 ccc; + + utf32norm = apfs_normalize_char(utf32char, pos, + case_fold); + if (utf32norm == NORM_END) + break; + + apfs_trie_find(apfs_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 */ + apfs_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 apfs_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 apfs_nfd[] = { + 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 apfs_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 apfs_cf[] = { + 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 apfs_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, +}; diff --git a/unicode.h b/unicode.h new file mode 100644 index 0000000..8bd142b --- /dev/null +++ b/unicode.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/unicode.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_UNICODE_H +#define _APFS_UNICODE_H + +#include + +/* + * This structure helps apfs_normalize_next() to retrieve one normalized + * (and case-folded) UTF-32 character at a time from a UTF-8 string. + */ +struct apfs_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 apfs_init_unicursor(struct apfs_unicursor *cursor, + const char *utf8str); +extern unicode_t apfs_normalize_next(struct apfs_unicursor *cursor, + bool case_fold); + +#endif /* _APFS_UNICODE_H */ diff --git a/xattr.c b/xattr.c new file mode 100644 index 0000000..d5cfe9f --- /dev/null +++ b/xattr.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/fs/apfs/xattr.c + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#include +#include +#include "apfs.h" +#include "btree.h" +#include "extents.h" +#include "key.h" +#include "super.h" +#include "node.h" +#include "message.h" +#include "xattr.h" + +/** + * apfs_xattr_from_query - Read the xattr record found by a successful query + * @query: the query that found the record + * @xattr: Return parameter. The xattr record found. + * + * Reads the xattr record into @xattr and performs some basic sanity checks + * as a protection against crafted filesystems. Returns 0 on success or + * -EFSCORRUPTED otherwise. + * + * The caller must not free @query while @xattr is in use, because @xattr->name + * and @xattr->xdata point to data on disk. + */ +static int apfs_xattr_from_query(struct apfs_query *query, + struct apfs_xattr *xattr) +{ + struct apfs_xattr_val *xattr_val; + struct apfs_xattr_key *xattr_key; + char *raw = query->node->object.bh->b_data; + int datalen = query->len - sizeof(*xattr_val); + int namelen = query->key_len - sizeof(*xattr_key); + + if (namelen < 1 || datalen < 0) + return -EFSCORRUPTED; + + xattr_val = (struct apfs_xattr_val *)(raw + query->off); + xattr_key = (struct apfs_xattr_key *)(raw + query->key_off); + + if (namelen != le16_to_cpu(xattr_key->name_len)) + return -EFSCORRUPTED; + + /* The xattr name must be NULL-terminated */ + if (xattr_key->name[namelen - 1] != 0) + return -EFSCORRUPTED; + + xattr->has_dstream = le16_to_cpu(xattr_val->flags) & + APFS_XATTR_DATA_STREAM; + + if (xattr->has_dstream && datalen != sizeof(struct apfs_xattr_dstream)) + return -EFSCORRUPTED; + if (!xattr->has_dstream && datalen != le16_to_cpu(xattr_val->xdata_len)) + return -EFSCORRUPTED; + + xattr->name = xattr_key->name; + xattr->name_len = namelen - 1; /* Don't count the NULL termination */ + xattr->xdata = xattr_val->xdata; + xattr->xdata_len = datalen; + return 0; +} + +/** + * apfs_xattr_extents_read - Read the value of a xattr from its extents + * @parent: inode the attribute belongs to + * @xattr: the xattr structure + * @buffer: where to copy the attribute value + * @size: size of @buffer + * + * Copies the value of @xattr to @buffer, if provided. If @buffer is NULL, just + * computes the size of the buffer required. + * + * Returns the number of bytes used/required, or a negative error code in case + * of failure. + */ +static int apfs_xattr_extents_read(struct inode *parent, + struct apfs_xattr *xattr, + void *buffer, size_t size) +{ + struct super_block *sb = parent->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_key key; + struct apfs_query *query; + struct apfs_xattr_dstream *xdata; + u64 extent_id; + int length; + int ret; + int i; + + xdata = (struct apfs_xattr_dstream *) xattr->xdata; + length = le64_to_cpu(xdata->dstream.size); + if (length < 0 || length < le64_to_cpu(xdata->dstream.size)) + return -E2BIG; + + if (!buffer) /* All we want is the length */ + return length; + if (length > size) /* xattr won't fit in the buffer */ + return -ERANGE; + + extent_id = le64_to_cpu(xdata->xattr_obj_id); + /* We will read all the extents, starting with the last one */ + apfs_init_file_extent_key(extent_id, 0 /* offset */, &key); + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + query->key = &key; + query->flags = APFS_QUERY_CAT | APFS_QUERY_MULTIPLE | APFS_QUERY_EXACT; + + /* + * The logic in this loop would allow a crafted filesystem with a large + * number of redundant extents to become stuck for a long time. We use + * the xattr length to put a limit on the number of iterations. + */ + ret = -EFSCORRUPTED; + for (i = 0; i < (length >> parent->i_blkbits) + 2; i++) { + struct apfs_file_extent ext; + u64 block_count, file_off; + int err; + int j; + + err = apfs_btree_query(sb, &query); + if (err == -ENODATA) { /* No more records to search */ + ret = length; + goto done; + } + if (err) { + ret = err; + goto done; + } + + err = apfs_extent_from_query(query, &ext); + if (err) { + apfs_alert(sb, "bad extent for xattr in inode 0x%llx", + (unsigned long long) parent->i_ino); + ret = err; + goto done; + } + + block_count = ext.len >> sb->s_blocksize_bits; + file_off = ext.logical_addr; + for (j = 0; j < block_count; ++j) { + struct buffer_head *bh; + int bytes; + + if (length <= file_off) /* Read the whole extent */ + break; + bytes = min(sb->s_blocksize, + (unsigned long)(length - file_off)); + + bh = sb_bread(sb, ext.phys_block_num + j); + if (!bh) { + ret = -EIO; + goto done; + } + memcpy(buffer + file_off, bh->b_data, bytes); + brelse(bh); + file_off = file_off + bytes; + } + } + +done: + apfs_free_query(sb, query); + return ret; +} + +/** + * apfs_xattr_inline_read - Read the value of an inline xattr + * @parent: inode the attribute belongs to + * @xattr: the xattr structure + * @buffer: where to copy the attribute value + * @size: size of @buffer + * + * Copies the inline value of @xattr to @buffer, if provided. If @buffer is + * NULL, just computes the size of the buffer required. + * + * Returns the number of bytes used/required, or a negative error code in case + * of failure. + */ +static int apfs_xattr_inline_read(struct inode *parent, + struct apfs_xattr *xattr, + void *buffer, size_t size) +{ + int length = xattr->xdata_len; + + if (!buffer) /* All we want is the length */ + return length; + if (length > size) /* xattr won't fit in the buffer */ + return -ERANGE; + memcpy(buffer, xattr->xdata, length); + return length; +} + +/** + * apfs_xattr_get - Find and read a named attribute + * @inode: inode the attribute belongs to + * @name: name of the attribute + * @buffer: where to copy the attribute value + * @size: size of @buffer + * + * Finds an extended attribute and copies its value to @buffer, if provided. If + * @buffer is NULL, just computes the size of the buffer required. + * + * Returns the number of bytes used/required, or a negative error code in case + * of failure. + */ +int apfs_xattr_get(struct inode *inode, const char *name, void *buffer, + size_t size) +{ + struct super_block *sb = inode->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_key key; + struct apfs_query *query; + struct apfs_xattr xattr; + u64 cnid = inode->i_ino; + int ret; + + apfs_init_xattr_key(cnid, name, &key); + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + query->key = &key; + query->flags |= APFS_QUERY_CAT | APFS_QUERY_EXACT; + + ret = apfs_btree_query(sb, &query); + if (ret) + goto done; + + ret = apfs_xattr_from_query(query, &xattr); + if (ret) { + apfs_alert(sb, "bad xattr record in inode 0x%llx", cnid); + goto done; + } + + if (xattr.has_dstream) + ret = apfs_xattr_extents_read(inode, &xattr, buffer, size); + else + ret = apfs_xattr_inline_read(inode, &xattr, buffer, size); + +done: + apfs_free_query(sb, query); + return ret; +} + +static int apfs_xattr_osx_get(const struct xattr_handler *handler, + struct dentry *unused, struct inode *inode, + const char *name, void *buffer, size_t size) +{ + /* Ignore the fake 'osx' prefix */ + return apfs_xattr_get(inode, name, buffer, size); +} + +static const struct xattr_handler apfs_xattr_osx_handler = { + .prefix = XATTR_MAC_OSX_PREFIX, + .get = apfs_xattr_osx_get, +}; + +/* On-disk xattrs have no namespace; use a fake 'osx' prefix in the kernel */ +const struct xattr_handler *apfs_xattr_handlers[] = { + &apfs_xattr_osx_handler, + NULL +}; + +ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct inode *inode = d_inode(dentry); + struct super_block *sb = inode->i_sb; + struct apfs_sb_info *sbi = APFS_SB(sb); + struct apfs_key key; + struct apfs_query *query; + u64 cnid = inode->i_ino; + size_t free = size; + ssize_t ret; + + query = apfs_alloc_query(sbi->s_cat_root, NULL /* parent */); + if (!query) + return -ENOMEM; + + /* We want all the xattrs for the cnid, regardless of the name */ + apfs_init_xattr_key(cnid, NULL /* name */, &key); + query->key = &key; + query->flags = APFS_QUERY_CAT | APFS_QUERY_MULTIPLE | APFS_QUERY_EXACT; + + while (1) { + struct apfs_xattr xattr; + + ret = apfs_btree_query(sb, &query); + if (ret == -ENODATA) { /* Got all the xattrs */ + ret = size - free; + break; + } + if (ret) + break; + + ret = apfs_xattr_from_query(query, &xattr); + if (ret) { + apfs_alert(sb, "bad xattr key in inode %llx", cnid); + break; + } + + if (buffer) { + /* Prepend the fake 'osx' prefix before listing */ + if (xattr.name_len + XATTR_MAC_OSX_PREFIX_LEN + 1 > + free) { + ret = -ERANGE; + break; + } + memcpy(buffer, XATTR_MAC_OSX_PREFIX, + XATTR_MAC_OSX_PREFIX_LEN); + buffer += XATTR_MAC_OSX_PREFIX_LEN; + memcpy(buffer, xattr.name, xattr.name_len + 1); + buffer += xattr.name_len + 1; + } + free -= xattr.name_len + XATTR_MAC_OSX_PREFIX_LEN + 1; + } + + apfs_free_query(sb, query); + return ret; +} diff --git a/xattr.h b/xattr.h new file mode 100644 index 0000000..2aa0c12 --- /dev/null +++ b/xattr.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/fs/apfs/xattr.h + * + * Copyright (C) 2018 Ernesto A. Fernández + */ + +#ifndef _APFS_XATTR_H +#define _APFS_XATTR_H + +#include +#include "inode.h" + +/* 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" + +/* 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, +}; + +/* + * 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; + +/* + * Xattr record data in memory + */ +struct apfs_xattr { + u8 *name; + u8 *xdata; + int name_len; + int xdata_len; + bool has_dstream; +}; + +extern int apfs_xattr_get(struct inode *inode, const char *name, void *buffer, + size_t size); +extern ssize_t apfs_listxattr(struct dentry *dentry, char *buffer, size_t size); + +extern const struct xattr_handler *apfs_xattr_handlers[]; + +#endif /* _APFS_XATTR_H */