├── .gitignore ├── Android.bp ├── COPYING ├── ChangeLog ├── Makefile.am ├── README.md ├── configure.ac ├── dump ├── Makefile.am ├── dumpexfat.8 └── main.c ├── fsck ├── Makefile.am ├── exfatfsck.8 └── main.c ├── fuse ├── Makefile.am ├── main.c └── mount.exfat-fuse.8 ├── label ├── Makefile.am ├── exfatlabel.8 └── main.c ├── libexfat ├── Makefile.am ├── byteorder.h ├── cluster.c ├── compiler.h ├── exfat.h ├── exfatfs.h ├── io.c ├── log.c ├── lookup.c ├── mount.c ├── node.c ├── platform.h ├── repair.c ├── time.c ├── utf.c └── utils.c └── mkfs ├── Makefile.am ├── cbm.c ├── cbm.h ├── fat.c ├── fat.h ├── main.c ├── mkexfat.c ├── mkexfat.h ├── mkexfatfs.8 ├── rootdir.c ├── rootdir.h ├── uct.c ├── uct.h ├── uctc.c ├── uctc.h ├── vbr.c └── vbr.h /.gitignore: -------------------------------------------------------------------------------- 1 | # autoreconf -i 2 | Makefile.in 3 | /aclocal.m4 4 | /ar-lib 5 | /autom4te.cache/ 6 | /compile 7 | /configure 8 | /depcomp 9 | /install-sh 10 | /libexfat/config.h.in 11 | /missing 12 | 13 | # ./configure 14 | .deps/ 15 | Makefile 16 | /config.log 17 | /config.status 18 | /libexfat/config.h 19 | /libexfat/stamp-h1 20 | 21 | # make 22 | *.o 23 | /dump/dumpexfat 24 | /fsck/exfatfsck 25 | /fuse/mount.exfat-fuse 26 | /label/exfatlabel 27 | /libexfat/libexfat.a 28 | /mkfs/mkexfatfs 29 | -------------------------------------------------------------------------------- /Android.bp: -------------------------------------------------------------------------------- 1 | /* 2 | * Free exFAT implementation. 3 | * Copyright (C) 2017 liminghao, LongPingWEI 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | cc_defaults { 21 | name: "exfat_defaults", 22 | 23 | cflags: [ 24 | "-std=gnu99", 25 | "-Wno-error", 26 | "-Wno-unused-parameter", 27 | "-D_FILE_OFFSET_BITS=64", 28 | "-DPACKAGE=\"exfat\"", 29 | "-DVERSION=\"1.3.0\"", 30 | ], 31 | shared_libs: ["liblog"], 32 | } 33 | 34 | cc_library_static { 35 | name: "libexfat", 36 | 37 | srcs: ["libexfat/*.c"], 38 | defaults: ["exfat_defaults"], 39 | local_include_dirs: ["libexfat"], 40 | export_include_dirs: ["libexfat"], 41 | } 42 | 43 | 44 | cc_binary { 45 | name: "mkfs.exfat", 46 | 47 | srcs: ["mkfs/*.c"], 48 | defaults: ["exfat_defaults"], 49 | local_include_dirs: ["mkfs"], 50 | static_libs: ["libexfat"], 51 | } 52 | 53 | cc_binary { 54 | name: "fsck.exfat", 55 | 56 | srcs: ["fsck/main.c"], 57 | defaults: ["exfat_defaults"], 58 | local_include_dirs: ["fsck"], 59 | static_libs: ["libexfat"], 60 | } 61 | 62 | cc_binary { 63 | name: "dumpexfat", 64 | 65 | srcs: ["dump/main.c"], 66 | defaults: ["exfat_defaults"], 67 | local_include_dirs: ["dump"], 68 | static_libs: ["libexfat"], 69 | } 70 | 71 | cc_binary { 72 | name: "exfatlabel", 73 | 74 | srcs: ["label/main.c"], 75 | defaults: ["exfat_defaults"], 76 | local_include_dirs: ["label"], 77 | static_libs: ["libexfat"], 78 | } 79 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 1.3.0 (2018-09-15) 2 | 3 | * exfatfsck can now repair some errors. 4 | * Added experimental Android support for exfat-utils [liminghao, LongPingWEI, 5 | Pablo Mendez Hernandez, Pierre-Hugues Husson]. 6 | * Cleaned up FUSE code preparing for FUSE 3 support. 7 | * Removed OpenBSD support as it does not handle -o option in fuse_main(). 8 | * Re-introduced FreeBSD support [Oleksii Samorukov]. 9 | * Fixed DragonFly BSD support [Tomohiro Kusumi]. 10 | * dirent->d_type in now filled on readdir() [Mark Browning]. 11 | 12 | 1.2.8 (2018-02-03) 13 | 14 | * Fixed new files or directories creation in the root directory: ensure there 15 | are no 0x00 entries before valid ones; otherwise Windows can corrupt them. 16 | * Fixed compilation on GNU/HURD platform. 17 | 18 | 1.2.7 (2017-06-05) 19 | 20 | * Fixed handling of two last clusters: operations with files that occupy these 21 | clusters could fail. 22 | * Fixed crash when started with stdin, stdout or stderr closed. 23 | 24 | 1.2.6 (2017-01-28) 25 | 26 | * Operations with directories (except initial listing) now make less 27 | read/write system calls. 28 | * Fixed handling of files with optional tail entries (0xe0-0xff): videoclip 29 | files created by Sony cameras were missing. 30 | * Write operations now correctly return ENOSPC (instead of EIO) when there is 31 | no free disk space left. 32 | * Fixed max file name length: it's 255 16-bit code units (not 256). 33 | 34 | 1.2.5 (2016-12-05) 35 | 36 | * Added an option for dumpexfat to show file fragments [Daniel Drake]. 37 | * Fixed crash when directory starts with an invalid cluster. 38 | * Daylight saving time in now properly reflected in file timestamps. 39 | 40 | 1.2.4 (2016-06-03) 41 | 42 | * Fixed wrong files names hashes when upper case table is compressed. 43 | * Man pages are now installed by default. 44 | * Commas and backslashes in device names are now escaped. 45 | 46 | 1.2.3 (2015-12-19) 47 | 48 | * Fixed clusters loss when file renaming replaces target. 49 | 50 | 1.2.2 (2015-11-09) 51 | 52 | * Improved reliability in case of a sudden unplug: FS will be in a clean state 53 | after closing all files and performing sync(1). 54 | * Fixed compilation on Debian GNU/kFreeBSD and GNU/Hurd platforms. 55 | * Updated mount.exfat-fuse man page. 56 | 57 | 1.2.1 (2015-09-24) 58 | 59 | * Fixed compatibility with Zalman VE-200: now newly created directories do not 60 | have archive bit set. 61 | * Fixed heap corruption: malformed FS can use invalid sector or cluster size. 62 | * Fixed hang on mount: malformed FS can have cyclic references in the clusters 63 | map. 64 | 65 | 1.2.0 (2015-08-26) 66 | 67 | * Switched from SCons to autotools. 68 | * Added musl libc support [Brendan Heading]. 69 | * Worked around "FS is larger than device" error for memory cards formatted by 70 | Panasonic Lumix cameras. 71 | * Worked around "unknown entry type 0xe1" error for memory cards formatted by 72 | Sony cameras. 73 | 74 | 1.1.1 (2014-11-15) 75 | 76 | * Fixed mkfs crash on some sectors-per-cluster (-s option) values. 77 | 78 | 1.1.0 (2014-07-08) 79 | 80 | * Relicensed the project from GPLv3+ to GPLv2+. 81 | * OpenBSD support [Helg Bredow]. 82 | * Improved I/O errors handling. 83 | * Implemented fsync() and fsyncdir(). 84 | * Fixed crash on Mac OS X 10.5 caused by non-standard use of realpath(). Also 85 | fixed TrueCrypt disks unmounting. 86 | * Avoid extra erase on writes to the end of a file. This should improve linear 87 | write speed. 88 | * Allow arbitrary changing of lower 9 bits of mode. Allow owner/group changing 89 | to the same owner/group. This fixes rsync. 90 | * Fixed buffers overflows when handling lengthy file names. 91 | * Fixed "real size does not equal to size" error on volumes with pagefile.sys. 92 | * Fixed negative IUsed in "df -i" output. 93 | 94 | 1.0.1 (2013-02-02) 95 | 96 | * Fixed unexpected removal of a directory if it is moved into itself. 97 | * Fixed "Operation not permitted" error on reading an empty file. 98 | 99 | 1.0.0 (2013-01-19) 100 | 101 | * Fixed crash when renaming a file within a single directory and a new name 102 | differs only in case. 103 | * Fixed clusters allocation: a cluster beyond valid clusters range could be 104 | allocated. 105 | * Fixed crash when a volume is unmounted while some files are open. 106 | * SConscript now respects AR and RANLIB environment variables. 107 | * Improved error handling. 108 | 109 | Linux: 110 | 111 | * Enabled big_writes. This improves write speed (larger block size means less 112 | switches between kernel- and user-space). 113 | * Do BLKROGET ioctl to make sure the device is not read-only: after 114 | "blockdev --setro" kernel still allows to open the device in read-write mode 115 | but fails writes. 116 | 117 | OS X: 118 | 119 | * Fixed OS X 10.8 support. 120 | * Switched to 64-bit inode numbers (now Mac OS X 10.5 or later is required). 121 | * Switched from unmaintained MacFUSE to OSXFUSE (http://osxfuse.github.com). 122 | * Fixed device size detection. Now mkfs works. 123 | * Workarounded some utilities failures due to missing chmod() support. 124 | * Disabled (senseless) permission checks made by FUSE. 125 | 126 | 0.9.8 (2012-08-09) 127 | 128 | * The mkfs utility can now create huge file systems (up to several exabytes). 129 | * Fixed handling of characters beyond Basic Multilingual Plane. 130 | * Echo messages to syslog only if stderr is not connected to a terminal. 131 | 132 | 0.9.7 (2012-03-08) 133 | 134 | * Out-of-the-box FreeBSD support (via ublio library). 135 | * Fixed "missing EOD entry" error (could happen while reading directory that 136 | consists of several clusters). 137 | * Fixed interpretation of minutes field in files timestamps (minutes could be 138 | displayed incorrectly). 139 | * Fixed mtime seconds field initialization for newly created file (mtime could 140 | be 1 sec less than creation time). 141 | * SConscript now respects CC, CCFLAGS and LDFLAGS environment variables. 142 | 143 | 0.9.6 (2012-01-14) 144 | 145 | * Fixed write performance regression introduced in 0.9.4. 146 | * Mount in read-only mode if the device is write-protected. 147 | * Set ctime to mtime to ensure we don't break programs that rely on ctime 148 | (e.g. rsync considered that all files are outdated) [Eldad Zack]. 149 | * Indicate that FS in not clean when it was not cleanly unmounted. 150 | * Utilities are now compatible with GNU/Hurd. 151 | * Fixed several memory leaks that could occur on error handling paths. 152 | * Improved handling of corrupted file systems. 153 | 154 | 0.9.5 (2011-05-15) 155 | 156 | * Fixed erasing of the root directory cluster when creating a new FS with 157 | mkexfatfs. This bug could cause mkexfatfs to produce invalid FS. 158 | * Utilities are not linked with libfuse anymore. 159 | * Ensure that the path being opened is either a device or a regular file. 160 | 161 | 0.9.4 (2011-03-05) 162 | 163 | * Introduced exfat-utils: dumpexfat, exfatfsck, mkexfatfs, exfatlabel. 164 | * Fixed "Invalid argument" error while mounting a volume from a disk with sector size greater than 512 bytes. 165 | * Wait for all data to be flushed to disk on unmount. 166 | * Kernel cache is no longer flushed on open. This can slightly improve read performance by avoiding extra read requests from kernel to user-space. 167 | * Allow to unmount volumes as user (fusermount -u) if they were mounted from the very same user [Tino Lange]. 168 | * Errors and warnings are now duplicated to syslog. 169 | 170 | 0.9.3 (2010-09-25) 171 | 172 | * Directories now can shrink. 173 | * Improved timestamps resolution from 2 sec to 1 sec. 174 | * Fixed timestamps displaying under Mac OS X when compiled for i386 or ppc. 175 | * Fixed FS size displaying for non-GNU systems. 176 | 177 | 0.9.2 (2010-07-24) 178 | 179 | * Fixed a bug which could cause the whole directory to become unreadable after renaming a file in it. 180 | * Support for Solaris and various *BSD [Albert Lee]. 181 | * Improved error handling on corrupted volumes. 182 | * Improved allowed file name characters filter. 183 | * Added man page. 184 | 185 | 0.9.1 (2010-06-12) 186 | 187 | * Implemented automounting (util-linux-ng 2.18 or later is required). 188 | * Fixed mounting when cluster bitmap is larger than expected. 189 | * Fixed crash on statfs() when root directory contains error. 190 | * Fixed bugs specific to big-endian machines. 191 | * Other bugfixes. 192 | 193 | 0.9.0 (2010-03-21) 194 | 195 | * Initial release. 196 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | SUBDIRS = libexfat dump fsck fuse label mkfs 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About 2 | ----- 3 | 4 | This project aims to provide a full-featured [exFAT][1] file system implementation for Unix-like systems. It consists of a [FUSE][2] module (fuse-exfat) and a set of utilities (exfat-utils). 5 | 6 | Supported operating systems: 7 | 8 | * GNU/Linux 9 | * Mac OS X 10.5 or later 10 | * FreeBSD 11 | 12 | Most GNU/Linux distributions already have fuse-exfat and exfat-utils in their repositories, so you can just install and use them. The next chapter describes how to compile them from source. 13 | 14 | Compiling 15 | --------- 16 | 17 | To build this project on GNU/Linux you need to install the following packages: 18 | 19 | * [git][4] 20 | * [autoconf][5] 21 | * [automake][6] 22 | * [pkg-config][7] 23 | * fuse-devel (or libfuse-dev) 24 | * [gcc][8] 25 | * [make][9] 26 | 27 | On Mac OS X: 28 | 29 | * autoconf 30 | * automake 31 | * pkg-config 32 | * [OSXFUSE][10] 33 | * [Xcode][11] (legacy versions include autotools but their versions are too old) 34 | 35 | On OpenBSD: 36 | 37 | * git 38 | * autoconf (set AUTOCONF_VERSION environment variable) 39 | * automake (set AUTOMAKE_VERSION environment variable) 40 | 41 | Get the source code, change directory and compile: 42 | 43 | git clone https://github.com/relan/exfat.git 44 | cd exfat 45 | autoreconf --install 46 | ./configure 47 | make 48 | 49 | Then install driver and utilities (from root): 50 | 51 | make install 52 | 53 | You can remove them using this command (from root): 54 | 55 | make uninstall 56 | 57 | Mounting 58 | -------- 59 | 60 | Modern GNU/Linux distributions (with [util-linux][12] 2.18 or later) will mount exFAT volumes automatically. Anyway, you can mount manually (from root): 61 | 62 | mount.exfat-fuse /dev/spec /mnt/exfat 63 | 64 | where /dev/spec is the [device file][13], /mnt/exfat is a mountpoint. 65 | 66 | Feedback 67 | -------- 68 | 69 | If you have any questions, issues, suggestions, bug reports, etc. please create an [issue][3]. Pull requests are also welcome! 70 | 71 | [1]: https://en.wikipedia.org/wiki/ExFAT 72 | [2]: https://en.wikipedia.org/wiki/Filesystem_in_Userspace 73 | [3]: https://github.com/relan/exfat/issues 74 | [4]: https://www.git-scm.com/ 75 | [5]: https://www.gnu.org/software/autoconf/ 76 | [6]: https://www.gnu.org/software/automake/ 77 | [7]: http://www.freedesktop.org/wiki/Software/pkg-config/ 78 | [8]: https://gcc.gnu.org/ 79 | [9]: https://www.gnu.org/software/make/ 80 | [10]: https://osxfuse.github.io/ 81 | [11]: https://en.wikipedia.org/wiki/Xcode 82 | [12]: https://www.kernel.org/pub/linux/utils/util-linux/ 83 | [13]: https://en.wikipedia.org/wiki/Device_file 84 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # configure.ac (30.03.15) 3 | # Autoconf source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | AC_INIT([Free exFAT implementation], 24 | [1.3.0], 25 | [relan@users.noreply.github.com], 26 | [exfat], 27 | [https://github.com/relan/exfat]) 28 | AM_INIT_AUTOMAKE([1.11.2 -Wall -Werror foreign subdir-objects]) 29 | AC_PROG_CC 30 | AC_PROG_CC_C99 31 | AC_PROG_RANLIB 32 | AM_PROG_AR 33 | AC_SYS_LARGEFILE 34 | AC_CANONICAL_HOST 35 | PKG_CHECK_MODULES([UBLIO], [libublio], [ 36 | CFLAGS="$CFLAGS $UBLIO_CFLAGS" 37 | LIBS="$LIBS $UBLIO_LIBS" 38 | AC_DEFINE([USE_UBLIO], [1], 39 | [Define if block devices are not supported.]) 40 | ], [:]) 41 | PKG_CHECK_MODULES([FUSE], [fuse]) 42 | case "$host_os" in 43 | *-gnu) 44 | AC_DEFINE([_XOPEN_SOURCE], [500], [Enable pread() and pwrite().]) 45 | AC_DEFINE([_DEFAULT_SOURCE], [], [Enable vsyslog().]) 46 | ;; 47 | esac 48 | AC_CONFIG_HEADERS([libexfat/config.h]) 49 | AC_CONFIG_FILES([ 50 | libexfat/Makefile 51 | dump/Makefile 52 | fsck/Makefile 53 | fuse/Makefile 54 | label/Makefile 55 | mkfs/Makefile 56 | Makefile]) 57 | AC_OUTPUT 58 | -------------------------------------------------------------------------------- /dump/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | sbin_PROGRAMS = dumpexfat 24 | dist_man8_MANS = dumpexfat.8 25 | dumpexfat_SOURCES = main.c 26 | dumpexfat_CPPFLAGS = -I$(top_srcdir)/libexfat 27 | dumpexfat_LDADD = ../libexfat/libexfat.a 28 | -------------------------------------------------------------------------------- /dump/dumpexfat.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2018 Andrew Nayenko 2 | .\" 3 | .TH DUMPEXFAT 8 "July 2016" 4 | .SH NAME 5 | .B dumpexfat 6 | \- dump exFAT file system 7 | .SH SYNOPSIS 8 | .B dumpexfat 9 | [ 10 | .B \-s 11 | ] 12 | [ 13 | .B \-u 14 | ] 15 | [ 16 | .B \-f 17 | .I file 18 | ] 19 | [ 20 | .B \-V 21 | ] 22 | .I device 23 | 24 | .SH DESCRIPTION 25 | .B dumpexfat 26 | dumps details about exFAT file system including low-level info. All sizes are 27 | in bytes. 28 | 29 | .SH OPTIONS 30 | Command line options available: 31 | .TP 32 | .B \-s 33 | Dump only info from super block. May be useful for heavily corrupted file 34 | systems. 35 | .TP 36 | .B \-u 37 | Dump ranges of used sectors starting from 0 and separated with spaces. May be 38 | useful for backup tools. 39 | .TP 40 | .B \-f file 41 | Print out a list of fragments that compose the given file. Each fragment is 42 | printed on its own line, as the start offset (in bytes) into the file system, 43 | and the length (in bytes). 44 | .TP 45 | .BI \-V 46 | Print version and copyright. 47 | 48 | .SH EXIT CODES 49 | Zero is returned on success. Any other code means an error. 50 | 51 | .SH AUTHOR 52 | Andrew Nayenko 53 | 54 | .SH SEE ALSO 55 | .BR mkexfatfs (8) 56 | -------------------------------------------------------------------------------- /dump/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (08.11.10) 3 | Prints detailed information about exFAT volume. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | static void print_generic_info(const struct exfat_super_block* sb) 31 | { 32 | printf("Volume serial number 0x%08x\n", 33 | le32_to_cpu(sb->volume_serial)); 34 | printf("FS version %hhu.%hhu\n", 35 | sb->version.major, sb->version.minor); 36 | printf("Sector size %10u\n", 37 | SECTOR_SIZE(*sb)); 38 | printf("Cluster size %10u\n", 39 | CLUSTER_SIZE(*sb)); 40 | } 41 | 42 | static void print_sector_info(const struct exfat_super_block* sb) 43 | { 44 | printf("Sectors count %10"PRIu64"\n", 45 | le64_to_cpu(sb->sector_count)); 46 | } 47 | 48 | static void print_cluster_info(const struct exfat_super_block* sb) 49 | { 50 | printf("Clusters count %10u\n", 51 | le32_to_cpu(sb->cluster_count)); 52 | } 53 | 54 | static void print_other_info(const struct exfat_super_block* sb) 55 | { 56 | printf("First sector %10"PRIu64"\n", 57 | le64_to_cpu(sb->sector_start)); 58 | printf("FAT first sector %10u\n", 59 | le32_to_cpu(sb->fat_sector_start)); 60 | printf("FAT sectors count %10u\n", 61 | le32_to_cpu(sb->fat_sector_count)); 62 | printf("First cluster sector %10u\n", 63 | le32_to_cpu(sb->cluster_sector_start)); 64 | printf("Root directory cluster %10u\n", 65 | le32_to_cpu(sb->rootdir_cluster)); 66 | printf("Volume state 0x%04hx\n", 67 | le16_to_cpu(sb->volume_state)); 68 | printf("FATs count %10hhu\n", 69 | sb->fat_count); 70 | printf("Drive number 0x%02hhx\n", 71 | sb->drive_no); 72 | printf("Allocated space %9hhu%%\n", 73 | sb->allocated_percent); 74 | } 75 | 76 | static int dump_sb(const char* spec) 77 | { 78 | struct exfat_dev* dev; 79 | struct exfat_super_block sb; 80 | 81 | dev = exfat_open(spec, EXFAT_MODE_RO); 82 | if (dev == NULL) 83 | return 1; 84 | 85 | if (exfat_read(dev, &sb, sizeof(struct exfat_super_block)) < 0) 86 | { 87 | exfat_close(dev); 88 | exfat_error("failed to read from '%s'", spec); 89 | return 1; 90 | } 91 | if (memcmp(sb.oem_name, "EXFAT ", sizeof(sb.oem_name)) != 0) 92 | { 93 | exfat_close(dev); 94 | exfat_error("exFAT file system is not found on '%s'", spec); 95 | return 1; 96 | } 97 | 98 | print_generic_info(&sb); 99 | print_sector_info(&sb); 100 | print_cluster_info(&sb); 101 | print_other_info(&sb); 102 | 103 | exfat_close(dev); 104 | return 0; 105 | } 106 | 107 | static void dump_sectors(struct exfat* ef) 108 | { 109 | off_t a = 0, b = 0; 110 | 111 | printf("Used sectors "); 112 | while (exfat_find_used_sectors(ef, &a, &b) == 0) 113 | printf(" %"PRIu64"-%"PRIu64, a, b); 114 | puts(""); 115 | } 116 | 117 | static int dump_full(const char* spec, bool used_sectors) 118 | { 119 | struct exfat ef; 120 | uint32_t free_clusters; 121 | uint64_t free_sectors; 122 | 123 | if (exfat_mount(&ef, spec, "ro") != 0) 124 | return 1; 125 | 126 | free_clusters = exfat_count_free_clusters(&ef); 127 | free_sectors = (uint64_t) free_clusters << ef.sb->spc_bits; 128 | 129 | printf("Volume label %15s\n", exfat_get_label(&ef)); 130 | print_generic_info(ef.sb); 131 | print_sector_info(ef.sb); 132 | printf("Free sectors %10"PRIu64"\n", free_sectors); 133 | print_cluster_info(ef.sb); 134 | printf("Free clusters %10u\n", free_clusters); 135 | print_other_info(ef.sb); 136 | if (used_sectors) 137 | dump_sectors(&ef); 138 | 139 | exfat_unmount(&ef); 140 | return 0; 141 | } 142 | 143 | static int dump_file_fragments(const char* spec, const char* path) 144 | { 145 | struct exfat ef; 146 | struct exfat_node* node; 147 | cluster_t cluster; 148 | cluster_t next_cluster; 149 | cluster_t fragment_start_cluster; 150 | off_t remainder; 151 | off_t fragment_size = 0; 152 | int rc = 0; 153 | 154 | if (exfat_mount(&ef, spec, "ro") != 0) 155 | return 1; 156 | 157 | rc = exfat_lookup(&ef, &node, path); 158 | if (rc != 0) 159 | { 160 | exfat_unmount(&ef); 161 | exfat_error("'%s': %s", path, strerror(-rc)); 162 | return 1; 163 | } 164 | 165 | cluster = fragment_start_cluster = node->start_cluster; 166 | remainder = node->size; 167 | while (remainder > 0) 168 | { 169 | off_t lsize; 170 | 171 | if (CLUSTER_INVALID(*ef.sb, cluster)) 172 | { 173 | exfat_error("'%s' has invalid cluster %#x", path, cluster); 174 | rc = 1; 175 | break; 176 | } 177 | 178 | lsize = MIN(CLUSTER_SIZE(*ef.sb), remainder); 179 | fragment_size += lsize; 180 | remainder -= lsize; 181 | 182 | next_cluster = exfat_next_cluster(&ef, node, cluster); 183 | if (next_cluster != cluster + 1 || remainder == 0) 184 | { 185 | /* next cluster is not contiguous or this is EOF */ 186 | printf("%"PRIu64" %"PRIu64"\n", 187 | exfat_c2o(&ef, fragment_start_cluster), fragment_size); 188 | /* start a new fragment */ 189 | fragment_start_cluster = next_cluster; 190 | fragment_size = 0; 191 | } 192 | cluster = next_cluster; 193 | } 194 | 195 | exfat_put_node(&ef, node); 196 | exfat_unmount(&ef); 197 | return rc; 198 | } 199 | 200 | static void usage(const char* prog) 201 | { 202 | fprintf(stderr, "Usage: %s [-s] [-u] [-f file] [-V] \n", prog); 203 | exit(1); 204 | } 205 | 206 | int main(int argc, char* argv[]) 207 | { 208 | int opt; 209 | const char* spec = NULL; 210 | bool sb_only = false; 211 | bool used_sectors = false; 212 | const char* file_path = NULL; 213 | 214 | while ((opt = getopt(argc, argv, "suf:V")) != -1) 215 | { 216 | switch (opt) 217 | { 218 | case 's': 219 | sb_only = true; 220 | break; 221 | case 'u': 222 | used_sectors = true; 223 | break; 224 | case 'f': 225 | file_path = optarg; 226 | break; 227 | case 'V': 228 | printf("dumpexfat %s\n", VERSION); 229 | puts("Copyright (C) 2011-2018 Andrew Nayenko"); 230 | return 0; 231 | default: 232 | usage(argv[0]); 233 | } 234 | } 235 | if (argc - optind != 1) 236 | usage(argv[0]); 237 | spec = argv[optind]; 238 | 239 | if (file_path) 240 | return dump_file_fragments(spec, file_path); 241 | 242 | if (sb_only) 243 | return dump_sb(spec); 244 | 245 | return dump_full(spec, used_sectors); 246 | } 247 | -------------------------------------------------------------------------------- /fsck/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | sbin_PROGRAMS = exfatfsck 24 | dist_man8_MANS = exfatfsck.8 25 | exfatfsck_SOURCES = main.c 26 | exfatfsck_CPPFLAGS = -I$(top_srcdir)/libexfat 27 | exfatfsck_LDADD = ../libexfat/libexfat.a 28 | 29 | install-exec-hook: 30 | ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/fsck.exfat 31 | 32 | uninstall-hook: 33 | rm -f $(DESTDIR)$(sbindir)/fsck.exfat 34 | -------------------------------------------------------------------------------- /fsck/exfatfsck.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2018 Andrew Nayenko 2 | .\" 3 | .TH EXFATFSCK 8 "September 2018" 4 | .SH NAME 5 | .B exfatfsck 6 | \- check an exFAT file system 7 | .SH SYNOPSIS 8 | .B exfatfsck 9 | [ 10 | .B \-a 11 | | 12 | .B \-n 13 | | 14 | .B \-p 15 | | 16 | .B \-y 17 | ] 18 | .I device 19 | .br 20 | .B exfatfsck 21 | [ 22 | .B \-V 23 | ] 24 | 25 | .SH DESCRIPTION 26 | .B exfatfsck 27 | checks an exFAT file system for errors. It can repair some of them. 28 | 29 | .SH COMMAND LINE OPTIONS 30 | Command line options available: 31 | .TP 32 | .BI \-a 33 | Automatically repair the file system. No user intervention required. 34 | .TP 35 | .BI \-n 36 | No-operation mode: non-interactively check for errors, but don't write 37 | anything to the file system. 38 | .TP 39 | .BI \-p 40 | Same as \fB\-a\fR for compatibility with other *fsck. 41 | .TP 42 | .BI \-V 43 | Print version and copyright. 44 | .TP 45 | .BI \-y 46 | Same as \fB\-a\fR for compatibility with other *fsck. 47 | 48 | .SH EXIT CODES 49 | Zero is returned if errors were not found. Any other code means an error. 50 | 51 | .SH AUTHOR 52 | Andrew Nayenko 53 | 54 | .SH SEE ALSO 55 | .BR fsck (8) 56 | -------------------------------------------------------------------------------- /fsck/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (02.09.09) 3 | exFAT file system checker. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define exfat_debug(format, ...) 30 | 31 | uint64_t files_count, directories_count; 32 | 33 | static int nodeck(struct exfat* ef, struct exfat_node* node) 34 | { 35 | const cluster_t cluster_size = CLUSTER_SIZE(*ef->sb); 36 | cluster_t clusters = DIV_ROUND_UP(node->size, cluster_size); 37 | cluster_t c = node->start_cluster; 38 | int rc = 0; 39 | 40 | while (clusters--) 41 | { 42 | if (CLUSTER_INVALID(*ef->sb, c)) 43 | { 44 | char name[EXFAT_UTF8_NAME_BUFFER_MAX]; 45 | 46 | exfat_get_name(node, name); 47 | exfat_error("file '%s' has invalid cluster 0x%x", name, c); 48 | rc = 1; 49 | break; 50 | } 51 | if (BMAP_GET(ef->cmap.chunk, c - EXFAT_FIRST_DATA_CLUSTER) == 0) 52 | { 53 | char name[EXFAT_UTF8_NAME_BUFFER_MAX]; 54 | 55 | exfat_get_name(node, name); 56 | exfat_error("cluster 0x%x of file '%s' is not allocated", c, name); 57 | rc = 1; 58 | } 59 | c = exfat_next_cluster(ef, node, c); 60 | } 61 | return rc; 62 | } 63 | 64 | static void dirck(struct exfat* ef, const char* path) 65 | { 66 | struct exfat_node* parent; 67 | struct exfat_node* node; 68 | struct exfat_iterator it; 69 | int rc; 70 | size_t path_length; 71 | char* entry_path; 72 | 73 | if (exfat_lookup(ef, &parent, path) != 0) 74 | exfat_bug("directory '%s' is not found", path); 75 | if (!(parent->attrib & EXFAT_ATTRIB_DIR)) 76 | exfat_bug("'%s' is not a directory (%#hx)", path, parent->attrib); 77 | if (nodeck(ef, parent) != 0) 78 | { 79 | exfat_put_node(ef, parent); 80 | return; 81 | } 82 | 83 | path_length = strlen(path); 84 | entry_path = malloc(path_length + 1 + EXFAT_UTF8_NAME_BUFFER_MAX); 85 | if (entry_path == NULL) 86 | { 87 | exfat_put_node(ef, parent); 88 | exfat_error("out of memory"); 89 | return; 90 | } 91 | strcpy(entry_path, path); 92 | strcat(entry_path, "/"); 93 | 94 | rc = exfat_opendir(ef, parent, &it); 95 | if (rc != 0) 96 | { 97 | free(entry_path); 98 | exfat_put_node(ef, parent); 99 | return; 100 | } 101 | while ((node = exfat_readdir(&it))) 102 | { 103 | exfat_get_name(node, entry_path + path_length + 1); 104 | exfat_debug("%s: %s, %"PRIu64" bytes, cluster %u", entry_path, 105 | node->is_contiguous ? "contiguous" : "fragmented", 106 | node->size, node->start_cluster); 107 | if (node->attrib & EXFAT_ATTRIB_DIR) 108 | { 109 | directories_count++; 110 | dirck(ef, entry_path); 111 | } 112 | else 113 | { 114 | files_count++; 115 | nodeck(ef, node); 116 | } 117 | exfat_flush_node(ef, node); 118 | exfat_put_node(ef, node); 119 | } 120 | exfat_closedir(ef, &it); 121 | exfat_flush_node(ef, parent); 122 | exfat_put_node(ef, parent); 123 | free(entry_path); 124 | } 125 | 126 | static void fsck(struct exfat* ef, const char* spec, const char* options) 127 | { 128 | if (exfat_mount(ef, spec, options) != 0) 129 | { 130 | fputs("File system checking stopped. ", stdout); 131 | return; 132 | } 133 | 134 | exfat_print_info(ef->sb, exfat_count_free_clusters(ef)); 135 | exfat_soil_super_block(ef); 136 | dirck(ef, ""); 137 | exfat_unmount(ef); 138 | 139 | printf("Totally %"PRIu64" directories and %"PRIu64" files.\n", 140 | directories_count, files_count); 141 | fputs("File system checking finished. ", stdout); 142 | } 143 | 144 | static void usage(const char* prog) 145 | { 146 | fprintf(stderr, "Usage: %s [-a | -n | -p | -y] \n", prog); 147 | fprintf(stderr, " %s -V\n", prog); 148 | exit(1); 149 | } 150 | 151 | int main(int argc, char* argv[]) 152 | { 153 | int opt; 154 | const char* options; 155 | const char* spec = NULL; 156 | struct exfat ef; 157 | 158 | printf("exfatfsck %s\n", VERSION); 159 | 160 | if (isatty(STDIN_FILENO)) 161 | options = "repair=1"; 162 | else 163 | options = "repair=0"; 164 | 165 | while ((opt = getopt(argc, argv, "anpVy")) != -1) 166 | { 167 | switch (opt) 168 | { 169 | case 'a': 170 | case 'p': 171 | case 'y': 172 | options = "repair=2"; 173 | break; 174 | case 'n': 175 | options = "repair=0,ro"; 176 | break; 177 | case 'V': 178 | puts("Copyright (C) 2011-2018 Andrew Nayenko"); 179 | return 0; 180 | default: 181 | usage(argv[0]); 182 | break; 183 | } 184 | } 185 | if (argc - optind != 1) 186 | usage(argv[0]); 187 | spec = argv[optind]; 188 | 189 | printf("Checking file system on %s.\n", spec); 190 | fsck(&ef, spec, options); 191 | if (exfat_errors != 0) 192 | { 193 | printf("ERRORS FOUND: %d, FIXED: %d.\n", 194 | exfat_errors, exfat_errors_fixed); 195 | return 1; 196 | } 197 | puts("No errors found."); 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /fuse/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | sbin_PROGRAMS = mount.exfat-fuse 24 | dist_man8_MANS = mount.exfat-fuse.8 25 | mount_exfat_fuse_SOURCES = main.c 26 | mount_exfat_fuse_CPPFLAGS = -I$(top_srcdir)/libexfat 27 | mount_exfat_fuse_CFLAGS = $(FUSE_CFLAGS) 28 | mount_exfat_fuse_LDADD = ../libexfat/libexfat.a $(FUSE_LIBS) 29 | 30 | install-exec-hook: 31 | ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/mount.exfat 32 | 33 | uninstall-hook: 34 | rm -f $(DESTDIR)$(sbindir)/mount.exfat 35 | -------------------------------------------------------------------------------- /fuse/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (01.09.09) 3 | FUSE-based exFAT implementation. Requires FUSE 2.6 or later. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include 24 | #define FUSE_USE_VERSION 26 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #ifndef DEBUG 38 | #define exfat_debug(format, ...) 39 | #endif 40 | 41 | #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) 42 | #error FUSE 2.6 or later is required 43 | #endif 44 | 45 | struct exfat ef; 46 | 47 | static struct exfat_node* get_node(const struct fuse_file_info* fi) 48 | { 49 | return (struct exfat_node*) (size_t) fi->fh; 50 | } 51 | 52 | static void set_node(struct fuse_file_info* fi, struct exfat_node* node) 53 | { 54 | fi->fh = (uint64_t) (size_t) node; 55 | fi->keep_cache = 1; 56 | } 57 | 58 | static int fuse_exfat_getattr(const char* path, struct stat* stbuf) 59 | { 60 | struct exfat_node* node; 61 | int rc; 62 | 63 | exfat_debug("[%s] %s", __func__, path); 64 | 65 | rc = exfat_lookup(&ef, &node, path); 66 | if (rc != 0) 67 | return rc; 68 | 69 | exfat_stat(&ef, node, stbuf); 70 | exfat_put_node(&ef, node); 71 | return 0; 72 | } 73 | 74 | static int fuse_exfat_truncate(const char* path, off_t size) 75 | { 76 | struct exfat_node* node; 77 | int rc; 78 | 79 | exfat_debug("[%s] %s, %"PRId64, __func__, path, size); 80 | 81 | rc = exfat_lookup(&ef, &node, path); 82 | if (rc != 0) 83 | return rc; 84 | 85 | rc = exfat_truncate(&ef, node, size, true); 86 | if (rc != 0) 87 | { 88 | exfat_flush_node(&ef, node); /* ignore return code */ 89 | exfat_put_node(&ef, node); 90 | return rc; 91 | } 92 | rc = exfat_flush_node(&ef, node); 93 | exfat_put_node(&ef, node); 94 | return rc; 95 | } 96 | 97 | static int fuse_exfat_readdir(const char* path, void* buffer, 98 | fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi) 99 | { 100 | struct exfat_node* parent; 101 | struct exfat_node* node; 102 | struct exfat_iterator it; 103 | int rc; 104 | char name[EXFAT_UTF8_NAME_BUFFER_MAX]; 105 | struct stat stbuf; 106 | 107 | exfat_debug("[%s] %s", __func__, path); 108 | 109 | rc = exfat_lookup(&ef, &parent, path); 110 | if (rc != 0) 111 | return rc; 112 | if (!(parent->attrib & EXFAT_ATTRIB_DIR)) 113 | { 114 | exfat_put_node(&ef, parent); 115 | exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib); 116 | return -ENOTDIR; 117 | } 118 | 119 | filler(buffer, ".", NULL, 0); 120 | filler(buffer, "..", NULL, 0); 121 | 122 | rc = exfat_opendir(&ef, parent, &it); 123 | if (rc != 0) 124 | { 125 | exfat_put_node(&ef, parent); 126 | exfat_error("failed to open directory '%s'", path); 127 | return rc; 128 | } 129 | while ((node = exfat_readdir(&it))) 130 | { 131 | exfat_get_name(node, name); 132 | exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__, 133 | name, node->is_contiguous ? "contiguous" : "fragmented", 134 | node->size, node->start_cluster); 135 | exfat_stat(&ef, node, &stbuf); 136 | filler(buffer, name, &stbuf, 0); 137 | exfat_put_node(&ef, node); 138 | } 139 | exfat_closedir(&ef, &it); 140 | exfat_put_node(&ef, parent); 141 | return 0; 142 | } 143 | 144 | static int fuse_exfat_open(const char* path, struct fuse_file_info* fi) 145 | { 146 | struct exfat_node* node; 147 | int rc; 148 | 149 | exfat_debug("[%s] %s", __func__, path); 150 | 151 | rc = exfat_lookup(&ef, &node, path); 152 | if (rc != 0) 153 | return rc; 154 | set_node(fi, node); 155 | return 0; 156 | } 157 | 158 | static int fuse_exfat_create(const char* path, mode_t mode, 159 | struct fuse_file_info* fi) 160 | { 161 | struct exfat_node* node; 162 | int rc; 163 | 164 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 165 | 166 | rc = exfat_mknod(&ef, path); 167 | if (rc != 0) 168 | return rc; 169 | rc = exfat_lookup(&ef, &node, path); 170 | if (rc != 0) 171 | return rc; 172 | set_node(fi, node); 173 | return 0; 174 | } 175 | 176 | static int fuse_exfat_release(const char* path, struct fuse_file_info* fi) 177 | { 178 | /* 179 | This handler is called by FUSE on close() syscall. If the FUSE 180 | implementation does not call flush handler, we will flush node here. 181 | But in this case we will not be able to return an error to the caller. 182 | See fuse_exfat_flush() below. 183 | */ 184 | exfat_debug("[%s] %s", __func__, path); 185 | exfat_flush_node(&ef, get_node(fi)); 186 | exfat_put_node(&ef, get_node(fi)); 187 | return 0; /* FUSE ignores this return value */ 188 | } 189 | 190 | static int fuse_exfat_flush(const char* path, struct fuse_file_info* fi) 191 | { 192 | /* 193 | This handler may be called by FUSE on close() syscall. FUSE also deals 194 | with removals of open files, so we don't free clusters on close but 195 | only on rmdir and unlink. If the FUSE implementation does not call this 196 | handler we will flush node on release. See fuse_exfat_relase() above. 197 | */ 198 | exfat_debug("[%s] %s", __func__, path); 199 | return exfat_flush_node(&ef, get_node(fi)); 200 | } 201 | 202 | static int fuse_exfat_fsync(const char* path, int datasync, 203 | struct fuse_file_info *fi) 204 | { 205 | int rc; 206 | 207 | exfat_debug("[%s] %s", __func__, path); 208 | rc = exfat_flush_nodes(&ef); 209 | if (rc != 0) 210 | return rc; 211 | rc = exfat_flush(&ef); 212 | if (rc != 0) 213 | return rc; 214 | return exfat_fsync(ef.dev); 215 | } 216 | 217 | static int fuse_exfat_read(const char* path, char* buffer, size_t size, 218 | off_t offset, struct fuse_file_info* fi) 219 | { 220 | exfat_debug("[%s] %s (%zu bytes)", __func__, path, size); 221 | return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset); 222 | } 223 | 224 | static int fuse_exfat_write(const char* path, const char* buffer, size_t size, 225 | off_t offset, struct fuse_file_info* fi) 226 | { 227 | exfat_debug("[%s] %s (%zu bytes)", __func__, path, size); 228 | return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset); 229 | } 230 | 231 | static int fuse_exfat_unlink(const char* path) 232 | { 233 | struct exfat_node* node; 234 | int rc; 235 | 236 | exfat_debug("[%s] %s", __func__, path); 237 | 238 | rc = exfat_lookup(&ef, &node, path); 239 | if (rc != 0) 240 | return rc; 241 | 242 | rc = exfat_unlink(&ef, node); 243 | exfat_put_node(&ef, node); 244 | if (rc != 0) 245 | return rc; 246 | return exfat_cleanup_node(&ef, node); 247 | } 248 | 249 | static int fuse_exfat_rmdir(const char* path) 250 | { 251 | struct exfat_node* node; 252 | int rc; 253 | 254 | exfat_debug("[%s] %s", __func__, path); 255 | 256 | rc = exfat_lookup(&ef, &node, path); 257 | if (rc != 0) 258 | return rc; 259 | 260 | rc = exfat_rmdir(&ef, node); 261 | exfat_put_node(&ef, node); 262 | if (rc != 0) 263 | return rc; 264 | return exfat_cleanup_node(&ef, node); 265 | } 266 | 267 | static int fuse_exfat_mknod(const char* path, mode_t mode, dev_t dev) 268 | { 269 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 270 | return exfat_mknod(&ef, path); 271 | } 272 | 273 | static int fuse_exfat_mkdir(const char* path, mode_t mode) 274 | { 275 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 276 | return exfat_mkdir(&ef, path); 277 | } 278 | 279 | static int fuse_exfat_rename(const char* old_path, const char* new_path) 280 | { 281 | exfat_debug("[%s] %s => %s", __func__, old_path, new_path); 282 | return exfat_rename(&ef, old_path, new_path); 283 | } 284 | 285 | static int fuse_exfat_utimens(const char* path, const struct timespec tv[2]) 286 | { 287 | struct exfat_node* node; 288 | int rc; 289 | 290 | exfat_debug("[%s] %s", __func__, path); 291 | 292 | rc = exfat_lookup(&ef, &node, path); 293 | if (rc != 0) 294 | return rc; 295 | 296 | exfat_utimes(node, tv); 297 | rc = exfat_flush_node(&ef, node); 298 | exfat_put_node(&ef, node); 299 | return rc; 300 | } 301 | 302 | static int fuse_exfat_chmod(const char* path, mode_t mode) 303 | { 304 | const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR | 305 | S_IRWXU | S_IRWXG | S_IRWXO; 306 | 307 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 308 | if (mode & ~VALID_MODE_MASK) 309 | return -EPERM; 310 | return 0; 311 | } 312 | 313 | static int fuse_exfat_chown(const char* path, uid_t uid, gid_t gid) 314 | { 315 | exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid); 316 | if (uid != ef.uid || gid != ef.gid) 317 | return -EPERM; 318 | return 0; 319 | } 320 | 321 | static int fuse_exfat_statfs(const char* path, struct statvfs* sfs) 322 | { 323 | exfat_debug("[%s]", __func__); 324 | 325 | sfs->f_bsize = CLUSTER_SIZE(*ef.sb); 326 | sfs->f_frsize = CLUSTER_SIZE(*ef.sb); 327 | sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits; 328 | sfs->f_bavail = exfat_count_free_clusters(&ef); 329 | sfs->f_bfree = sfs->f_bavail; 330 | sfs->f_namemax = EXFAT_NAME_MAX; 331 | 332 | /* 333 | Below are fake values because in exFAT there is 334 | a) no simple way to count files; 335 | b) no such thing as inode; 336 | So here we assume that inode = cluster. 337 | */ 338 | sfs->f_files = le32_to_cpu(ef.sb->cluster_count); 339 | sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits; 340 | sfs->f_ffree = sfs->f_bavail; 341 | 342 | return 0; 343 | } 344 | 345 | static void* fuse_exfat_init(struct fuse_conn_info* fci) 346 | { 347 | exfat_debug("[%s]", __func__); 348 | #ifdef FUSE_CAP_BIG_WRITES 349 | fci->want |= FUSE_CAP_BIG_WRITES; 350 | #endif 351 | 352 | /* mark super block as dirty; failure isn't a big deal */ 353 | exfat_soil_super_block(&ef); 354 | 355 | return NULL; 356 | } 357 | 358 | static void fuse_exfat_destroy(void* unused) 359 | { 360 | exfat_debug("[%s]", __func__); 361 | exfat_unmount(&ef); 362 | } 363 | 364 | static void usage(const char* prog) 365 | { 366 | fprintf(stderr, "Usage: %s [-d] [-o options] [-V] \n", prog); 367 | exit(1); 368 | } 369 | 370 | static struct fuse_operations fuse_exfat_ops = 371 | { 372 | .getattr = fuse_exfat_getattr, 373 | .truncate = fuse_exfat_truncate, 374 | .readdir = fuse_exfat_readdir, 375 | .open = fuse_exfat_open, 376 | .create = fuse_exfat_create, 377 | .release = fuse_exfat_release, 378 | .flush = fuse_exfat_flush, 379 | .fsync = fuse_exfat_fsync, 380 | .fsyncdir = fuse_exfat_fsync, 381 | .read = fuse_exfat_read, 382 | .write = fuse_exfat_write, 383 | .unlink = fuse_exfat_unlink, 384 | .rmdir = fuse_exfat_rmdir, 385 | .mknod = fuse_exfat_mknod, 386 | .mkdir = fuse_exfat_mkdir, 387 | .rename = fuse_exfat_rename, 388 | .utimens = fuse_exfat_utimens, 389 | .chmod = fuse_exfat_chmod, 390 | .chown = fuse_exfat_chown, 391 | .statfs = fuse_exfat_statfs, 392 | .init = fuse_exfat_init, 393 | .destroy = fuse_exfat_destroy, 394 | }; 395 | 396 | static char* add_option(char* options, const char* name, const char* value) 397 | { 398 | size_t size; 399 | char* optionsf = options; 400 | 401 | if (value) 402 | size = strlen(options) + strlen(name) + strlen(value) + 3; 403 | else 404 | size = strlen(options) + strlen(name) + 2; 405 | 406 | options = realloc(options, size); 407 | if (options == NULL) 408 | { 409 | free(optionsf); 410 | exfat_error("failed to reallocate options string"); 411 | return NULL; 412 | } 413 | strcat(options, ","); 414 | strcat(options, name); 415 | if (value) 416 | { 417 | strcat(options, "="); 418 | strcat(options, value); 419 | } 420 | return options; 421 | } 422 | 423 | static void escape(char* escaped, const char* orig) 424 | { 425 | do 426 | { 427 | if (*orig == ',' || *orig == '\\') 428 | *escaped++ = '\\'; 429 | } 430 | while ((*escaped++ = *orig++)); 431 | } 432 | 433 | static char* add_fsname_option(char* options, const char* spec) 434 | { 435 | /* escaped string cannot be more than twice as big as the original one */ 436 | char* escaped = malloc(strlen(spec) * 2 + 1); 437 | 438 | if (escaped == NULL) 439 | { 440 | free(options); 441 | exfat_error("failed to allocate escaped string for %s", spec); 442 | return NULL; 443 | } 444 | 445 | /* on some platforms (e.g. Android, Solaris) device names can contain 446 | commas */ 447 | escape(escaped, spec); 448 | options = add_option(options, "fsname", escaped); 449 | free(escaped); 450 | return options; 451 | } 452 | 453 | static char* add_ro_option(char* options, bool ro) 454 | { 455 | return ro ? add_option(options, "ro", NULL) : options; 456 | } 457 | 458 | #if defined(__linux__) || defined(__FreeBSD__) 459 | static char* add_user_option(char* options) 460 | { 461 | struct passwd* pw; 462 | 463 | if (getuid() == 0) 464 | return options; 465 | 466 | pw = getpwuid(getuid()); 467 | if (pw == NULL || pw->pw_name == NULL) 468 | { 469 | free(options); 470 | exfat_error("failed to determine username"); 471 | return NULL; 472 | } 473 | return add_option(options, "user", pw->pw_name); 474 | } 475 | #endif 476 | 477 | #if defined(__linux__) 478 | static char* add_blksize_option(char* options, long cluster_size) 479 | { 480 | long page_size = sysconf(_SC_PAGESIZE); 481 | char blksize[20]; 482 | 483 | if (page_size < 1) 484 | page_size = 0x1000; 485 | 486 | snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size)); 487 | return add_option(options, "blksize", blksize); 488 | } 489 | #endif 490 | 491 | static char* add_fuse_options(char* options, const char* spec, bool ro) 492 | { 493 | options = add_fsname_option(options, spec); 494 | if (options == NULL) 495 | return NULL; 496 | options = add_ro_option(options, ro); 497 | if (options == NULL) 498 | return NULL; 499 | #if defined(__linux__) || defined(__FreeBSD__) 500 | options = add_user_option(options); 501 | if (options == NULL) 502 | return NULL; 503 | #endif 504 | #if defined(__linux__) 505 | options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb)); 506 | if (options == NULL) 507 | return NULL; 508 | #endif 509 | return options; 510 | } 511 | 512 | static int fuse_exfat_main(char* mount_options, char* mount_point) 513 | { 514 | char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL}; 515 | return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv, 516 | &fuse_exfat_ops, NULL); 517 | } 518 | 519 | int main(int argc, char* argv[]) 520 | { 521 | const char* spec = NULL; 522 | char* mount_point = NULL; 523 | char* fuse_options; 524 | char* exfat_options; 525 | int opt; 526 | int rc; 527 | 528 | printf("FUSE exfat %s\n", VERSION); 529 | 530 | fuse_options = strdup("allow_other," 531 | #if defined(__linux__) || defined(__FreeBSD__) 532 | "big_writes," 533 | #endif 534 | #if defined(__linux__) 535 | "blkdev," 536 | #endif 537 | "default_permissions"); 538 | exfat_options = strdup("ro_fallback"); 539 | if (fuse_options == NULL || exfat_options == NULL) 540 | { 541 | exfat_error("failed to allocate options string"); 542 | return 1; 543 | } 544 | 545 | while ((opt = getopt(argc, argv, "dno:Vv")) != -1) 546 | { 547 | switch (opt) 548 | { 549 | case 'd': 550 | fuse_options = add_option(fuse_options, "debug", NULL); 551 | if (fuse_options == NULL) 552 | { 553 | free(exfat_options); 554 | return 1; 555 | } 556 | break; 557 | case 'n': 558 | break; 559 | case 'o': 560 | exfat_options = add_option(exfat_options, optarg, NULL); 561 | if (exfat_options == NULL) 562 | { 563 | free(fuse_options); 564 | return 1; 565 | } 566 | break; 567 | case 'V': 568 | free(exfat_options); 569 | free(fuse_options); 570 | puts("Copyright (C) 2010-2018 Andrew Nayenko"); 571 | return 0; 572 | case 'v': 573 | break; 574 | default: 575 | free(exfat_options); 576 | free(fuse_options); 577 | usage(argv[0]); 578 | break; 579 | } 580 | } 581 | if (argc - optind != 2) 582 | { 583 | free(exfat_options); 584 | free(fuse_options); 585 | usage(argv[0]); 586 | } 587 | spec = argv[optind]; 588 | mount_point = argv[optind + 1]; 589 | 590 | if (exfat_mount(&ef, spec, exfat_options) != 0) 591 | { 592 | free(exfat_options); 593 | free(fuse_options); 594 | return 1; 595 | } 596 | 597 | free(exfat_options); 598 | 599 | fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0); 600 | if (fuse_options == NULL) 601 | { 602 | exfat_unmount(&ef); 603 | return 1; 604 | } 605 | 606 | /* let FUSE do all its wizardry */ 607 | rc = fuse_exfat_main(fuse_options, mount_point); 608 | 609 | free(fuse_options); 610 | return rc; 611 | } 612 | -------------------------------------------------------------------------------- /fuse/mount.exfat-fuse.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2010-2016 Andrew Nayenko 2 | .\" 3 | .TH EXFAT-FUSE 8 "November 2015" 4 | .SH NAME 5 | mount.exfat-fuse \- mount an exFAT file system 6 | .SH SYNOPSIS 7 | .B mount.exfat-fuse 8 | [ 9 | .B \-d 10 | ] 11 | [ 12 | .B \-n 13 | ] 14 | [ 15 | .B \-o 16 | .I options 17 | ] 18 | [ 19 | .B \-V 20 | ] 21 | [ 22 | .B \-v 23 | ] 24 | .I device dir 25 | 26 | .SH DESCRIPTION 27 | .B mount.exfat-fuse 28 | is a free exFAT file system implementation with write support. exFAT is a 29 | simple file system created by Microsoft. It is intended to replace FAT32 30 | removing some of its limitations. exFAT is a standard FS for SDXC memory 31 | cards. 32 | 33 | .SH COMMAND LINE OPTIONS 34 | Command line options available: 35 | .TP 36 | .BI \-d 37 | Enable debug logging and do not detach from shell. 38 | .TP 39 | .BI \-n 40 | Ignored. 41 | .TP 42 | .BI \-o " options" 43 | File system specific options. For more details see 44 | .B FILE SYSTEM OPTIONS 45 | section below. 46 | .TP 47 | .BI \-V 48 | Print version and copyright. 49 | .TP 50 | .BI \-v 51 | Ignored. 52 | 53 | .SH FILE SYSTEM OPTIONS 54 | .TP 55 | .BI umask= value 56 | Set the umask (the bitmask of the permissions that are 57 | .B not 58 | present, in octal). 59 | The default is 0. 60 | .TP 61 | .BI dmask= value 62 | Set the umask for directories only. 63 | .TP 64 | .BI fmask= value 65 | Set the umask for files only. 66 | .TP 67 | .BI uid= n 68 | Set the owner for all files and directories. 69 | The default is the owner of the current process. 70 | .TP 71 | .BI gid= n 72 | Set the group for all files and directories. 73 | The default is the group of the current process. 74 | .TP 75 | .BI ro 76 | Mount the file system in read only mode. 77 | .TP 78 | .BI noatime 79 | Do not update access time when file is read. 80 | 81 | .SH EXIT CODES 82 | Zero is returned on successful mount. Any other code means an error. 83 | 84 | .SH BUGS 85 | exFAT is a case-insensitive file system. Some things can behave unexpectedly, 86 | e.g. directory renaming that changes only case of some characters: 87 | 88 | .B \t$ mv FOO Foo 89 | .br 90 | .B \tmv: cannot move \(cqFOO\(cq to a subdirectory of itself, \(cqFoo/FOO\(cq 91 | 92 | This happens because 93 | .B mv 94 | finds that destination exists (for case-insensitive file 95 | systems 96 | .B FOO 97 | and 98 | .B Foo 99 | are the same thing) and adds source basename to the destination. The file 100 | system gets 101 | .B rename(\(dqFOO\(dq,\ \(dqFoo/FOO\(dq) 102 | syscall and returns an error. 103 | 104 | .SH AUTHOR 105 | Andrew Nayenko 106 | 107 | .SH SEE ALSO 108 | .BR mount (8) 109 | -------------------------------------------------------------------------------- /label/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | sbin_PROGRAMS = exfatlabel 24 | dist_man8_MANS = exfatlabel.8 25 | exfatlabel_SOURCES = main.c 26 | exfatlabel_CPPFLAGS = -I$(top_srcdir)/libexfat 27 | exfatlabel_LDADD = ../libexfat/libexfat.a 28 | -------------------------------------------------------------------------------- /label/exfatlabel.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2018 Andrew Nayenko 2 | .\" 3 | .TH EXFATLABEL 8 "September 2017" 4 | .SH NAME 5 | .B exfatlabel 6 | \- get or set an exFAT file system label 7 | .SH SYNOPSIS 8 | .B exfatlabel 9 | [ 10 | .B \-V 11 | ] 12 | .I device 13 | [ 14 | .I label 15 | ] 16 | 17 | .SH DESCRIPTION 18 | .B exfatlabel 19 | reads or changes an exFAT file system label (volume name). 20 | 21 | If 22 | .I label 23 | argument is present, 24 | .B exfatlabel 25 | sets the new volume name. Empty label ('') removes volume name. Label can be 26 | up to 15 characters. This limit is shorter if characters beyond Unicode BMP 27 | are used because internally label is stored in UTF-16. 28 | 29 | If 30 | .I label 31 | argument is omitted, 32 | .B exfatlabel 33 | just prints current volume name. 34 | 35 | .SH COMMAND LINE OPTIONS 36 | Command line options available: 37 | .TP 38 | .BI \-V 39 | Print version and copyright. 40 | 41 | .SH EXIT CODES 42 | Zero is returned on success. Any other code means an error. 43 | 44 | .SH AUTHOR 45 | Andrew Nayenko 46 | 47 | .SH SEE ALSO 48 | .BR mkexfatfs (8) 49 | -------------------------------------------------------------------------------- /label/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (20.01.11) 3 | Prints or changes exFAT volume label. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | int main(int argc, char* argv[]) 28 | { 29 | char** pp; 30 | struct exfat ef; 31 | int rc = 0; 32 | 33 | for (pp = argv + 1; *pp; pp++) 34 | if (strcmp(*pp, "-V") == 0) 35 | { 36 | printf("exfatlabel %s\n", VERSION); 37 | puts("Copyright (C) 2011-2018 Andrew Nayenko"); 38 | return 0; 39 | } 40 | 41 | if (argc != 2 && argc != 3) 42 | { 43 | fprintf(stderr, "Usage: %s [-V] [label]\n", argv[0]); 44 | return 1; 45 | } 46 | 47 | if (argv[2]) 48 | { 49 | if (exfat_mount(&ef, argv[1], "") != 0) 50 | return 1; 51 | rc = (exfat_set_label(&ef, argv[2]) != 0); 52 | } 53 | else 54 | { 55 | if (exfat_mount(&ef, argv[1], "ro") != 0) 56 | return 1; 57 | puts(exfat_get_label(&ef)); 58 | } 59 | 60 | exfat_unmount(&ef); 61 | return rc; 62 | } 63 | -------------------------------------------------------------------------------- /libexfat/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | noinst_LIBRARIES = libexfat.a 24 | libexfat_a_SOURCES = \ 25 | byteorder.h \ 26 | cluster.c \ 27 | compiler.h \ 28 | exfat.h \ 29 | exfatfs.h \ 30 | io.c \ 31 | log.c \ 32 | lookup.c \ 33 | mount.c \ 34 | node.c \ 35 | platform.h \ 36 | repair.c \ 37 | time.c \ 38 | utf.c \ 39 | utils.c 40 | -------------------------------------------------------------------------------- /libexfat/byteorder.h: -------------------------------------------------------------------------------- 1 | /* 2 | byteorder.h (12.01.10) 3 | Endianness stuff. exFAT uses little-endian byte order. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef BYTEORDER_H_INCLUDED 24 | #define BYTEORDER_H_INCLUDED 25 | 26 | #include "platform.h" 27 | #include 28 | #include 29 | 30 | typedef struct { uint16_t __u16; } le16_t; 31 | typedef struct { uint32_t __u32; } le32_t; 32 | typedef struct { uint64_t __u64; } le64_t; 33 | 34 | #if EXFAT_BYTE_ORDER == EXFAT_LITTLE_ENDIAN 35 | 36 | static inline uint16_t le16_to_cpu(le16_t v) { return v.__u16; } 37 | static inline uint32_t le32_to_cpu(le32_t v) { return v.__u32; } 38 | static inline uint64_t le64_to_cpu(le64_t v) { return v.__u64; } 39 | 40 | static inline le16_t cpu_to_le16(uint16_t v) { le16_t t = {v}; return t; } 41 | static inline le32_t cpu_to_le32(uint32_t v) { le32_t t = {v}; return t; } 42 | static inline le64_t cpu_to_le64(uint64_t v) { le64_t t = {v}; return t; } 43 | 44 | typedef size_t bitmap_t; 45 | 46 | #elif EXFAT_BYTE_ORDER == EXFAT_BIG_ENDIAN 47 | 48 | static inline uint16_t le16_to_cpu(le16_t v) 49 | { return exfat_bswap16(v.__u16); } 50 | static inline uint32_t le32_to_cpu(le32_t v) 51 | { return exfat_bswap32(v.__u32); } 52 | static inline uint64_t le64_to_cpu(le64_t v) 53 | { return exfat_bswap64(v.__u64); } 54 | 55 | static inline le16_t cpu_to_le16(uint16_t v) 56 | { le16_t t = {exfat_bswap16(v)}; return t; } 57 | static inline le32_t cpu_to_le32(uint32_t v) 58 | { le32_t t = {exfat_bswap32(v)}; return t; } 59 | static inline le64_t cpu_to_le64(uint64_t v) 60 | { le64_t t = {exfat_bswap64(v)}; return t; } 61 | 62 | typedef unsigned char bitmap_t; 63 | 64 | #else 65 | #error Wow! You have a PDP machine?! 66 | #endif 67 | 68 | #endif /* ifndef BYTEORDER_H_INCLUDED */ 69 | -------------------------------------------------------------------------------- /libexfat/cluster.c: -------------------------------------------------------------------------------- 1 | /* 2 | cluster.c (03.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | #include 26 | #include 27 | 28 | /* 29 | * Sector to absolute offset. 30 | */ 31 | static off_t s2o(const struct exfat* ef, off_t sector) 32 | { 33 | return sector << ef->sb->sector_bits; 34 | } 35 | 36 | /* 37 | * Cluster to sector. 38 | */ 39 | static off_t c2s(const struct exfat* ef, cluster_t cluster) 40 | { 41 | if (cluster < EXFAT_FIRST_DATA_CLUSTER) 42 | exfat_bug("invalid cluster number %u", cluster); 43 | return le32_to_cpu(ef->sb->cluster_sector_start) + 44 | ((off_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) << ef->sb->spc_bits); 45 | } 46 | 47 | /* 48 | * Cluster to absolute offset. 49 | */ 50 | off_t exfat_c2o(const struct exfat* ef, cluster_t cluster) 51 | { 52 | return s2o(ef, c2s(ef, cluster)); 53 | } 54 | 55 | /* 56 | * Sector to cluster. 57 | */ 58 | static cluster_t s2c(const struct exfat* ef, off_t sector) 59 | { 60 | return ((sector - le32_to_cpu(ef->sb->cluster_sector_start)) >> 61 | ef->sb->spc_bits) + EXFAT_FIRST_DATA_CLUSTER; 62 | } 63 | 64 | /* 65 | * Size in bytes to size in clusters (rounded upwards). 66 | */ 67 | static uint32_t bytes2clusters(const struct exfat* ef, uint64_t bytes) 68 | { 69 | uint64_t cluster_size = CLUSTER_SIZE(*ef->sb); 70 | return DIV_ROUND_UP(bytes, cluster_size); 71 | } 72 | 73 | cluster_t exfat_next_cluster(const struct exfat* ef, 74 | const struct exfat_node* node, cluster_t cluster) 75 | { 76 | le32_t next; 77 | off_t fat_offset; 78 | 79 | if (cluster < EXFAT_FIRST_DATA_CLUSTER) 80 | exfat_bug("bad cluster 0x%x", cluster); 81 | 82 | if (node->is_contiguous) 83 | return cluster + 1; 84 | fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start)) 85 | + cluster * sizeof(cluster_t); 86 | if (exfat_pread(ef->dev, &next, sizeof(next), fat_offset) < 0) 87 | return EXFAT_CLUSTER_BAD; /* the caller should handle this and print 88 | appropriate error message */ 89 | return le32_to_cpu(next); 90 | } 91 | 92 | cluster_t exfat_advance_cluster(const struct exfat* ef, 93 | struct exfat_node* node, uint32_t count) 94 | { 95 | uint32_t i; 96 | 97 | if (node->fptr_index > count) 98 | { 99 | node->fptr_index = 0; 100 | node->fptr_cluster = node->start_cluster; 101 | } 102 | 103 | for (i = node->fptr_index; i < count; i++) 104 | { 105 | node->fptr_cluster = exfat_next_cluster(ef, node, node->fptr_cluster); 106 | if (CLUSTER_INVALID(*ef->sb, node->fptr_cluster)) 107 | break; /* the caller should handle this and print appropriate 108 | error message */ 109 | } 110 | node->fptr_index = count; 111 | return node->fptr_cluster; 112 | } 113 | 114 | static cluster_t find_bit_and_set(bitmap_t* bitmap, size_t start, size_t end) 115 | { 116 | const size_t start_index = start / sizeof(bitmap_t) / 8; 117 | const size_t end_index = DIV_ROUND_UP(end, sizeof(bitmap_t) * 8); 118 | size_t i; 119 | size_t start_bitindex; 120 | size_t end_bitindex; 121 | size_t c; 122 | 123 | for (i = start_index; i < end_index; i++) 124 | { 125 | if (bitmap[i] == ~((bitmap_t) 0)) 126 | continue; 127 | start_bitindex = MAX(i * sizeof(bitmap_t) * 8, start); 128 | end_bitindex = MIN((i + 1) * sizeof(bitmap_t) * 8, end); 129 | for (c = start_bitindex; c < end_bitindex; c++) 130 | if (BMAP_GET(bitmap, c) == 0) 131 | { 132 | BMAP_SET(bitmap, c); 133 | return c + EXFAT_FIRST_DATA_CLUSTER; 134 | } 135 | } 136 | return EXFAT_CLUSTER_END; 137 | } 138 | 139 | static int flush_nodes(struct exfat* ef, struct exfat_node* node) 140 | { 141 | struct exfat_node* p; 142 | 143 | for (p = node->child; p != NULL; p = p->next) 144 | { 145 | int rc = flush_nodes(ef, p); 146 | if (rc != 0) 147 | return rc; 148 | } 149 | return exfat_flush_node(ef, node); 150 | } 151 | 152 | int exfat_flush_nodes(struct exfat* ef) 153 | { 154 | return flush_nodes(ef, ef->root); 155 | } 156 | 157 | int exfat_flush(struct exfat* ef) 158 | { 159 | if (ef->cmap.dirty) 160 | { 161 | if (exfat_pwrite(ef->dev, ef->cmap.chunk, 162 | BMAP_SIZE(ef->cmap.chunk_size), 163 | exfat_c2o(ef, ef->cmap.start_cluster)) < 0) 164 | { 165 | exfat_error("failed to write clusters bitmap"); 166 | return -EIO; 167 | } 168 | ef->cmap.dirty = false; 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | static bool set_next_cluster(const struct exfat* ef, bool contiguous, 175 | cluster_t current, cluster_t next) 176 | { 177 | off_t fat_offset; 178 | le32_t next_le32; 179 | 180 | if (contiguous) 181 | return true; 182 | fat_offset = s2o(ef, le32_to_cpu(ef->sb->fat_sector_start)) 183 | + current * sizeof(cluster_t); 184 | next_le32 = cpu_to_le32(next); 185 | if (exfat_pwrite(ef->dev, &next_le32, sizeof(next_le32), fat_offset) < 0) 186 | { 187 | exfat_error("failed to write the next cluster %#x after %#x", next, 188 | current); 189 | return false; 190 | } 191 | return true; 192 | } 193 | 194 | static cluster_t allocate_cluster(struct exfat* ef, cluster_t hint) 195 | { 196 | cluster_t cluster; 197 | 198 | hint -= EXFAT_FIRST_DATA_CLUSTER; 199 | if (hint >= ef->cmap.chunk_size) 200 | hint = 0; 201 | 202 | cluster = find_bit_and_set(ef->cmap.chunk, hint, ef->cmap.chunk_size); 203 | if (cluster == EXFAT_CLUSTER_END) 204 | cluster = find_bit_and_set(ef->cmap.chunk, 0, hint); 205 | if (cluster == EXFAT_CLUSTER_END) 206 | { 207 | exfat_error("no free space left"); 208 | return EXFAT_CLUSTER_END; 209 | } 210 | 211 | ef->cmap.dirty = true; 212 | return cluster; 213 | } 214 | 215 | static void free_cluster(struct exfat* ef, cluster_t cluster) 216 | { 217 | if (cluster - EXFAT_FIRST_DATA_CLUSTER >= ef->cmap.size) 218 | exfat_bug("caller must check cluster validity (%#x, %#x)", cluster, 219 | ef->cmap.size); 220 | 221 | BMAP_CLR(ef->cmap.chunk, cluster - EXFAT_FIRST_DATA_CLUSTER); 222 | ef->cmap.dirty = true; 223 | } 224 | 225 | static bool make_noncontiguous(const struct exfat* ef, cluster_t first, 226 | cluster_t last) 227 | { 228 | cluster_t c; 229 | 230 | for (c = first; c < last; c++) 231 | if (!set_next_cluster(ef, false, c, c + 1)) 232 | return false; 233 | return true; 234 | } 235 | 236 | static int shrink_file(struct exfat* ef, struct exfat_node* node, 237 | uint32_t current, uint32_t difference); 238 | 239 | static int grow_file(struct exfat* ef, struct exfat_node* node, 240 | uint32_t current, uint32_t difference) 241 | { 242 | cluster_t previous; 243 | cluster_t next; 244 | uint32_t allocated = 0; 245 | 246 | if (difference == 0) 247 | exfat_bug("zero clusters count passed"); 248 | 249 | if (node->start_cluster != EXFAT_CLUSTER_FREE) 250 | { 251 | /* get the last cluster of the file */ 252 | previous = exfat_advance_cluster(ef, node, current - 1); 253 | if (CLUSTER_INVALID(*ef->sb, previous)) 254 | { 255 | exfat_error("invalid cluster 0x%x while growing", previous); 256 | return -EIO; 257 | } 258 | } 259 | else 260 | { 261 | if (node->fptr_index != 0) 262 | exfat_bug("non-zero pointer index (%u)", node->fptr_index); 263 | /* file does not have clusters (i.e. is empty), allocate 264 | the first one for it */ 265 | previous = allocate_cluster(ef, 0); 266 | if (CLUSTER_INVALID(*ef->sb, previous)) 267 | return -ENOSPC; 268 | node->fptr_cluster = node->start_cluster = previous; 269 | allocated = 1; 270 | /* file consists of only one cluster, so it's contiguous */ 271 | node->is_contiguous = true; 272 | } 273 | 274 | while (allocated < difference) 275 | { 276 | next = allocate_cluster(ef, previous + 1); 277 | if (CLUSTER_INVALID(*ef->sb, next)) 278 | { 279 | if (allocated != 0) 280 | shrink_file(ef, node, current + allocated, allocated); 281 | return -ENOSPC; 282 | } 283 | if (next != previous - 1 && node->is_contiguous) 284 | { 285 | /* it's a pity, but we are not able to keep the file contiguous 286 | anymore */ 287 | if (!make_noncontiguous(ef, node->start_cluster, previous)) 288 | return -EIO; 289 | node->is_contiguous = false; 290 | node->is_dirty = true; 291 | } 292 | if (!set_next_cluster(ef, node->is_contiguous, previous, next)) 293 | return -EIO; 294 | previous = next; 295 | allocated++; 296 | } 297 | 298 | if (!set_next_cluster(ef, node->is_contiguous, previous, 299 | EXFAT_CLUSTER_END)) 300 | return -EIO; 301 | return 0; 302 | } 303 | 304 | static int shrink_file(struct exfat* ef, struct exfat_node* node, 305 | uint32_t current, uint32_t difference) 306 | { 307 | cluster_t previous; 308 | cluster_t next; 309 | 310 | if (difference == 0) 311 | exfat_bug("zero difference passed"); 312 | if (node->start_cluster == EXFAT_CLUSTER_FREE) 313 | exfat_bug("unable to shrink empty file (%u clusters)", current); 314 | if (current < difference) 315 | exfat_bug("file underflow (%u < %u)", current, difference); 316 | 317 | /* crop the file */ 318 | if (current > difference) 319 | { 320 | cluster_t last = exfat_advance_cluster(ef, node, 321 | current - difference - 1); 322 | if (CLUSTER_INVALID(*ef->sb, last)) 323 | { 324 | exfat_error("invalid cluster 0x%x while shrinking", last); 325 | return -EIO; 326 | } 327 | previous = exfat_next_cluster(ef, node, last); 328 | if (!set_next_cluster(ef, node->is_contiguous, last, 329 | EXFAT_CLUSTER_END)) 330 | return -EIO; 331 | } 332 | else 333 | { 334 | previous = node->start_cluster; 335 | node->start_cluster = EXFAT_CLUSTER_FREE; 336 | node->is_dirty = true; 337 | } 338 | node->fptr_index = 0; 339 | node->fptr_cluster = node->start_cluster; 340 | 341 | /* free remaining clusters */ 342 | while (difference--) 343 | { 344 | if (CLUSTER_INVALID(*ef->sb, previous)) 345 | { 346 | exfat_error("invalid cluster 0x%x while freeing after shrink", 347 | previous); 348 | return -EIO; 349 | } 350 | 351 | next = exfat_next_cluster(ef, node, previous); 352 | if (!set_next_cluster(ef, node->is_contiguous, previous, 353 | EXFAT_CLUSTER_FREE)) 354 | return -EIO; 355 | free_cluster(ef, previous); 356 | previous = next; 357 | } 358 | return 0; 359 | } 360 | 361 | static bool erase_raw(struct exfat* ef, size_t size, off_t offset) 362 | { 363 | if (exfat_pwrite(ef->dev, ef->zero_cluster, size, offset) < 0) 364 | { 365 | exfat_error("failed to erase %zu bytes at %"PRId64, size, offset); 366 | return false; 367 | } 368 | return true; 369 | } 370 | 371 | static int erase_range(struct exfat* ef, struct exfat_node* node, 372 | uint64_t begin, uint64_t end) 373 | { 374 | uint64_t cluster_boundary; 375 | cluster_t cluster; 376 | 377 | if (begin >= end) 378 | return 0; 379 | 380 | cluster_boundary = (begin | (CLUSTER_SIZE(*ef->sb) - 1)) + 1; 381 | cluster = exfat_advance_cluster(ef, node, 382 | begin / CLUSTER_SIZE(*ef->sb)); 383 | if (CLUSTER_INVALID(*ef->sb, cluster)) 384 | { 385 | exfat_error("invalid cluster 0x%x while erasing", cluster); 386 | return -EIO; 387 | } 388 | /* erase from the beginning to the closest cluster boundary */ 389 | if (!erase_raw(ef, MIN(cluster_boundary, end) - begin, 390 | exfat_c2o(ef, cluster) + begin % CLUSTER_SIZE(*ef->sb))) 391 | return -EIO; 392 | /* erase whole clusters */ 393 | while (cluster_boundary < end) 394 | { 395 | cluster = exfat_next_cluster(ef, node, cluster); 396 | /* the cluster cannot be invalid because we have just allocated it */ 397 | if (CLUSTER_INVALID(*ef->sb, cluster)) 398 | exfat_bug("invalid cluster 0x%x after allocation", cluster); 399 | if (!erase_raw(ef, CLUSTER_SIZE(*ef->sb), exfat_c2o(ef, cluster))) 400 | return -EIO; 401 | cluster_boundary += CLUSTER_SIZE(*ef->sb); 402 | } 403 | return 0; 404 | } 405 | 406 | int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size, 407 | bool erase) 408 | { 409 | uint32_t c1 = bytes2clusters(ef, node->size); 410 | uint32_t c2 = bytes2clusters(ef, size); 411 | int rc = 0; 412 | 413 | if (node->references == 0 && node->parent) 414 | exfat_bug("no references, node changes can be lost"); 415 | 416 | if (node->size == size) 417 | return 0; 418 | 419 | if (c1 < c2) 420 | rc = grow_file(ef, node, c1, c2 - c1); 421 | else if (c1 > c2) 422 | rc = shrink_file(ef, node, c1, c1 - c2); 423 | 424 | if (rc != 0) 425 | return rc; 426 | 427 | if (erase) 428 | { 429 | rc = erase_range(ef, node, node->size, size); 430 | if (rc != 0) 431 | return rc; 432 | } 433 | 434 | exfat_update_mtime(node); 435 | node->size = size; 436 | node->is_dirty = true; 437 | return 0; 438 | } 439 | 440 | uint32_t exfat_count_free_clusters(const struct exfat* ef) 441 | { 442 | uint32_t free_clusters = 0; 443 | uint32_t i; 444 | 445 | for (i = 0; i < ef->cmap.size; i++) 446 | if (BMAP_GET(ef->cmap.chunk, i) == 0) 447 | free_clusters++; 448 | return free_clusters; 449 | } 450 | 451 | static int find_used_clusters(const struct exfat* ef, 452 | cluster_t* a, cluster_t* b) 453 | { 454 | const cluster_t end = le32_to_cpu(ef->sb->cluster_count); 455 | 456 | /* find first used cluster */ 457 | for (*a = *b + 1; *a < end; (*a)++) 458 | if (BMAP_GET(ef->cmap.chunk, *a - EXFAT_FIRST_DATA_CLUSTER)) 459 | break; 460 | if (*a >= end) 461 | return 1; 462 | 463 | /* find last contiguous used cluster */ 464 | for (*b = *a; *b < end; (*b)++) 465 | if (BMAP_GET(ef->cmap.chunk, *b - EXFAT_FIRST_DATA_CLUSTER) == 0) 466 | { 467 | (*b)--; 468 | break; 469 | } 470 | 471 | return 0; 472 | } 473 | 474 | int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b) 475 | { 476 | cluster_t ca, cb; 477 | 478 | if (*a == 0 && *b == 0) 479 | ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1; 480 | else 481 | { 482 | ca = s2c(ef, *a); 483 | cb = s2c(ef, *b); 484 | } 485 | if (find_used_clusters(ef, &ca, &cb) != 0) 486 | return 1; 487 | if (*a != 0 || *b != 0) 488 | *a = c2s(ef, ca); 489 | *b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb); 490 | return 0; 491 | } 492 | -------------------------------------------------------------------------------- /libexfat/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | compiler.h (09.06.13) 3 | Compiler-specific definitions. Note that unknown compiler is not a 4 | showstopper. 5 | 6 | Free exFAT implementation. 7 | Copyright (C) 2010-2018 Andrew Nayenko 8 | 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with this program; if not, write to the Free Software Foundation, Inc., 21 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | */ 23 | 24 | #ifndef COMPILER_H_INCLUDED 25 | #define COMPILER_H_INCLUDED 26 | 27 | #if __STDC_VERSION__ < 199901L 28 | #error C99-compliant compiler is required 29 | #endif 30 | 31 | #if defined(__clang__) 32 | 33 | #define PRINTF __attribute__((format(printf, 1, 2))) 34 | #define NORETURN __attribute__((noreturn)) 35 | #define PACKED __attribute__((packed)) 36 | #if __has_extension(c_static_assert) 37 | #define USE_C11_STATIC_ASSERT 38 | #endif 39 | 40 | #elif defined(__GNUC__) 41 | 42 | #define PRINTF __attribute__((format(printf, 1, 2))) 43 | #define NORETURN __attribute__((noreturn)) 44 | #define PACKED __attribute__((packed)) 45 | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) 46 | #define USE_C11_STATIC_ASSERT 47 | #endif 48 | 49 | #else 50 | 51 | #define PRINTF 52 | #define NORETURN 53 | #define PACKED 54 | 55 | #endif 56 | 57 | #ifdef USE_C11_STATIC_ASSERT 58 | #define STATIC_ASSERT(cond) _Static_assert(cond, #cond) 59 | #else 60 | #define CONCAT2(a, b) a ## b 61 | #define CONCAT1(a, b) CONCAT2(a, b) 62 | #define STATIC_ASSERT(cond) \ 63 | extern void CONCAT1(static_assert, __LINE__)(int x[(cond) ? 1 : -1]) 64 | #endif 65 | 66 | #endif /* ifndef COMPILER_H_INCLUDED */ 67 | -------------------------------------------------------------------------------- /libexfat/exfat.h: -------------------------------------------------------------------------------- 1 | /* 2 | exfat.h (29.08.09) 3 | Definitions of structures and constants used in exFAT file system 4 | implementation. 5 | 6 | Free exFAT implementation. 7 | Copyright (C) 2010-2018 Andrew Nayenko 8 | 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with this program; if not, write to the Free Software Foundation, Inc., 21 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | */ 23 | 24 | #ifndef EXFAT_H_INCLUDED 25 | #define EXFAT_H_INCLUDED 26 | 27 | #ifndef ANDROID 28 | /* Android.bp is used instead of autotools when targeting Android */ 29 | #include "config.h" 30 | #endif 31 | #include "compiler.h" 32 | #include "exfatfs.h" 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define EXFAT_NAME_MAX 255 41 | /* UTF-16 encodes code points up to U+FFFF as single 16-bit code units. 42 | UTF-8 uses up to 3 bytes (i.e. 8-bit code units) to encode code points 43 | up to U+FFFF. One additional character is for null terminator. */ 44 | #define EXFAT_UTF8_NAME_BUFFER_MAX (EXFAT_NAME_MAX * 3 + 1) 45 | #define EXFAT_UTF8_ENAME_BUFFER_MAX (EXFAT_ENAME_MAX * 3 + 1) 46 | 47 | #define SECTOR_SIZE(sb) (1 << (sb).sector_bits) 48 | #define CLUSTER_SIZE(sb) (SECTOR_SIZE(sb) << (sb).spc_bits) 49 | #define CLUSTER_INVALID(sb, c) ((c) < EXFAT_FIRST_DATA_CLUSTER || \ 50 | (c) - EXFAT_FIRST_DATA_CLUSTER >= le32_to_cpu((sb).cluster_count)) 51 | 52 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 53 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 54 | #define DIV_ROUND_UP(x, d) (((x) + (d) - 1) / (d)) 55 | #define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d)) 56 | 57 | #define BMAP_SIZE(count) (ROUND_UP(count, sizeof(bitmap_t) * 8) / 8) 58 | #define BMAP_BLOCK(index) ((index) / sizeof(bitmap_t) / 8) 59 | #define BMAP_MASK(index) ((bitmap_t) 1 << ((index) % (sizeof(bitmap_t) * 8))) 60 | #define BMAP_GET(bitmap, index) \ 61 | ((bitmap)[BMAP_BLOCK(index)] & BMAP_MASK(index)) 62 | #define BMAP_SET(bitmap, index) \ 63 | ((bitmap)[BMAP_BLOCK(index)] |= BMAP_MASK(index)) 64 | #define BMAP_CLR(bitmap, index) \ 65 | ((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index)) 66 | 67 | #define EXFAT_REPAIR(hook, ef, ...) \ 68 | (exfat_ask_to_fix(ef) && exfat_fix_ ## hook(ef, __VA_ARGS__)) 69 | 70 | /* The size of off_t type must be 64 bits. File systems larger than 2 GB will 71 | be corrupted with 32-bit off_t. */ 72 | STATIC_ASSERT(sizeof(off_t) == 8); 73 | 74 | struct exfat_node 75 | { 76 | struct exfat_node* parent; 77 | struct exfat_node* child; 78 | struct exfat_node* next; 79 | struct exfat_node* prev; 80 | 81 | int references; 82 | uint32_t fptr_index; 83 | cluster_t fptr_cluster; 84 | off_t entry_offset; 85 | cluster_t start_cluster; 86 | uint16_t attrib; 87 | uint8_t continuations; 88 | bool is_contiguous : 1; 89 | bool is_cached : 1; 90 | bool is_dirty : 1; 91 | bool is_unlinked : 1; 92 | uint64_t size; 93 | time_t mtime, atime; 94 | le16_t name[EXFAT_NAME_MAX + 1]; 95 | }; 96 | 97 | enum exfat_mode 98 | { 99 | EXFAT_MODE_RO, 100 | EXFAT_MODE_RW, 101 | EXFAT_MODE_ANY, 102 | }; 103 | 104 | struct exfat_dev; 105 | 106 | struct exfat 107 | { 108 | struct exfat_dev* dev; 109 | struct exfat_super_block* sb; 110 | uint16_t* upcase; 111 | struct exfat_node* root; 112 | struct 113 | { 114 | cluster_t start_cluster; 115 | uint32_t size; /* in bits */ 116 | bitmap_t* chunk; 117 | uint32_t chunk_size; /* in bits */ 118 | bool dirty; 119 | } 120 | cmap; 121 | char label[EXFAT_UTF8_ENAME_BUFFER_MAX]; 122 | void* zero_cluster; 123 | int dmask, fmask; 124 | uid_t uid; 125 | gid_t gid; 126 | int ro; 127 | bool noatime; 128 | enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair; 129 | }; 130 | 131 | /* in-core nodes iterator */ 132 | struct exfat_iterator 133 | { 134 | struct exfat_node* parent; 135 | struct exfat_node* current; 136 | }; 137 | 138 | struct exfat_human_bytes 139 | { 140 | uint64_t value; 141 | const char* unit; 142 | }; 143 | 144 | extern int exfat_errors; 145 | extern int exfat_errors_fixed; 146 | 147 | void exfat_bug(const char* format, ...) PRINTF NORETURN; 148 | void exfat_error(const char* format, ...) PRINTF; 149 | void exfat_warn(const char* format, ...) PRINTF; 150 | void exfat_debug(const char* format, ...) PRINTF; 151 | 152 | struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode); 153 | int exfat_close(struct exfat_dev* dev); 154 | int exfat_fsync(struct exfat_dev* dev); 155 | enum exfat_mode exfat_get_mode(const struct exfat_dev* dev); 156 | off_t exfat_get_size(const struct exfat_dev* dev); 157 | off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence); 158 | ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size); 159 | ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size); 160 | ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, 161 | off_t offset); 162 | ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, 163 | off_t offset); 164 | ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, 165 | void* buffer, size_t size, off_t offset); 166 | ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, 167 | const void* buffer, size_t size, off_t offset); 168 | 169 | int exfat_opendir(struct exfat* ef, struct exfat_node* dir, 170 | struct exfat_iterator* it); 171 | void exfat_closedir(struct exfat* ef, struct exfat_iterator* it); 172 | struct exfat_node* exfat_readdir(struct exfat_iterator* it); 173 | int exfat_lookup(struct exfat* ef, struct exfat_node** node, 174 | const char* path); 175 | int exfat_split(struct exfat* ef, struct exfat_node** parent, 176 | struct exfat_node** node, le16_t* name, const char* path); 177 | 178 | off_t exfat_c2o(const struct exfat* ef, cluster_t cluster); 179 | cluster_t exfat_next_cluster(const struct exfat* ef, 180 | const struct exfat_node* node, cluster_t cluster); 181 | cluster_t exfat_advance_cluster(const struct exfat* ef, 182 | struct exfat_node* node, uint32_t count); 183 | int exfat_flush_nodes(struct exfat* ef); 184 | int exfat_flush(struct exfat* ef); 185 | int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size, 186 | bool erase); 187 | uint32_t exfat_count_free_clusters(const struct exfat* ef); 188 | int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b); 189 | 190 | void exfat_stat(const struct exfat* ef, const struct exfat_node* node, 191 | struct stat* stbuf); 192 | void exfat_get_name(const struct exfat_node* node, 193 | char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]); 194 | uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry); 195 | uint16_t exfat_add_checksum(const void* entry, uint16_t sum); 196 | le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n); 197 | uint32_t exfat_vbr_start_checksum(const void* sector, size_t size); 198 | uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum); 199 | le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name, 200 | size_t length); 201 | void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb); 202 | void exfat_print_info(const struct exfat_super_block* sb, 203 | uint32_t free_clusters); 204 | 205 | int utf16_to_utf8(char* output, const le16_t* input, size_t outsize, 206 | size_t insize); 207 | int utf8_to_utf16(le16_t* output, const char* input, size_t outsize, 208 | size_t insize); 209 | size_t utf16_length(const le16_t* str); 210 | 211 | struct exfat_node* exfat_get_node(struct exfat_node* node); 212 | void exfat_put_node(struct exfat* ef, struct exfat_node* node); 213 | int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node); 214 | int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir); 215 | void exfat_reset_cache(struct exfat* ef); 216 | int exfat_flush_node(struct exfat* ef, struct exfat_node* node); 217 | int exfat_unlink(struct exfat* ef, struct exfat_node* node); 218 | int exfat_rmdir(struct exfat* ef, struct exfat_node* node); 219 | int exfat_mknod(struct exfat* ef, const char* path); 220 | int exfat_mkdir(struct exfat* ef, const char* path); 221 | int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path); 222 | void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]); 223 | void exfat_update_atime(struct exfat_node* node); 224 | void exfat_update_mtime(struct exfat_node* node); 225 | const char* exfat_get_label(struct exfat* ef); 226 | int exfat_set_label(struct exfat* ef, const char* label); 227 | 228 | int exfat_soil_super_block(const struct exfat* ef); 229 | int exfat_mount(struct exfat* ef, const char* spec, const char* options); 230 | void exfat_unmount(struct exfat* ef); 231 | 232 | time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec, 233 | uint8_t tzoffset); 234 | void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, 235 | uint8_t* centisec, uint8_t* tzoffset); 236 | void exfat_tzset(void); 237 | 238 | bool exfat_ask_to_fix(const struct exfat* ef); 239 | bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector, 240 | uint32_t vbr_checksum); 241 | bool exfat_fix_invalid_node_checksum(const struct exfat* ef, 242 | struct exfat_node* node); 243 | bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir, 244 | const struct exfat_entry* entry, off_t offset); 245 | 246 | #endif /* ifndef EXFAT_H_INCLUDED */ 247 | -------------------------------------------------------------------------------- /libexfat/exfatfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | exfatfs.h (29.08.09) 3 | Definitions of structures and constants used in exFAT file system. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef EXFATFS_H_INCLUDED 24 | #define EXFATFS_H_INCLUDED 25 | 26 | #include "byteorder.h" 27 | #include "compiler.h" 28 | 29 | typedef uint32_t cluster_t; /* cluster number */ 30 | 31 | #define EXFAT_FIRST_DATA_CLUSTER 2 32 | #define EXFAT_LAST_DATA_CLUSTER 0xfffffff6 33 | 34 | #define EXFAT_CLUSTER_FREE 0 /* free cluster */ 35 | #define EXFAT_CLUSTER_BAD 0xfffffff7 /* cluster contains bad sector */ 36 | #define EXFAT_CLUSTER_END 0xffffffff /* final cluster of file or directory */ 37 | 38 | #define EXFAT_STATE_MOUNTED 2 39 | 40 | struct exfat_super_block 41 | { 42 | uint8_t jump[3]; /* 0x00 jmp and nop instructions */ 43 | uint8_t oem_name[8]; /* 0x03 "EXFAT " */ 44 | uint8_t __unused1[53]; /* 0x0B always 0 */ 45 | le64_t sector_start; /* 0x40 partition first sector */ 46 | le64_t sector_count; /* 0x48 partition sectors count */ 47 | le32_t fat_sector_start; /* 0x50 FAT first sector */ 48 | le32_t fat_sector_count; /* 0x54 FAT sectors count */ 49 | le32_t cluster_sector_start; /* 0x58 first cluster sector */ 50 | le32_t cluster_count; /* 0x5C total clusters count */ 51 | le32_t rootdir_cluster; /* 0x60 first cluster of the root dir */ 52 | le32_t volume_serial; /* 0x64 volume serial number */ 53 | struct /* 0x68 FS version */ 54 | { 55 | uint8_t minor; 56 | uint8_t major; 57 | } 58 | version; 59 | le16_t volume_state; /* 0x6A volume state flags */ 60 | uint8_t sector_bits; /* 0x6C sector size as (1 << n) */ 61 | uint8_t spc_bits; /* 0x6D sectors per cluster as (1 << n) */ 62 | uint8_t fat_count; /* 0x6E always 1 */ 63 | uint8_t drive_no; /* 0x6F always 0x80 */ 64 | uint8_t allocated_percent; /* 0x70 percentage of allocated space */ 65 | uint8_t __unused2[397]; /* 0x71 always 0 */ 66 | le16_t boot_signature; /* the value of 0xAA55 */ 67 | } 68 | PACKED; 69 | STATIC_ASSERT(sizeof(struct exfat_super_block) == 512); 70 | 71 | #define EXFAT_ENTRY_VALID 0x80 72 | #define EXFAT_ENTRY_CONTINUED 0x40 73 | #define EXFAT_ENTRY_OPTIONAL 0x20 74 | 75 | #define EXFAT_ENTRY_BITMAP (0x01 | EXFAT_ENTRY_VALID) 76 | #define EXFAT_ENTRY_UPCASE (0x02 | EXFAT_ENTRY_VALID) 77 | #define EXFAT_ENTRY_LABEL (0x03 | EXFAT_ENTRY_VALID) 78 | #define EXFAT_ENTRY_FILE (0x05 | EXFAT_ENTRY_VALID) 79 | #define EXFAT_ENTRY_FILE_INFO (0x00 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED) 80 | #define EXFAT_ENTRY_FILE_NAME (0x01 | EXFAT_ENTRY_VALID | EXFAT_ENTRY_CONTINUED) 81 | #define EXFAT_ENTRY_FILE_TAIL (0x00 | EXFAT_ENTRY_VALID \ 82 | | EXFAT_ENTRY_CONTINUED \ 83 | | EXFAT_ENTRY_OPTIONAL) 84 | 85 | struct exfat_entry /* common container for all entries */ 86 | { 87 | uint8_t type; /* any of EXFAT_ENTRY_xxx */ 88 | uint8_t data[31]; 89 | } 90 | PACKED; 91 | STATIC_ASSERT(sizeof(struct exfat_entry) == 32); 92 | 93 | #define EXFAT_ENAME_MAX 15 94 | 95 | struct exfat_entry_bitmap /* allocated clusters bitmap */ 96 | { 97 | uint8_t type; /* EXFAT_ENTRY_BITMAP */ 98 | uint8_t __unknown1[19]; 99 | le32_t start_cluster; 100 | le64_t size; /* in bytes */ 101 | } 102 | PACKED; 103 | STATIC_ASSERT(sizeof(struct exfat_entry_bitmap) == 32); 104 | 105 | #define EXFAT_UPCASE_CHARS 0x10000 106 | 107 | struct exfat_entry_upcase /* upper case translation table */ 108 | { 109 | uint8_t type; /* EXFAT_ENTRY_UPCASE */ 110 | uint8_t __unknown1[3]; 111 | le32_t checksum; 112 | uint8_t __unknown2[12]; 113 | le32_t start_cluster; 114 | le64_t size; /* in bytes */ 115 | } 116 | PACKED; 117 | STATIC_ASSERT(sizeof(struct exfat_entry_upcase) == 32); 118 | 119 | struct exfat_entry_label /* volume label */ 120 | { 121 | uint8_t type; /* EXFAT_ENTRY_LABEL */ 122 | uint8_t length; /* number of characters */ 123 | le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */ 124 | } 125 | PACKED; 126 | STATIC_ASSERT(sizeof(struct exfat_entry_label) == 32); 127 | 128 | #define EXFAT_ATTRIB_RO 0x01 129 | #define EXFAT_ATTRIB_HIDDEN 0x02 130 | #define EXFAT_ATTRIB_SYSTEM 0x04 131 | #define EXFAT_ATTRIB_VOLUME 0x08 132 | #define EXFAT_ATTRIB_DIR 0x10 133 | #define EXFAT_ATTRIB_ARCH 0x20 134 | 135 | struct exfat_entry_meta1 /* file or directory info (part 1) */ 136 | { 137 | uint8_t type; /* EXFAT_ENTRY_FILE */ 138 | uint8_t continuations; 139 | le16_t checksum; 140 | le16_t attrib; /* combination of EXFAT_ATTRIB_xxx */ 141 | le16_t __unknown1; 142 | le16_t crtime, crdate; /* creation date and time */ 143 | le16_t mtime, mdate; /* latest modification date and time */ 144 | le16_t atime, adate; /* latest access date and time */ 145 | uint8_t crtime_cs; /* creation time in cs (centiseconds) */ 146 | uint8_t mtime_cs; /* latest modification time in cs */ 147 | uint8_t crtime_tzo, mtime_tzo, atime_tzo; /* timezone offset encoded */ 148 | uint8_t __unknown2[7]; 149 | } 150 | PACKED; 151 | STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32); 152 | 153 | #define EXFAT_FLAG_ALWAYS1 (1u << 0) 154 | #define EXFAT_FLAG_CONTIGUOUS (1u << 1) 155 | 156 | struct exfat_entry_meta2 /* file or directory info (part 2) */ 157 | { 158 | uint8_t type; /* EXFAT_ENTRY_FILE_INFO */ 159 | uint8_t flags; /* combination of EXFAT_FLAG_xxx */ 160 | uint8_t __unknown1; 161 | uint8_t name_length; 162 | le16_t name_hash; 163 | le16_t __unknown2; 164 | le64_t valid_size; /* in bytes, less or equal to size */ 165 | uint8_t __unknown3[4]; 166 | le32_t start_cluster; 167 | le64_t size; /* in bytes */ 168 | } 169 | PACKED; 170 | STATIC_ASSERT(sizeof(struct exfat_entry_meta2) == 32); 171 | 172 | struct exfat_entry_name /* file or directory name */ 173 | { 174 | uint8_t type; /* EXFAT_ENTRY_FILE_NAME */ 175 | uint8_t __unknown; 176 | le16_t name[EXFAT_ENAME_MAX]; /* in UTF-16LE */ 177 | } 178 | PACKED; 179 | STATIC_ASSERT(sizeof(struct exfat_entry_name) == 32); 180 | 181 | #endif /* ifndef EXFATFS_H_INCLUDED */ 182 | -------------------------------------------------------------------------------- /libexfat/io.c: -------------------------------------------------------------------------------- 1 | /* 2 | io.c (02.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #if defined(__APPLE__) 32 | #include 33 | #elif defined(__OpenBSD__) 34 | #include 35 | #include 36 | #include 37 | #include 38 | #elif __linux__ 39 | #include 40 | #endif 41 | #ifdef USE_UBLIO 42 | #include 43 | #include 44 | #endif 45 | 46 | struct exfat_dev 47 | { 48 | int fd; 49 | enum exfat_mode mode; 50 | off_t size; /* in bytes */ 51 | #ifdef USE_UBLIO 52 | off_t pos; 53 | ublio_filehandle_t ufh; 54 | #endif 55 | }; 56 | 57 | static bool is_open(int fd) 58 | { 59 | return fcntl(fd, F_GETFD) != -1; 60 | } 61 | 62 | static int open_ro(const char* spec) 63 | { 64 | return open(spec, O_RDONLY); 65 | } 66 | 67 | static int open_rw(const char* spec) 68 | { 69 | int fd = open(spec, O_RDWR); 70 | #ifdef __linux__ 71 | int ro = 0; 72 | 73 | /* 74 | This ioctl is needed because after "blockdev --setro" kernel still 75 | allows to open the device in read-write mode but fails writes. 76 | */ 77 | if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro) 78 | { 79 | close(fd); 80 | errno = EROFS; 81 | return -1; 82 | } 83 | #endif 84 | return fd; 85 | } 86 | 87 | struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) 88 | { 89 | struct exfat_dev* dev; 90 | struct stat stbuf; 91 | #ifdef USE_UBLIO 92 | struct ublio_param up; 93 | #endif 94 | 95 | /* The system allocates file descriptors sequentially. If we have been 96 | started with stdin (0), stdout (1) or stderr (2) closed, the system 97 | will give us descriptor 0, 1 or 2 later when we open block device, 98 | FUSE communication pipe, etc. As a result, functions using stdin, 99 | stdout or stderr will actually work with a different thing and can 100 | corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */ 101 | while (!is_open(STDIN_FILENO) 102 | || !is_open(STDOUT_FILENO) 103 | || !is_open(STDERR_FILENO)) 104 | { 105 | /* we don't need those descriptors, let them leak */ 106 | if (open("/dev/null", O_RDWR) == -1) 107 | { 108 | exfat_error("failed to open /dev/null"); 109 | return NULL; 110 | } 111 | } 112 | 113 | dev = malloc(sizeof(struct exfat_dev)); 114 | if (dev == NULL) 115 | { 116 | exfat_error("failed to allocate memory for device structure"); 117 | return NULL; 118 | } 119 | 120 | switch (mode) 121 | { 122 | case EXFAT_MODE_RO: 123 | dev->fd = open_ro(spec); 124 | if (dev->fd == -1) 125 | { 126 | free(dev); 127 | exfat_error("failed to open '%s' in read-only mode: %s", spec, 128 | strerror(errno)); 129 | return NULL; 130 | } 131 | dev->mode = EXFAT_MODE_RO; 132 | break; 133 | case EXFAT_MODE_RW: 134 | dev->fd = open_rw(spec); 135 | if (dev->fd == -1) 136 | { 137 | free(dev); 138 | exfat_error("failed to open '%s' in read-write mode: %s", spec, 139 | strerror(errno)); 140 | return NULL; 141 | } 142 | dev->mode = EXFAT_MODE_RW; 143 | break; 144 | case EXFAT_MODE_ANY: 145 | dev->fd = open_rw(spec); 146 | if (dev->fd != -1) 147 | { 148 | dev->mode = EXFAT_MODE_RW; 149 | break; 150 | } 151 | dev->fd = open_ro(spec); 152 | if (dev->fd != -1) 153 | { 154 | dev->mode = EXFAT_MODE_RO; 155 | exfat_warn("'%s' is write-protected, mounting read-only", spec); 156 | break; 157 | } 158 | free(dev); 159 | exfat_error("failed to open '%s': %s", spec, strerror(errno)); 160 | return NULL; 161 | } 162 | 163 | if (fstat(dev->fd, &stbuf) != 0) 164 | { 165 | close(dev->fd); 166 | free(dev); 167 | exfat_error("failed to fstat '%s'", spec); 168 | return NULL; 169 | } 170 | if (!S_ISBLK(stbuf.st_mode) && 171 | !S_ISCHR(stbuf.st_mode) && 172 | !S_ISREG(stbuf.st_mode)) 173 | { 174 | close(dev->fd); 175 | free(dev); 176 | exfat_error("'%s' is neither a device, nor a regular file", spec); 177 | return NULL; 178 | } 179 | 180 | #if defined(__APPLE__) 181 | if (!S_ISREG(stbuf.st_mode)) 182 | { 183 | uint32_t block_size = 0; 184 | uint64_t blocks = 0; 185 | 186 | if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0) 187 | { 188 | close(dev->fd); 189 | free(dev); 190 | exfat_error("failed to get block size"); 191 | return NULL; 192 | } 193 | if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0) 194 | { 195 | close(dev->fd); 196 | free(dev); 197 | exfat_error("failed to get blocks count"); 198 | return NULL; 199 | } 200 | dev->size = blocks * block_size; 201 | } 202 | else 203 | #elif defined(__OpenBSD__) 204 | if (!S_ISREG(stbuf.st_mode)) 205 | { 206 | struct disklabel lab; 207 | struct partition* pp; 208 | char* partition; 209 | 210 | if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1) 211 | { 212 | close(dev->fd); 213 | free(dev); 214 | exfat_error("failed to get disklabel"); 215 | return NULL; 216 | } 217 | 218 | /* Don't need to check that partition letter is valid as we won't get 219 | this far otherwise. */ 220 | partition = strchr(spec, '\0') - 1; 221 | pp = &(lab.d_partitions[*partition - 'a']); 222 | dev->size = DL_GETPSIZE(pp) * lab.d_secsize; 223 | 224 | if (pp->p_fstype != FS_NTFS) 225 | exfat_warn("partition type is not 0x07 (NTFS/exFAT); " 226 | "you can fix this with fdisk(8)"); 227 | } 228 | else 229 | #endif 230 | { 231 | /* works for Linux, FreeBSD, Solaris */ 232 | dev->size = exfat_seek(dev, 0, SEEK_END); 233 | if (dev->size <= 0) 234 | { 235 | close(dev->fd); 236 | free(dev); 237 | exfat_error("failed to get size of '%s'", spec); 238 | return NULL; 239 | } 240 | if (exfat_seek(dev, 0, SEEK_SET) == -1) 241 | { 242 | close(dev->fd); 243 | free(dev); 244 | exfat_error("failed to seek to the beginning of '%s'", spec); 245 | return NULL; 246 | } 247 | } 248 | 249 | #ifdef USE_UBLIO 250 | memset(&up, 0, sizeof(struct ublio_param)); 251 | up.up_blocksize = 256 * 1024; 252 | up.up_items = 64; 253 | up.up_grace = 32; 254 | up.up_priv = &dev->fd; 255 | 256 | dev->pos = 0; 257 | dev->ufh = ublio_open(&up); 258 | if (dev->ufh == NULL) 259 | { 260 | close(dev->fd); 261 | free(dev); 262 | exfat_error("failed to initialize ublio"); 263 | return NULL; 264 | } 265 | #endif 266 | 267 | return dev; 268 | } 269 | 270 | int exfat_close(struct exfat_dev* dev) 271 | { 272 | int rc = 0; 273 | 274 | #ifdef USE_UBLIO 275 | if (ublio_close(dev->ufh) != 0) 276 | { 277 | exfat_error("failed to close ublio"); 278 | rc = -EIO; 279 | } 280 | #endif 281 | if (close(dev->fd) != 0) 282 | { 283 | exfat_error("failed to close device: %s", strerror(errno)); 284 | rc = -EIO; 285 | } 286 | free(dev); 287 | return rc; 288 | } 289 | 290 | int exfat_fsync(struct exfat_dev* dev) 291 | { 292 | int rc = 0; 293 | 294 | #ifdef USE_UBLIO 295 | if (ublio_fsync(dev->ufh) != 0) 296 | { 297 | exfat_error("ublio fsync failed"); 298 | rc = -EIO; 299 | } 300 | #endif 301 | if (fsync(dev->fd) != 0) 302 | { 303 | exfat_error("fsync failed: %s", strerror(errno)); 304 | rc = -EIO; 305 | } 306 | return rc; 307 | } 308 | 309 | enum exfat_mode exfat_get_mode(const struct exfat_dev* dev) 310 | { 311 | return dev->mode; 312 | } 313 | 314 | off_t exfat_get_size(const struct exfat_dev* dev) 315 | { 316 | return dev->size; 317 | } 318 | 319 | off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence) 320 | { 321 | #ifdef USE_UBLIO 322 | /* XXX SEEK_CUR will be handled incorrectly */ 323 | return dev->pos = lseek(dev->fd, offset, whence); 324 | #else 325 | return lseek(dev->fd, offset, whence); 326 | #endif 327 | } 328 | 329 | ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size) 330 | { 331 | #ifdef USE_UBLIO 332 | ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos); 333 | if (result >= 0) 334 | dev->pos += size; 335 | return result; 336 | #else 337 | return read(dev->fd, buffer, size); 338 | #endif 339 | } 340 | 341 | ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size) 342 | { 343 | #ifdef USE_UBLIO 344 | ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos); 345 | if (result >= 0) 346 | dev->pos += size; 347 | return result; 348 | #else 349 | return write(dev->fd, buffer, size); 350 | #endif 351 | } 352 | 353 | ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, 354 | off_t offset) 355 | { 356 | #ifdef USE_UBLIO 357 | return ublio_pread(dev->ufh, buffer, size, offset); 358 | #else 359 | return pread(dev->fd, buffer, size, offset); 360 | #endif 361 | } 362 | 363 | ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, 364 | off_t offset) 365 | { 366 | #ifdef USE_UBLIO 367 | return ublio_pwrite(dev->ufh, buffer, size, offset); 368 | #else 369 | return pwrite(dev->fd, buffer, size, offset); 370 | #endif 371 | } 372 | 373 | ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, 374 | void* buffer, size_t size, off_t offset) 375 | { 376 | cluster_t cluster; 377 | char* bufp = buffer; 378 | off_t lsize, loffset, remainder; 379 | 380 | if (offset >= node->size) 381 | return 0; 382 | if (size == 0) 383 | return 0; 384 | 385 | cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); 386 | if (CLUSTER_INVALID(*ef->sb, cluster)) 387 | { 388 | exfat_error("invalid cluster 0x%x while reading", cluster); 389 | return -EIO; 390 | } 391 | 392 | loffset = offset % CLUSTER_SIZE(*ef->sb); 393 | remainder = MIN(size, node->size - offset); 394 | while (remainder > 0) 395 | { 396 | if (CLUSTER_INVALID(*ef->sb, cluster)) 397 | { 398 | exfat_error("invalid cluster 0x%x while reading", cluster); 399 | return -EIO; 400 | } 401 | lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); 402 | if (exfat_pread(ef->dev, bufp, lsize, 403 | exfat_c2o(ef, cluster) + loffset) < 0) 404 | { 405 | exfat_error("failed to read cluster %#x", cluster); 406 | return -EIO; 407 | } 408 | bufp += lsize; 409 | loffset = 0; 410 | remainder -= lsize; 411 | cluster = exfat_next_cluster(ef, node, cluster); 412 | } 413 | if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime) 414 | exfat_update_atime(node); 415 | return MIN(size, node->size - offset) - remainder; 416 | } 417 | 418 | ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, 419 | const void* buffer, size_t size, off_t offset) 420 | { 421 | int rc; 422 | cluster_t cluster; 423 | const char* bufp = buffer; 424 | off_t lsize, loffset, remainder; 425 | 426 | if (offset > node->size) 427 | { 428 | rc = exfat_truncate(ef, node, offset, true); 429 | if (rc != 0) 430 | return rc; 431 | } 432 | if (offset + size > node->size) 433 | { 434 | rc = exfat_truncate(ef, node, offset + size, false); 435 | if (rc != 0) 436 | return rc; 437 | } 438 | if (size == 0) 439 | return 0; 440 | 441 | cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb)); 442 | if (CLUSTER_INVALID(*ef->sb, cluster)) 443 | { 444 | exfat_error("invalid cluster 0x%x while writing", cluster); 445 | return -EIO; 446 | } 447 | 448 | loffset = offset % CLUSTER_SIZE(*ef->sb); 449 | remainder = size; 450 | while (remainder > 0) 451 | { 452 | if (CLUSTER_INVALID(*ef->sb, cluster)) 453 | { 454 | exfat_error("invalid cluster 0x%x while writing", cluster); 455 | return -EIO; 456 | } 457 | lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); 458 | if (exfat_pwrite(ef->dev, bufp, lsize, 459 | exfat_c2o(ef, cluster) + loffset) < 0) 460 | { 461 | exfat_error("failed to write cluster %#x", cluster); 462 | return -EIO; 463 | } 464 | bufp += lsize; 465 | loffset = 0; 466 | remainder -= lsize; 467 | cluster = exfat_next_cluster(ef, node, cluster); 468 | } 469 | if (!(node->attrib & EXFAT_ATTRIB_DIR)) 470 | /* directory's mtime should be updated by the caller only when it 471 | creates or removes something in this directory */ 472 | exfat_update_mtime(node); 473 | return size - remainder; 474 | } 475 | -------------------------------------------------------------------------------- /libexfat/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | log.c (02.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | #ifdef __ANDROID__ 26 | #include 27 | #else 28 | #include 29 | #endif 30 | #include 31 | 32 | int exfat_errors; 33 | 34 | /* 35 | * This message means an internal bug in exFAT implementation. 36 | */ 37 | void exfat_bug(const char* format, ...) 38 | { 39 | va_list ap, aq; 40 | 41 | va_start(ap, format); 42 | va_copy(aq, ap); 43 | 44 | fflush(stdout); 45 | fputs("BUG: ", stderr); 46 | vfprintf(stderr, format, ap); 47 | va_end(ap); 48 | fputs(".\n", stderr); 49 | 50 | #ifdef __ANDROID__ 51 | __android_log_vprint(ANDROID_LOG_FATAL, PACKAGE, format, aq); 52 | #else 53 | if (!isatty(STDERR_FILENO)) 54 | vsyslog(LOG_CRIT, format, aq); 55 | #endif 56 | va_end(aq); 57 | 58 | abort(); 59 | } 60 | 61 | /* 62 | * This message means an error in exFAT file system. 63 | */ 64 | void exfat_error(const char* format, ...) 65 | { 66 | va_list ap, aq; 67 | 68 | exfat_errors++; 69 | va_start(ap, format); 70 | va_copy(aq, ap); 71 | 72 | fflush(stdout); 73 | fputs("ERROR: ", stderr); 74 | vfprintf(stderr, format, ap); 75 | va_end(ap); 76 | fputs(".\n", stderr); 77 | 78 | #ifdef __ANDROID__ 79 | __android_log_vprint(ANDROID_LOG_ERROR, PACKAGE, format, aq); 80 | #else 81 | if (!isatty(STDERR_FILENO)) 82 | vsyslog(LOG_ERR, format, aq); 83 | #endif 84 | va_end(aq); 85 | } 86 | 87 | /* 88 | * This message means that there is something unexpected in exFAT file system 89 | * that can be a potential problem. 90 | */ 91 | void exfat_warn(const char* format, ...) 92 | { 93 | va_list ap, aq; 94 | 95 | va_start(ap, format); 96 | va_copy(aq, ap); 97 | 98 | fflush(stdout); 99 | fputs("WARN: ", stderr); 100 | vfprintf(stderr, format, ap); 101 | va_end(ap); 102 | fputs(".\n", stderr); 103 | 104 | #ifdef __ANDROID__ 105 | __android_log_vprint(ANDROID_LOG_WARN, PACKAGE, format, aq); 106 | #else 107 | if (!isatty(STDERR_FILENO)) 108 | vsyslog(LOG_WARNING, format, aq); 109 | #endif 110 | va_end(aq); 111 | } 112 | 113 | /* 114 | * Just debug message. Disabled by default. 115 | */ 116 | void exfat_debug(const char* format, ...) 117 | { 118 | va_list ap; 119 | 120 | fflush(stdout); 121 | fputs("DEBUG: ", stderr); 122 | va_start(ap, format); 123 | vfprintf(stderr, format, ap); 124 | va_end(ap); 125 | fputs(".\n", stderr); 126 | } 127 | -------------------------------------------------------------------------------- /libexfat/lookup.c: -------------------------------------------------------------------------------- 1 | /* 2 | lookup.c (02.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | #include 26 | #include 27 | 28 | int exfat_opendir(struct exfat* ef, struct exfat_node* dir, 29 | struct exfat_iterator* it) 30 | { 31 | int rc; 32 | 33 | exfat_get_node(dir); 34 | it->parent = dir; 35 | it->current = NULL; 36 | rc = exfat_cache_directory(ef, dir); 37 | if (rc != 0) 38 | exfat_put_node(ef, dir); 39 | return rc; 40 | } 41 | 42 | void exfat_closedir(struct exfat* ef, struct exfat_iterator* it) 43 | { 44 | exfat_put_node(ef, it->parent); 45 | it->parent = NULL; 46 | it->current = NULL; 47 | } 48 | 49 | struct exfat_node* exfat_readdir(struct exfat_iterator* it) 50 | { 51 | if (it->current == NULL) 52 | it->current = it->parent->child; 53 | else 54 | it->current = it->current->next; 55 | 56 | if (it->current != NULL) 57 | return exfat_get_node(it->current); 58 | else 59 | return NULL; 60 | } 61 | 62 | static int compare_char(struct exfat* ef, uint16_t a, uint16_t b) 63 | { 64 | return (int) ef->upcase[a] - (int) ef->upcase[b]; 65 | } 66 | 67 | static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b) 68 | { 69 | while (le16_to_cpu(*a) && le16_to_cpu(*b)) 70 | { 71 | int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b)); 72 | if (rc != 0) 73 | return rc; 74 | a++; 75 | b++; 76 | } 77 | return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b)); 78 | } 79 | 80 | static int lookup_name(struct exfat* ef, struct exfat_node* parent, 81 | struct exfat_node** node, const char* name, size_t n) 82 | { 83 | struct exfat_iterator it; 84 | le16_t buffer[EXFAT_NAME_MAX + 1]; 85 | int rc; 86 | 87 | *node = NULL; 88 | 89 | rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX + 1, n); 90 | if (rc != 0) 91 | return rc; 92 | 93 | rc = exfat_opendir(ef, parent, &it); 94 | if (rc != 0) 95 | return rc; 96 | while ((*node = exfat_readdir(&it))) 97 | { 98 | if (compare_name(ef, buffer, (*node)->name) == 0) 99 | { 100 | exfat_closedir(ef, &it); 101 | return 0; 102 | } 103 | exfat_put_node(ef, *node); 104 | } 105 | exfat_closedir(ef, &it); 106 | return -ENOENT; 107 | } 108 | 109 | static size_t get_comp(const char* path, const char** comp) 110 | { 111 | const char* end; 112 | 113 | *comp = path + strspn(path, "/"); /* skip leading slashes */ 114 | end = strchr(*comp, '/'); 115 | if (end == NULL) 116 | return strlen(*comp); 117 | else 118 | return end - *comp; 119 | } 120 | 121 | int exfat_lookup(struct exfat* ef, struct exfat_node** node, 122 | const char* path) 123 | { 124 | struct exfat_node* parent; 125 | const char* p; 126 | size_t n; 127 | int rc; 128 | 129 | /* start from the root directory */ 130 | parent = *node = exfat_get_node(ef->root); 131 | for (p = path; (n = get_comp(p, &p)); p += n) 132 | { 133 | if (n == 1 && *p == '.') /* skip "." component */ 134 | continue; 135 | rc = lookup_name(ef, parent, node, p, n); 136 | if (rc != 0) 137 | { 138 | exfat_put_node(ef, parent); 139 | return rc; 140 | } 141 | exfat_put_node(ef, parent); 142 | parent = *node; 143 | } 144 | return 0; 145 | } 146 | 147 | static bool is_last_comp(const char* comp, size_t length) 148 | { 149 | const char* p = comp + length; 150 | 151 | return get_comp(p, &p) == 0; 152 | } 153 | 154 | static bool is_allowed(const char* comp, size_t length) 155 | { 156 | size_t i; 157 | 158 | for (i = 0; i < length; i++) 159 | switch (comp[i]) 160 | { 161 | case 0x01 ... 0x1f: 162 | case '/': 163 | case '\\': 164 | case ':': 165 | case '*': 166 | case '?': 167 | case '"': 168 | case '<': 169 | case '>': 170 | case '|': 171 | return false; 172 | } 173 | return true; 174 | } 175 | 176 | int exfat_split(struct exfat* ef, struct exfat_node** parent, 177 | struct exfat_node** node, le16_t* name, const char* path) 178 | { 179 | const char* p; 180 | size_t n; 181 | int rc; 182 | 183 | memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t)); 184 | *parent = *node = exfat_get_node(ef->root); 185 | for (p = path; (n = get_comp(p, &p)); p += n) 186 | { 187 | if (n == 1 && *p == '.') 188 | continue; 189 | if (is_last_comp(p, n)) 190 | { 191 | if (!is_allowed(p, n)) 192 | { 193 | /* contains characters that are not allowed */ 194 | exfat_put_node(ef, *parent); 195 | return -ENOENT; 196 | } 197 | rc = utf8_to_utf16(name, p, EXFAT_NAME_MAX + 1, n); 198 | if (rc != 0) 199 | { 200 | exfat_put_node(ef, *parent); 201 | return rc; 202 | } 203 | 204 | rc = lookup_name(ef, *parent, node, p, n); 205 | if (rc != 0 && rc != -ENOENT) 206 | { 207 | exfat_put_node(ef, *parent); 208 | return rc; 209 | } 210 | return 0; 211 | } 212 | rc = lookup_name(ef, *parent, node, p, n); 213 | if (rc != 0) 214 | { 215 | exfat_put_node(ef, *parent); 216 | return rc; 217 | } 218 | exfat_put_node(ef, *parent); 219 | *parent = *node; 220 | } 221 | exfat_bug("impossible"); 222 | } 223 | -------------------------------------------------------------------------------- /libexfat/mount.c: -------------------------------------------------------------------------------- 1 | /* 2 | mount.c (22.10.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | static uint64_t rootdir_size(const struct exfat* ef) 32 | { 33 | uint32_t clusters = 0; 34 | uint32_t clusters_max = le32_to_cpu(ef->sb->cluster_count); 35 | cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster); 36 | 37 | /* Iterate all clusters of the root directory to calculate its size. 38 | It can't be contiguous because there is no flag to indicate this. */ 39 | do 40 | { 41 | if (clusters == clusters_max) /* infinite loop detected */ 42 | { 43 | exfat_error("root directory cannot occupy all %d clusters", 44 | clusters); 45 | return 0; 46 | } 47 | if (CLUSTER_INVALID(*ef->sb, rootdir_cluster)) 48 | { 49 | exfat_error("bad cluster %#x while reading root directory", 50 | rootdir_cluster); 51 | return 0; 52 | } 53 | rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster); 54 | clusters++; 55 | } 56 | while (rootdir_cluster != EXFAT_CLUSTER_END); 57 | 58 | return (uint64_t) clusters * CLUSTER_SIZE(*ef->sb); 59 | } 60 | 61 | static const char* get_option(const char* options, const char* option_name) 62 | { 63 | const char* p; 64 | size_t length = strlen(option_name); 65 | 66 | for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name)) 67 | if ((p == options || p[-1] == ',') && p[length] == '=') 68 | return p + length + 1; 69 | return NULL; 70 | } 71 | 72 | static int get_int_option(const char* options, const char* option_name, 73 | int base, int default_value) 74 | { 75 | const char* p = get_option(options, option_name); 76 | 77 | if (p == NULL) 78 | return default_value; 79 | return strtol(p, NULL, base); 80 | } 81 | 82 | static bool match_option(const char* options, const char* option_name) 83 | { 84 | const char* p; 85 | size_t length = strlen(option_name); 86 | 87 | for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name)) 88 | if ((p == options || p[-1] == ',') && 89 | (p[length] == ',' || p[length] == '\0')) 90 | return true; 91 | return false; 92 | } 93 | 94 | static void parse_options(struct exfat* ef, const char* options) 95 | { 96 | int opt_umask; 97 | 98 | opt_umask = get_int_option(options, "umask", 8, 0); 99 | ef->dmask = get_int_option(options, "dmask", 8, opt_umask); 100 | ef->fmask = get_int_option(options, "fmask", 8, opt_umask); 101 | 102 | ef->uid = get_int_option(options, "uid", 10, geteuid()); 103 | ef->gid = get_int_option(options, "gid", 10, getegid()); 104 | 105 | ef->noatime = match_option(options, "noatime"); 106 | 107 | switch (get_int_option(options, "repair", 10, 0)) 108 | { 109 | case 1: 110 | ef->repair = EXFAT_REPAIR_ASK; 111 | break; 112 | case 2: 113 | ef->repair = EXFAT_REPAIR_YES; 114 | break; 115 | default: 116 | ef->repair = EXFAT_REPAIR_NO; 117 | break; 118 | } 119 | } 120 | 121 | static bool verify_vbr_checksum(const struct exfat* ef, void* sector) 122 | { 123 | off_t sector_size = SECTOR_SIZE(*ef->sb); 124 | uint32_t vbr_checksum; 125 | int i; 126 | 127 | if (exfat_pread(ef->dev, sector, sector_size, 0) < 0) 128 | { 129 | exfat_error("failed to read boot sector"); 130 | return false; 131 | } 132 | vbr_checksum = exfat_vbr_start_checksum(sector, sector_size); 133 | for (i = 1; i < 11; i++) 134 | { 135 | if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0) 136 | { 137 | exfat_error("failed to read VBR sector"); 138 | return false; 139 | } 140 | vbr_checksum = exfat_vbr_add_checksum(sector, sector_size, 141 | vbr_checksum); 142 | } 143 | if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0) 144 | { 145 | exfat_error("failed to read VBR checksum sector"); 146 | return false; 147 | } 148 | for (i = 0; i < sector_size / sizeof(vbr_checksum); i++) 149 | if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum) 150 | { 151 | exfat_error("invalid VBR checksum 0x%x (expected 0x%x)", 152 | le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum); 153 | if (!EXFAT_REPAIR(invalid_vbr_checksum, ef, sector, vbr_checksum)) 154 | return false; 155 | } 156 | return true; 157 | } 158 | 159 | static int commit_super_block(const struct exfat* ef) 160 | { 161 | if (exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0) 162 | { 163 | exfat_error("failed to write super block"); 164 | return 1; 165 | } 166 | return exfat_fsync(ef->dev); 167 | } 168 | 169 | int exfat_soil_super_block(const struct exfat* ef) 170 | { 171 | if (ef->ro) 172 | return 0; 173 | 174 | ef->sb->volume_state = cpu_to_le16( 175 | le16_to_cpu(ef->sb->volume_state) | EXFAT_STATE_MOUNTED); 176 | return commit_super_block(ef); 177 | } 178 | 179 | static void exfat_free(struct exfat* ef) 180 | { 181 | exfat_close(ef->dev); /* first of all, close the descriptor */ 182 | ef->dev = NULL; /* struct exfat_dev is freed by exfat_close() */ 183 | free(ef->root); 184 | ef->root = NULL; 185 | free(ef->zero_cluster); 186 | ef->zero_cluster = NULL; 187 | free(ef->cmap.chunk); 188 | ef->cmap.chunk = NULL; 189 | free(ef->upcase); 190 | ef->upcase = NULL; 191 | free(ef->sb); 192 | ef->sb = NULL; 193 | } 194 | 195 | int exfat_mount(struct exfat* ef, const char* spec, const char* options) 196 | { 197 | int rc; 198 | enum exfat_mode mode; 199 | 200 | exfat_tzset(); 201 | memset(ef, 0, sizeof(struct exfat)); 202 | 203 | parse_options(ef, options); 204 | 205 | if (match_option(options, "ro")) 206 | mode = EXFAT_MODE_RO; 207 | else if (match_option(options, "ro_fallback")) 208 | mode = EXFAT_MODE_ANY; 209 | else 210 | mode = EXFAT_MODE_RW; 211 | ef->dev = exfat_open(spec, mode); 212 | if (ef->dev == NULL) 213 | return -EIO; 214 | if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO) 215 | { 216 | if (mode == EXFAT_MODE_ANY) 217 | ef->ro = -1; 218 | else 219 | ef->ro = 1; 220 | } 221 | 222 | ef->sb = malloc(sizeof(struct exfat_super_block)); 223 | if (ef->sb == NULL) 224 | { 225 | exfat_error("failed to allocate memory for the super block"); 226 | exfat_free(ef); 227 | return -ENOMEM; 228 | } 229 | memset(ef->sb, 0, sizeof(struct exfat_super_block)); 230 | 231 | if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0) 232 | { 233 | exfat_error("failed to read boot sector"); 234 | exfat_free(ef); 235 | return -EIO; 236 | } 237 | if (memcmp(ef->sb->oem_name, "EXFAT ", 8) != 0) 238 | { 239 | exfat_error("exFAT file system is not found"); 240 | exfat_free(ef); 241 | return -EIO; 242 | } 243 | /* sector cannot be smaller than 512 bytes */ 244 | if (ef->sb->sector_bits < 9) 245 | { 246 | exfat_error("too small sector size: 2^%hhd", ef->sb->sector_bits); 247 | exfat_free(ef); 248 | return -EIO; 249 | } 250 | /* officially exFAT supports cluster size up to 32 MB */ 251 | if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25) 252 | { 253 | exfat_error("too big cluster size: 2^(%hhd+%hhd)", 254 | ef->sb->sector_bits, ef->sb->spc_bits); 255 | exfat_free(ef); 256 | return -EIO; 257 | } 258 | ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb)); 259 | if (ef->zero_cluster == NULL) 260 | { 261 | exfat_error("failed to allocate zero sector"); 262 | exfat_free(ef); 263 | return -ENOMEM; 264 | } 265 | /* use zero_cluster as a temporary buffer for VBR checksum verification */ 266 | if (!verify_vbr_checksum(ef, ef->zero_cluster)) 267 | { 268 | exfat_free(ef); 269 | return -EIO; 270 | } 271 | memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb)); 272 | if (ef->sb->version.major != 1 || ef->sb->version.minor != 0) 273 | { 274 | exfat_error("unsupported exFAT version: %hhu.%hhu", 275 | ef->sb->version.major, ef->sb->version.minor); 276 | exfat_free(ef); 277 | return -EIO; 278 | } 279 | if (ef->sb->fat_count != 1) 280 | { 281 | exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count); 282 | exfat_free(ef); 283 | return -EIO; 284 | } 285 | if (le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb) > 286 | exfat_get_size(ef->dev)) 287 | { 288 | /* this can cause I/O errors later but we don't fail mounting to let 289 | user rescue data */ 290 | exfat_warn("file system in sectors is larger than device: " 291 | "%"PRIu64" * %d > %"PRIu64, 292 | le64_to_cpu(ef->sb->sector_count), SECTOR_SIZE(*ef->sb), 293 | exfat_get_size(ef->dev)); 294 | } 295 | if ((off_t) le32_to_cpu(ef->sb->cluster_count) * CLUSTER_SIZE(*ef->sb) > 296 | exfat_get_size(ef->dev)) 297 | { 298 | exfat_error("file system in clusters is larger than device: " 299 | "%u * %d > %"PRIu64, 300 | le32_to_cpu(ef->sb->cluster_count), CLUSTER_SIZE(*ef->sb), 301 | exfat_get_size(ef->dev)); 302 | exfat_free(ef); 303 | return -EIO; 304 | } 305 | if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED) 306 | exfat_warn("volume was not unmounted cleanly"); 307 | 308 | ef->root = malloc(sizeof(struct exfat_node)); 309 | if (ef->root == NULL) 310 | { 311 | exfat_error("failed to allocate root node"); 312 | exfat_free(ef); 313 | return -ENOMEM; 314 | } 315 | memset(ef->root, 0, sizeof(struct exfat_node)); 316 | ef->root->attrib = EXFAT_ATTRIB_DIR; 317 | ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster); 318 | ef->root->fptr_cluster = ef->root->start_cluster; 319 | ef->root->name[0] = cpu_to_le16('\0'); 320 | ef->root->size = rootdir_size(ef); 321 | if (ef->root->size == 0) 322 | { 323 | exfat_free(ef); 324 | return -EIO; 325 | } 326 | /* exFAT does not have time attributes for the root directory */ 327 | ef->root->mtime = 0; 328 | ef->root->atime = 0; 329 | /* always keep at least 1 reference to the root node */ 330 | exfat_get_node(ef->root); 331 | 332 | rc = exfat_cache_directory(ef, ef->root); 333 | if (rc != 0) 334 | goto error; 335 | if (ef->upcase == NULL) 336 | { 337 | exfat_error("upcase table is not found"); 338 | goto error; 339 | } 340 | if (ef->cmap.chunk == NULL) 341 | { 342 | exfat_error("clusters bitmap is not found"); 343 | goto error; 344 | } 345 | 346 | return 0; 347 | 348 | error: 349 | exfat_put_node(ef, ef->root); 350 | exfat_reset_cache(ef); 351 | exfat_free(ef); 352 | return -EIO; 353 | } 354 | 355 | static void finalize_super_block(struct exfat* ef) 356 | { 357 | if (ef->ro) 358 | return; 359 | 360 | ef->sb->volume_state = cpu_to_le16( 361 | le16_to_cpu(ef->sb->volume_state) & ~EXFAT_STATE_MOUNTED); 362 | 363 | /* Some implementations set the percentage of allocated space to 0xff 364 | on FS creation and never update it. In this case leave it as is. */ 365 | if (ef->sb->allocated_percent != 0xff) 366 | { 367 | uint32_t free, total; 368 | 369 | free = exfat_count_free_clusters(ef); 370 | total = le32_to_cpu(ef->sb->cluster_count); 371 | ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total; 372 | } 373 | 374 | commit_super_block(ef); /* ignore return code */ 375 | } 376 | 377 | void exfat_unmount(struct exfat* ef) 378 | { 379 | exfat_flush_nodes(ef); /* ignore return code */ 380 | exfat_flush(ef); /* ignore return code */ 381 | exfat_put_node(ef, ef->root); 382 | exfat_reset_cache(ef); 383 | finalize_super_block(ef); 384 | exfat_free(ef); /* will close the descriptor */ 385 | } 386 | -------------------------------------------------------------------------------- /libexfat/platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | platform.h (14.05.13) 3 | OS-specific code (libc-specific in fact). Note that systems with the 4 | same kernel can use different libc implementations. 5 | 6 | Free exFAT implementation. 7 | Copyright (C) 2010-2018 Andrew Nayenko 8 | 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with this program; if not, write to the Free Software Foundation, Inc., 21 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | */ 23 | 24 | #ifndef PLATFORM_H_INCLUDED 25 | #define PLATFORM_H_INCLUDED 26 | 27 | #if defined(__linux__) || defined(__GLIBC__) || defined(__GNU__) 28 | 29 | #include 30 | #include 31 | #define exfat_bswap16(x) bswap_16(x) 32 | #define exfat_bswap32(x) bswap_32(x) 33 | #define exfat_bswap64(x) bswap_64(x) 34 | #define EXFAT_BYTE_ORDER __BYTE_ORDER 35 | #define EXFAT_LITTLE_ENDIAN __LITTLE_ENDIAN 36 | #define EXFAT_BIG_ENDIAN __BIG_ENDIAN 37 | 38 | #elif defined(__APPLE__) 39 | 40 | #include 41 | #include 42 | #define exfat_bswap16(x) OSSwapInt16(x) 43 | #define exfat_bswap32(x) OSSwapInt32(x) 44 | #define exfat_bswap64(x) OSSwapInt64(x) 45 | #define EXFAT_BYTE_ORDER BYTE_ORDER 46 | #define EXFAT_LITTLE_ENDIAN LITTLE_ENDIAN 47 | #define EXFAT_BIG_ENDIAN BIG_ENDIAN 48 | 49 | #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) 50 | 51 | #include 52 | #define exfat_bswap16(x) bswap16(x) 53 | #define exfat_bswap32(x) bswap32(x) 54 | #define exfat_bswap64(x) bswap64(x) 55 | #define EXFAT_BYTE_ORDER _BYTE_ORDER 56 | #define EXFAT_LITTLE_ENDIAN _LITTLE_ENDIAN 57 | #define EXFAT_BIG_ENDIAN _BIG_ENDIAN 58 | 59 | #else 60 | #error Unknown platform 61 | #endif 62 | 63 | #endif /* ifndef PLATFORM_H_INCLUDED */ 64 | -------------------------------------------------------------------------------- /libexfat/repair.c: -------------------------------------------------------------------------------- 1 | /* 2 | repair.c (09.03.17) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | 26 | int exfat_errors_fixed; 27 | 28 | bool exfat_ask_to_fix(const struct exfat* ef) 29 | { 30 | const char* question = "Fix (Y/N)?"; 31 | char answer[8]; 32 | bool yeah, nope; 33 | 34 | switch (ef->repair) 35 | { 36 | case EXFAT_REPAIR_NO: 37 | return false; 38 | case EXFAT_REPAIR_YES: 39 | printf("%s %s", question, "Y\n"); 40 | return true; 41 | case EXFAT_REPAIR_ASK: 42 | do 43 | { 44 | printf("%s ", question); 45 | fflush(stdout); 46 | if (fgets(answer, sizeof(answer), stdin)) 47 | { 48 | yeah = strcasecmp(answer, "Y\n") == 0; 49 | nope = strcasecmp(answer, "N\n") == 0; 50 | } 51 | else 52 | { 53 | yeah = false; 54 | nope = true; 55 | } 56 | } 57 | while (!yeah && !nope); 58 | return yeah; 59 | } 60 | exfat_bug("invalid repair option value: %d", ef->repair); 61 | } 62 | 63 | bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector, 64 | uint32_t vbr_checksum) 65 | { 66 | size_t i; 67 | off_t sector_size = SECTOR_SIZE(*ef->sb); 68 | 69 | for (i = 0; i < sector_size / sizeof(vbr_checksum); i++) 70 | ((le32_t*) sector)[i] = cpu_to_le32(vbr_checksum); 71 | if (exfat_pwrite(ef->dev, sector, sector_size, 11 * sector_size) < 0) 72 | { 73 | exfat_error("failed to write correct VBR checksum"); 74 | return false; 75 | } 76 | exfat_errors_fixed++; 77 | return true; 78 | } 79 | 80 | bool exfat_fix_invalid_node_checksum(const struct exfat* ef, 81 | struct exfat_node* node) 82 | { 83 | /* checksum will be rewritten by exfat_flush_node() */ 84 | node->is_dirty = true; 85 | 86 | exfat_errors_fixed++; 87 | return true; 88 | } 89 | 90 | bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir, 91 | const struct exfat_entry* entry, off_t offset) 92 | { 93 | struct exfat_entry deleted = *entry; 94 | 95 | deleted.type &= ~EXFAT_ENTRY_VALID; 96 | if (exfat_generic_pwrite(ef, dir, &deleted, sizeof(struct exfat_entry), 97 | offset) != sizeof(struct exfat_entry)) 98 | return false; 99 | 100 | exfat_errors_fixed++; 101 | return true; 102 | } 103 | -------------------------------------------------------------------------------- /libexfat/time.c: -------------------------------------------------------------------------------- 1 | /* 2 | time.c (03.02.12) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | 25 | /* timezone offset from UTC in seconds; positive for western timezones, 26 | negative for eastern ones */ 27 | static long exfat_timezone; 28 | 29 | #define SEC_IN_MIN 60ll 30 | #define SEC_IN_HOUR (60 * SEC_IN_MIN) 31 | #define SEC_IN_DAY (24 * SEC_IN_HOUR) 32 | #define SEC_IN_YEAR (365 * SEC_IN_DAY) /* not leap year */ 33 | /* Unix epoch started at 0:00:00 UTC 1 January 1970 */ 34 | #define UNIX_EPOCH_YEAR 1970 35 | /* exFAT epoch started at 0:00:00 UTC 1 January 1980 */ 36 | #define EXFAT_EPOCH_YEAR 1980 37 | /* number of years from Unix epoch to exFAT epoch */ 38 | #define EPOCH_DIFF_YEAR (EXFAT_EPOCH_YEAR - UNIX_EPOCH_YEAR) 39 | /* number of days from Unix epoch to exFAT epoch (considering leap days) */ 40 | #define EPOCH_DIFF_DAYS (EPOCH_DIFF_YEAR * 365 + EPOCH_DIFF_YEAR / 4) 41 | /* number of seconds from Unix epoch to exFAT epoch (considering leap days) */ 42 | #define EPOCH_DIFF_SEC (EPOCH_DIFF_DAYS * SEC_IN_DAY) 43 | /* number of leap years passed from exFAT epoch to the specified year 44 | (excluding the specified year itself) */ 45 | #define LEAP_YEARS(year) ((EXFAT_EPOCH_YEAR + (year) - 1) / 4 \ 46 | - (EXFAT_EPOCH_YEAR - 1) / 4) 47 | /* checks whether the specified year is leap */ 48 | #define IS_LEAP_YEAR(year) ((EXFAT_EPOCH_YEAR + (year)) % 4 == 0) 49 | 50 | static const time_t days_in_year[] = 51 | { 52 | /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ 53 | 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 54 | }; 55 | 56 | time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec, 57 | uint8_t tzoffset) 58 | { 59 | time_t unix_time = EPOCH_DIFF_SEC; 60 | uint16_t ndate = le16_to_cpu(date); 61 | uint16_t ntime = le16_to_cpu(time); 62 | 63 | uint16_t day = ndate & 0x1f; /* 5 bits, 1-31 */ 64 | uint16_t month = ndate >> 5 & 0xf; /* 4 bits, 1-12 */ 65 | uint16_t year = ndate >> 9; /* 7 bits, 1-127 (+1980) */ 66 | 67 | uint16_t twosec = ntime & 0x1f; /* 5 bits, 0-29 (2 sec granularity) */ 68 | uint16_t min = ntime >> 5 & 0x3f; /* 6 bits, 0-59 */ 69 | uint16_t hour = ntime >> 11; /* 5 bits, 0-23 */ 70 | 71 | if (day == 0 || month == 0 || month > 12) 72 | { 73 | exfat_error("bad date %u-%02hu-%02hu", 74 | year + EXFAT_EPOCH_YEAR, month, day); 75 | return 0; 76 | } 77 | if (hour > 23 || min > 59 || twosec > 29) 78 | { 79 | exfat_error("bad time %hu:%02hu:%02u", 80 | hour, min, twosec * 2); 81 | return 0; 82 | } 83 | if (centisec > 199) 84 | { 85 | exfat_error("bad centiseconds count %hhu", centisec); 86 | return 0; 87 | } 88 | 89 | /* every 4th year between 1904 and 2096 is leap */ 90 | unix_time += year * SEC_IN_YEAR + LEAP_YEARS(year) * SEC_IN_DAY; 91 | unix_time += days_in_year[month] * SEC_IN_DAY; 92 | /* if it's leap year and February has passed we should add 1 day */ 93 | if ((EXFAT_EPOCH_YEAR + year) % 4 == 0 && month > 2) 94 | unix_time += SEC_IN_DAY; 95 | unix_time += (day - 1) * SEC_IN_DAY; 96 | 97 | unix_time += hour * SEC_IN_HOUR; 98 | unix_time += min * SEC_IN_MIN; 99 | /* exFAT represents time with 2 sec granularity */ 100 | unix_time += twosec * 2; 101 | unix_time += centisec / 100; 102 | 103 | /* exFAT stores timestamps in local time, so we correct it to UTC */ 104 | if (tzoffset & 0x80) 105 | /* lower 7 bits are signed timezone offset in 15 minute increments */ 106 | unix_time -= (int8_t)(tzoffset << 1) * 15 * 60 / 2; 107 | else 108 | /* timezone offset not present, assume our local timezone */ 109 | unix_time += exfat_timezone; 110 | 111 | return unix_time; 112 | } 113 | 114 | void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, 115 | uint8_t* centisec, uint8_t* tzoffset) 116 | { 117 | time_t shift = EPOCH_DIFF_SEC + exfat_timezone; 118 | uint16_t day, month, year; 119 | uint16_t twosec, min, hour; 120 | int days; 121 | int i; 122 | 123 | /* time before exFAT epoch cannot be represented */ 124 | if (unix_time < shift) 125 | unix_time = shift; 126 | 127 | unix_time -= shift; 128 | 129 | days = unix_time / SEC_IN_DAY; 130 | year = (4 * days) / (4 * 365 + 1); 131 | days -= year * 365 + LEAP_YEARS(year); 132 | month = 0; 133 | for (i = 1; i <= 12; i++) 134 | { 135 | int leap_day = (IS_LEAP_YEAR(year) && i == 2); 136 | int leap_sub = (IS_LEAP_YEAR(year) && i >= 3); 137 | 138 | if (i == 12 || days - leap_sub < days_in_year[i + 1] + leap_day) 139 | { 140 | month = i; 141 | days -= days_in_year[i] + leap_sub; 142 | break; 143 | } 144 | } 145 | day = days + 1; 146 | 147 | hour = (unix_time % SEC_IN_DAY) / SEC_IN_HOUR; 148 | min = (unix_time % SEC_IN_HOUR) / SEC_IN_MIN; 149 | twosec = (unix_time % SEC_IN_MIN) / 2; 150 | 151 | *date = cpu_to_le16(day | (month << 5) | (year << 9)); 152 | *time = cpu_to_le16(twosec | (min << 5) | (hour << 11)); 153 | if (centisec) 154 | *centisec = (unix_time % 2) * 100; 155 | 156 | /* record our local timezone offset in exFAT (15 minute increment) format */ 157 | *tzoffset = (uint8_t)(-exfat_timezone / 60 / 15) | 0x80; 158 | } 159 | 160 | void exfat_tzset(void) 161 | { 162 | time_t now; 163 | struct tm* utc; 164 | 165 | tzset(); 166 | now = time(NULL); 167 | utc = gmtime(&now); 168 | /* gmtime() always sets tm_isdst to 0 because daylight savings never 169 | affect UTC. Setting tm_isdst to -1 makes mktime() to determine whether 170 | summer time is in effect. */ 171 | utc->tm_isdst = -1; 172 | exfat_timezone = mktime(utc) - now; 173 | } 174 | -------------------------------------------------------------------------------- /libexfat/utf.c: -------------------------------------------------------------------------------- 1 | /* 2 | utf.c (13.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | 26 | static char* wchar_to_utf8(char* output, wchar_t wc, size_t outsize) 27 | { 28 | if (wc <= 0x7f) 29 | { 30 | if (outsize < 1) 31 | return NULL; 32 | *output++ = (char) wc; 33 | } 34 | else if (wc <= 0x7ff) 35 | { 36 | if (outsize < 2) 37 | return NULL; 38 | *output++ = 0xc0 | (wc >> 6); 39 | *output++ = 0x80 | (wc & 0x3f); 40 | } 41 | else if (wc <= 0xffff) 42 | { 43 | if (outsize < 3) 44 | return NULL; 45 | *output++ = 0xe0 | (wc >> 12); 46 | *output++ = 0x80 | ((wc >> 6) & 0x3f); 47 | *output++ = 0x80 | (wc & 0x3f); 48 | } 49 | else if (wc <= 0x1fffff) 50 | { 51 | if (outsize < 4) 52 | return NULL; 53 | *output++ = 0xf0 | (wc >> 18); 54 | *output++ = 0x80 | ((wc >> 12) & 0x3f); 55 | *output++ = 0x80 | ((wc >> 6) & 0x3f); 56 | *output++ = 0x80 | (wc & 0x3f); 57 | } 58 | else if (wc <= 0x3ffffff) 59 | { 60 | if (outsize < 5) 61 | return NULL; 62 | *output++ = 0xf8 | (wc >> 24); 63 | *output++ = 0x80 | ((wc >> 18) & 0x3f); 64 | *output++ = 0x80 | ((wc >> 12) & 0x3f); 65 | *output++ = 0x80 | ((wc >> 6) & 0x3f); 66 | *output++ = 0x80 | (wc & 0x3f); 67 | } 68 | else if (wc <= 0x7fffffff) 69 | { 70 | if (outsize < 6) 71 | return NULL; 72 | *output++ = 0xfc | (wc >> 30); 73 | *output++ = 0x80 | ((wc >> 24) & 0x3f); 74 | *output++ = 0x80 | ((wc >> 18) & 0x3f); 75 | *output++ = 0x80 | ((wc >> 12) & 0x3f); 76 | *output++ = 0x80 | ((wc >> 6) & 0x3f); 77 | *output++ = 0x80 | (wc & 0x3f); 78 | } 79 | else 80 | return NULL; 81 | 82 | return output; 83 | } 84 | 85 | static const le16_t* utf16_to_wchar(const le16_t* input, wchar_t* wc, 86 | size_t insize) 87 | { 88 | if ((le16_to_cpu(input[0]) & 0xfc00) == 0xd800) 89 | { 90 | if (insize < 2 || (le16_to_cpu(input[1]) & 0xfc00) != 0xdc00) 91 | return NULL; 92 | *wc = ((wchar_t) (le16_to_cpu(input[0]) & 0x3ff) << 10); 93 | *wc |= (le16_to_cpu(input[1]) & 0x3ff); 94 | *wc += 0x10000; 95 | return input + 2; 96 | } 97 | else 98 | { 99 | *wc = le16_to_cpu(*input); 100 | return input + 1; 101 | } 102 | } 103 | 104 | int utf16_to_utf8(char* output, const le16_t* input, size_t outsize, 105 | size_t insize) 106 | { 107 | const le16_t* iptr = input; 108 | const le16_t* iend = input + insize; 109 | char* optr = output; 110 | const char* oend = output + outsize; 111 | wchar_t wc; 112 | 113 | while (iptr < iend) 114 | { 115 | iptr = utf16_to_wchar(iptr, &wc, iend - iptr); 116 | if (iptr == NULL) 117 | { 118 | exfat_error("illegal UTF-16 sequence"); 119 | return -EILSEQ; 120 | } 121 | optr = wchar_to_utf8(optr, wc, oend - optr); 122 | if (optr == NULL) 123 | { 124 | exfat_error("name is too long"); 125 | return -ENAMETOOLONG; 126 | } 127 | if (wc == 0) 128 | return 0; 129 | } 130 | if (optr >= oend) 131 | { 132 | exfat_error("name is too long"); 133 | return -ENAMETOOLONG; 134 | } 135 | *optr = '\0'; 136 | return 0; 137 | } 138 | 139 | static const char* utf8_to_wchar(const char* input, wchar_t* wc, 140 | size_t insize) 141 | { 142 | if ((input[0] & 0x80) == 0 && insize >= 1) 143 | { 144 | *wc = (wchar_t) input[0]; 145 | return input + 1; 146 | } 147 | if ((input[0] & 0xe0) == 0xc0 && insize >= 2) 148 | { 149 | *wc = (((wchar_t) input[0] & 0x1f) << 6) | 150 | ((wchar_t) input[1] & 0x3f); 151 | return input + 2; 152 | } 153 | if ((input[0] & 0xf0) == 0xe0 && insize >= 3) 154 | { 155 | *wc = (((wchar_t) input[0] & 0x0f) << 12) | 156 | (((wchar_t) input[1] & 0x3f) << 6) | 157 | ((wchar_t) input[2] & 0x3f); 158 | return input + 3; 159 | } 160 | if ((input[0] & 0xf8) == 0xf0 && insize >= 4) 161 | { 162 | *wc = (((wchar_t) input[0] & 0x07) << 18) | 163 | (((wchar_t) input[1] & 0x3f) << 12) | 164 | (((wchar_t) input[2] & 0x3f) << 6) | 165 | ((wchar_t) input[3] & 0x3f); 166 | return input + 4; 167 | } 168 | if ((input[0] & 0xfc) == 0xf8 && insize >= 5) 169 | { 170 | *wc = (((wchar_t) input[0] & 0x03) << 24) | 171 | (((wchar_t) input[1] & 0x3f) << 18) | 172 | (((wchar_t) input[2] & 0x3f) << 12) | 173 | (((wchar_t) input[3] & 0x3f) << 6) | 174 | ((wchar_t) input[4] & 0x3f); 175 | return input + 5; 176 | } 177 | if ((input[0] & 0xfe) == 0xfc && insize >= 6) 178 | { 179 | *wc = (((wchar_t) input[0] & 0x01) << 30) | 180 | (((wchar_t) input[1] & 0x3f) << 24) | 181 | (((wchar_t) input[2] & 0x3f) << 18) | 182 | (((wchar_t) input[3] & 0x3f) << 12) | 183 | (((wchar_t) input[4] & 0x3f) << 6) | 184 | ((wchar_t) input[5] & 0x3f); 185 | return input + 6; 186 | } 187 | return NULL; 188 | } 189 | 190 | static le16_t* wchar_to_utf16(le16_t* output, wchar_t wc, size_t outsize) 191 | { 192 | if (wc <= 0xffff) /* if character is from BMP */ 193 | { 194 | if (outsize == 0) 195 | return NULL; 196 | output[0] = cpu_to_le16(wc); 197 | return output + 1; 198 | } 199 | if (outsize < 2) 200 | return NULL; 201 | wc -= 0x10000; 202 | output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff)); 203 | output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff)); 204 | return output + 2; 205 | } 206 | 207 | int utf8_to_utf16(le16_t* output, const char* input, size_t outsize, 208 | size_t insize) 209 | { 210 | const char* iptr = input; 211 | const char* iend = input + insize; 212 | le16_t* optr = output; 213 | const le16_t* oend = output + outsize; 214 | wchar_t wc; 215 | 216 | while (iptr < iend) 217 | { 218 | iptr = utf8_to_wchar(iptr, &wc, iend - iptr); 219 | if (iptr == NULL) 220 | { 221 | exfat_error("illegal UTF-8 sequence"); 222 | return -EILSEQ; 223 | } 224 | optr = wchar_to_utf16(optr, wc, oend - optr); 225 | if (optr == NULL) 226 | { 227 | exfat_error("name is too long"); 228 | return -ENAMETOOLONG; 229 | } 230 | if (wc == 0) 231 | break; 232 | } 233 | if (optr >= oend) 234 | { 235 | exfat_error("name is too long"); 236 | return -ENAMETOOLONG; 237 | } 238 | *optr = cpu_to_le16(0); 239 | return 0; 240 | } 241 | 242 | size_t utf16_length(const le16_t* str) 243 | { 244 | size_t i = 0; 245 | 246 | while (le16_to_cpu(str[i])) 247 | i++; 248 | return i; 249 | } 250 | -------------------------------------------------------------------------------- /libexfat/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | utils.c (04.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "exfat.h" 24 | #include 25 | #include 26 | #include 27 | 28 | void exfat_stat(const struct exfat* ef, const struct exfat_node* node, 29 | struct stat* stbuf) 30 | { 31 | memset(stbuf, 0, sizeof(struct stat)); 32 | if (node->attrib & EXFAT_ATTRIB_DIR) 33 | stbuf->st_mode = S_IFDIR | (0777 & ~ef->dmask); 34 | else 35 | stbuf->st_mode = S_IFREG | (0777 & ~ef->fmask); 36 | stbuf->st_nlink = 1; 37 | stbuf->st_uid = ef->uid; 38 | stbuf->st_gid = ef->gid; 39 | stbuf->st_size = node->size; 40 | stbuf->st_blocks = ROUND_UP(node->size, CLUSTER_SIZE(*ef->sb)) / 512; 41 | stbuf->st_mtime = node->mtime; 42 | stbuf->st_atime = node->atime; 43 | /* set ctime to mtime to ensure we don't break programs that rely on ctime 44 | (e.g. rsync) */ 45 | stbuf->st_ctime = node->mtime; 46 | } 47 | 48 | void exfat_get_name(const struct exfat_node* node, 49 | char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]) 50 | { 51 | if (utf16_to_utf8(buffer, node->name, EXFAT_UTF8_NAME_BUFFER_MAX, 52 | EXFAT_NAME_MAX) != 0) 53 | exfat_bug("failed to convert name to UTF-8"); 54 | } 55 | 56 | static uint16_t add_checksum_byte(uint16_t sum, uint8_t byte) 57 | { 58 | return ((sum << 15) | (sum >> 1)) + byte; 59 | } 60 | 61 | static uint16_t add_checksum_bytes(uint16_t sum, const void* buffer, size_t n) 62 | { 63 | size_t i; 64 | 65 | for (i = 0; i < n; i++) 66 | sum = add_checksum_byte(sum, ((const uint8_t*) buffer)[i]); 67 | return sum; 68 | } 69 | 70 | uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry) 71 | { 72 | uint16_t sum = 0; 73 | size_t i; 74 | 75 | for (i = 0; i < sizeof(struct exfat_entry); i++) 76 | if (i != 2 && i != 3) /* skip checksum field itself */ 77 | sum = add_checksum_byte(sum, ((const uint8_t*) entry)[i]); 78 | return sum; 79 | } 80 | 81 | uint16_t exfat_add_checksum(const void* entry, uint16_t sum) 82 | { 83 | return add_checksum_bytes(sum, entry, sizeof(struct exfat_entry)); 84 | } 85 | 86 | le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n) 87 | { 88 | uint16_t checksum; 89 | int i; 90 | 91 | checksum = exfat_start_checksum((const struct exfat_entry_meta1*) entries); 92 | for (i = 1; i < n; i++) 93 | checksum = exfat_add_checksum(entries + i, checksum); 94 | return cpu_to_le16(checksum); 95 | } 96 | 97 | uint32_t exfat_vbr_start_checksum(const void* sector, size_t size) 98 | { 99 | size_t i; 100 | uint32_t sum = 0; 101 | 102 | for (i = 0; i < size; i++) 103 | /* skip volume_state and allocated_percent fields */ 104 | if (i != 0x6a && i != 0x6b && i != 0x70) 105 | sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i]; 106 | return sum; 107 | } 108 | 109 | uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum) 110 | { 111 | size_t i; 112 | 113 | for (i = 0; i < size; i++) 114 | sum = ((sum << 31) | (sum >> 1)) + ((const uint8_t*) sector)[i]; 115 | return sum; 116 | } 117 | 118 | le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name, 119 | size_t length) 120 | { 121 | size_t i; 122 | uint16_t hash = 0; 123 | 124 | for (i = 0; i < length; i++) 125 | { 126 | uint16_t c = le16_to_cpu(name[i]); 127 | 128 | /* convert to upper case */ 129 | c = ef->upcase[c]; 130 | 131 | hash = ((hash << 15) | (hash >> 1)) + (c & 0xff); 132 | hash = ((hash << 15) | (hash >> 1)) + (c >> 8); 133 | } 134 | return cpu_to_le16(hash); 135 | } 136 | 137 | void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb) 138 | { 139 | size_t i; 140 | /* 16 EB (minus 1 byte) is the largest size that can be represented by 141 | uint64_t */ 142 | const char* units[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"}; 143 | uint64_t divisor = 1; 144 | uint64_t temp = 0; 145 | 146 | for (i = 0; ; i++, divisor *= 1024) 147 | { 148 | temp = (value + divisor / 2) / divisor; 149 | 150 | if (temp == 0) 151 | break; 152 | if (temp / 1024 * 1024 == temp) 153 | continue; 154 | if (temp < 10240) 155 | break; 156 | } 157 | hb->value = temp; 158 | hb->unit = units[i]; 159 | } 160 | 161 | void exfat_print_info(const struct exfat_super_block* sb, 162 | uint32_t free_clusters) 163 | { 164 | struct exfat_human_bytes hb; 165 | off_t total_space = le64_to_cpu(sb->sector_count) * SECTOR_SIZE(*sb); 166 | off_t avail_space = (off_t) free_clusters * CLUSTER_SIZE(*sb); 167 | 168 | printf("File system version %hhu.%hhu\n", 169 | sb->version.major, sb->version.minor); 170 | exfat_humanize_bytes(SECTOR_SIZE(*sb), &hb); 171 | printf("Sector size %10"PRIu64" %s\n", hb.value, hb.unit); 172 | exfat_humanize_bytes(CLUSTER_SIZE(*sb), &hb); 173 | printf("Cluster size %10"PRIu64" %s\n", hb.value, hb.unit); 174 | exfat_humanize_bytes(total_space, &hb); 175 | printf("Volume size %10"PRIu64" %s\n", hb.value, hb.unit); 176 | exfat_humanize_bytes(total_space - avail_space, &hb); 177 | printf("Used space %10"PRIu64" %s\n", hb.value, hb.unit); 178 | exfat_humanize_bytes(avail_space, &hb); 179 | printf("Available space %10"PRIu64" %s\n", hb.value, hb.unit); 180 | } 181 | -------------------------------------------------------------------------------- /mkfs/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2018 Andrew Nayenko 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 2 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License along 19 | # with this program; if not, write to the Free Software Foundation, Inc., 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | # 22 | 23 | sbin_PROGRAMS = mkexfatfs 24 | dist_man8_MANS = mkexfatfs.8 25 | mkexfatfs_SOURCES = \ 26 | cbm.c \ 27 | cbm.h \ 28 | fat.c \ 29 | fat.h \ 30 | main.c \ 31 | mkexfat.c \ 32 | mkexfat.h \ 33 | rootdir.c \ 34 | rootdir.h \ 35 | uct.c \ 36 | uct.h \ 37 | uctc.c \ 38 | uctc.h \ 39 | vbr.c \ 40 | vbr.h 41 | mkexfatfs_CPPFLAGS = -I$(top_srcdir)/libexfat 42 | mkexfatfs_LDADD = ../libexfat/libexfat.a 43 | 44 | install-exec-hook: 45 | ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/mkfs.exfat 46 | 47 | uninstall-hook: 48 | rm -f $(DESTDIR)$(sbindir)/mkfs.exfat 49 | -------------------------------------------------------------------------------- /mkfs/cbm.c: -------------------------------------------------------------------------------- 1 | /* 2 | cbm.c (09.11.10) 3 | Clusters Bitmap creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "cbm.h" 24 | #include "fat.h" 25 | #include "uct.h" 26 | #include "rootdir.h" 27 | #include 28 | #include 29 | 30 | static off_t cbm_alignment(void) 31 | { 32 | return get_cluster_size(); 33 | } 34 | 35 | static off_t cbm_size(void) 36 | { 37 | return DIV_ROUND_UP( 38 | (get_volume_size() - get_position(&cbm)) / get_cluster_size(), 39 | CHAR_BIT); 40 | } 41 | 42 | static int cbm_write(struct exfat_dev* dev) 43 | { 44 | uint32_t allocated_clusters = 45 | DIV_ROUND_UP(cbm.get_size(), get_cluster_size()) + 46 | DIV_ROUND_UP(uct.get_size(), get_cluster_size()) + 47 | DIV_ROUND_UP(rootdir.get_size(), get_cluster_size()); 48 | size_t bitmap_size = ROUND_UP(allocated_clusters, CHAR_BIT); 49 | bitmap_t* bitmap = malloc(BMAP_SIZE(bitmap_size)); 50 | size_t i; 51 | 52 | if (bitmap == NULL) 53 | { 54 | exfat_error("failed to allocate bitmap of %zu bytes", 55 | BMAP_SIZE(bitmap_size)); 56 | return 1; 57 | } 58 | memset(bitmap, 0, BMAP_SIZE(bitmap_size)); 59 | 60 | for (i = 0; i < bitmap_size; i++) 61 | if (i < allocated_clusters) 62 | BMAP_SET(bitmap, i); 63 | if (exfat_write(dev, bitmap, bitmap_size / CHAR_BIT) < 0) 64 | { 65 | free(bitmap); 66 | exfat_error("failed to write bitmap of %zu bytes", 67 | bitmap_size / CHAR_BIT); 68 | return 1; 69 | } 70 | free(bitmap); 71 | return 0; 72 | } 73 | 74 | const struct fs_object cbm = 75 | { 76 | .get_alignment = cbm_alignment, 77 | .get_size = cbm_size, 78 | .write = cbm_write, 79 | }; 80 | -------------------------------------------------------------------------------- /mkfs/cbm.h: -------------------------------------------------------------------------------- 1 | /* 2 | cbm.h (09.11.10) 3 | Clusters Bitmap creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_CBM_H_INCLUDED 24 | #define MKFS_CBM_H_INCLUDED 25 | 26 | #include "mkexfat.h" 27 | 28 | extern const struct fs_object cbm; 29 | 30 | #endif /* ifndef MKFS_CBM_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /mkfs/fat.c: -------------------------------------------------------------------------------- 1 | /* 2 | fat.c (09.11.10) 3 | File Allocation Table creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "fat.h" 24 | #include "cbm.h" 25 | #include "uct.h" 26 | #include "rootdir.h" 27 | #include 28 | 29 | static off_t fat_alignment(void) 30 | { 31 | return (off_t) 128 * get_sector_size(); 32 | } 33 | 34 | static off_t fat_size(void) 35 | { 36 | return get_volume_size() / get_cluster_size() * sizeof(cluster_t); 37 | } 38 | 39 | static cluster_t fat_write_entry(struct exfat_dev* dev, cluster_t cluster, 40 | cluster_t value) 41 | { 42 | le32_t fat_entry = cpu_to_le32(value); 43 | if (exfat_write(dev, &fat_entry, sizeof(fat_entry)) < 0) 44 | { 45 | exfat_error("failed to write FAT entry 0x%x", value); 46 | return 0; 47 | } 48 | return cluster + 1; 49 | } 50 | 51 | static cluster_t fat_write_entries(struct exfat_dev* dev, cluster_t cluster, 52 | uint64_t length) 53 | { 54 | cluster_t end = cluster + DIV_ROUND_UP(length, get_cluster_size()); 55 | 56 | while (cluster < end - 1) 57 | { 58 | cluster = fat_write_entry(dev, cluster, cluster + 1); 59 | if (cluster == 0) 60 | return 0; 61 | } 62 | return fat_write_entry(dev, cluster, EXFAT_CLUSTER_END); 63 | } 64 | 65 | static int fat_write(struct exfat_dev* dev) 66 | { 67 | cluster_t c = 0; 68 | 69 | if (!(c = fat_write_entry(dev, c, 0xfffffff8))) /* media type */ 70 | return 1; 71 | if (!(c = fat_write_entry(dev, c, 0xffffffff))) /* some weird constant */ 72 | return 1; 73 | if (!(c = fat_write_entries(dev, c, cbm.get_size()))) 74 | return 1; 75 | if (!(c = fat_write_entries(dev, c, uct.get_size()))) 76 | return 1; 77 | if (!(c = fat_write_entries(dev, c, rootdir.get_size()))) 78 | return 1; 79 | 80 | return 0; 81 | } 82 | 83 | const struct fs_object fat = 84 | { 85 | .get_alignment = fat_alignment, 86 | .get_size = fat_size, 87 | .write = fat_write, 88 | }; 89 | -------------------------------------------------------------------------------- /mkfs/fat.h: -------------------------------------------------------------------------------- 1 | /* 2 | fat.h (09.11.10) 3 | File Allocation Table creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_FAT_H_INCLUDED 24 | #define MKFS_FAT_H_INCLUDED 25 | 26 | #include "mkexfat.h" 27 | 28 | extern const struct fs_object fat; 29 | 30 | #endif /* ifndef MKFS_FAT_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /mkfs/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (15.08.10) 3 | Creates exFAT file system. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "mkexfat.h" 24 | #include "vbr.h" 25 | #include "fat.h" 26 | #include "cbm.h" 27 | #include "uct.h" 28 | #include "rootdir.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | const struct fs_object* objects[] = 39 | { 40 | &vbr, 41 | &vbr, 42 | &fat, 43 | /* clusters heap */ 44 | &cbm, 45 | &uct, 46 | &rootdir, 47 | NULL, 48 | }; 49 | 50 | static struct 51 | { 52 | int sector_bits; 53 | int spc_bits; 54 | off_t volume_size; 55 | le16_t volume_label[EXFAT_ENAME_MAX + 1]; 56 | uint32_t volume_serial; 57 | uint64_t first_sector; 58 | } 59 | param; 60 | 61 | int get_sector_bits(void) 62 | { 63 | return param.sector_bits; 64 | } 65 | 66 | int get_spc_bits(void) 67 | { 68 | return param.spc_bits; 69 | } 70 | 71 | off_t get_volume_size(void) 72 | { 73 | return param.volume_size; 74 | } 75 | 76 | const le16_t* get_volume_label(void) 77 | { 78 | return param.volume_label; 79 | } 80 | 81 | uint32_t get_volume_serial(void) 82 | { 83 | return param.volume_serial; 84 | } 85 | 86 | uint64_t get_first_sector(void) 87 | { 88 | return param.first_sector; 89 | } 90 | 91 | int get_sector_size(void) 92 | { 93 | return 1 << get_sector_bits(); 94 | } 95 | 96 | int get_cluster_size(void) 97 | { 98 | return get_sector_size() << get_spc_bits(); 99 | } 100 | 101 | static int setup_spc_bits(int sector_bits, int user_defined, off_t volume_size) 102 | { 103 | int i; 104 | 105 | if (user_defined != -1) 106 | { 107 | off_t cluster_size = 1 << sector_bits << user_defined; 108 | if (volume_size / cluster_size > EXFAT_LAST_DATA_CLUSTER) 109 | { 110 | struct exfat_human_bytes chb, vhb; 111 | 112 | exfat_humanize_bytes(cluster_size, &chb); 113 | exfat_humanize_bytes(volume_size, &vhb); 114 | exfat_error("cluster size %"PRIu64" %s is too small for " 115 | "%"PRIu64" %s volume, try -s %d", 116 | chb.value, chb.unit, 117 | vhb.value, vhb.unit, 118 | 1 << setup_spc_bits(sector_bits, -1, volume_size)); 119 | return -1; 120 | } 121 | return user_defined; 122 | } 123 | 124 | if (volume_size < 256LL * 1024 * 1024) 125 | return MAX(0, 12 - sector_bits); /* 4 KB */ 126 | if (volume_size < 32LL * 1024 * 1024 * 1024) 127 | return MAX(0, 15 - sector_bits); /* 32 KB */ 128 | 129 | for (i = 17; ; i++) /* 128 KB or more */ 130 | if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER) 131 | return MAX(0, i - sector_bits); 132 | } 133 | 134 | static int setup_volume_label(le16_t label[EXFAT_ENAME_MAX + 1], const char* s) 135 | { 136 | memset(label, 0, (EXFAT_ENAME_MAX + 1) * sizeof(le16_t)); 137 | if (s == NULL) 138 | return 0; 139 | return utf8_to_utf16(label, s, EXFAT_ENAME_MAX + 1, strlen(s)); 140 | } 141 | 142 | static uint32_t setup_volume_serial(uint32_t user_defined) 143 | { 144 | struct timeval now; 145 | 146 | if (user_defined != 0) 147 | return user_defined; 148 | 149 | if (gettimeofday(&now, NULL) != 0) 150 | { 151 | exfat_error("failed to form volume id"); 152 | return 0; 153 | } 154 | return (now.tv_sec << 20) | now.tv_usec; 155 | } 156 | 157 | static int setup(struct exfat_dev* dev, int sector_bits, int spc_bits, 158 | const char* volume_label, uint32_t volume_serial, 159 | uint64_t first_sector) 160 | { 161 | param.sector_bits = sector_bits; 162 | param.first_sector = first_sector; 163 | param.volume_size = exfat_get_size(dev); 164 | 165 | param.spc_bits = setup_spc_bits(sector_bits, spc_bits, param.volume_size); 166 | if (param.spc_bits == -1) 167 | return 1; 168 | 169 | if (setup_volume_label(param.volume_label, volume_label) != 0) 170 | return 1; 171 | 172 | param.volume_serial = setup_volume_serial(volume_serial); 173 | if (param.volume_serial == 0) 174 | return 1; 175 | 176 | return mkfs(dev, param.volume_size); 177 | } 178 | 179 | static int logarithm2(int n) 180 | { 181 | size_t i; 182 | 183 | for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++) 184 | if ((1 << i) == n) 185 | return i; 186 | return -1; 187 | } 188 | 189 | static void usage(const char* prog) 190 | { 191 | fprintf(stderr, "Usage: %s [-i volume-id] [-n label] " 192 | "[-p partition-first-sector] " 193 | "[-s sectors-per-cluster] [-V] \n", prog); 194 | exit(1); 195 | } 196 | 197 | int main(int argc, char* argv[]) 198 | { 199 | const char* spec = NULL; 200 | int opt; 201 | int spc_bits = -1; 202 | const char* volume_label = NULL; 203 | uint32_t volume_serial = 0; 204 | uint64_t first_sector = 0; 205 | struct exfat_dev* dev; 206 | 207 | printf("mkexfatfs %s\n", VERSION); 208 | 209 | while ((opt = getopt(argc, argv, "i:n:p:s:V")) != -1) 210 | { 211 | switch (opt) 212 | { 213 | case 'i': 214 | volume_serial = strtol(optarg, NULL, 16); 215 | break; 216 | case 'n': 217 | volume_label = optarg; 218 | break; 219 | case 'p': 220 | first_sector = strtoll(optarg, NULL, 10); 221 | break; 222 | case 's': 223 | spc_bits = logarithm2(atoi(optarg)); 224 | if (spc_bits < 0) 225 | { 226 | exfat_error("invalid option value: '%s'", optarg); 227 | return 1; 228 | } 229 | break; 230 | case 'V': 231 | puts("Copyright (C) 2011-2018 Andrew Nayenko"); 232 | return 0; 233 | default: 234 | usage(argv[0]); 235 | break; 236 | } 237 | } 238 | if (argc - optind != 1) 239 | usage(argv[0]); 240 | spec = argv[optind]; 241 | 242 | dev = exfat_open(spec, EXFAT_MODE_RW); 243 | if (dev == NULL) 244 | return 1; 245 | if (setup(dev, 9, spc_bits, volume_label, volume_serial, 246 | first_sector) != 0) 247 | { 248 | exfat_close(dev); 249 | return 1; 250 | } 251 | if (exfat_close(dev) != 0) 252 | return 1; 253 | printf("File system created successfully.\n"); 254 | return 0; 255 | } 256 | -------------------------------------------------------------------------------- /mkfs/mkexfat.c: -------------------------------------------------------------------------------- 1 | /* 2 | mkexfat.c (22.04.12) 3 | FS creation engine. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "mkexfat.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | static int check_size(off_t volume_size) 31 | { 32 | const struct fs_object** pp; 33 | off_t position = 0; 34 | 35 | for (pp = objects; *pp; pp++) 36 | { 37 | position = ROUND_UP(position, (*pp)->get_alignment()); 38 | position += (*pp)->get_size(); 39 | } 40 | 41 | if (position > volume_size) 42 | { 43 | struct exfat_human_bytes vhb; 44 | 45 | exfat_humanize_bytes(volume_size, &vhb); 46 | exfat_error("too small device (%"PRIu64" %s)", vhb.value, vhb.unit); 47 | return 1; 48 | } 49 | 50 | return 0; 51 | 52 | } 53 | 54 | static int erase_object(struct exfat_dev* dev, const void* block, 55 | size_t block_size, off_t start, off_t size) 56 | { 57 | const off_t block_count = DIV_ROUND_UP(size, block_size); 58 | off_t i; 59 | 60 | if (exfat_seek(dev, start, SEEK_SET) == (off_t) -1) 61 | { 62 | exfat_error("seek to 0x%"PRIx64" failed", start); 63 | return 1; 64 | } 65 | for (i = 0; i < size; i += block_size) 66 | { 67 | if (exfat_write(dev, block, MIN(size - i, block_size)) < 0) 68 | { 69 | exfat_error("failed to erase block %"PRIu64"/%"PRIu64 70 | " at 0x%"PRIx64, i + 1, block_count, start); 71 | return 1; 72 | } 73 | } 74 | return 0; 75 | } 76 | 77 | static int erase(struct exfat_dev* dev) 78 | { 79 | const struct fs_object** pp; 80 | off_t position = 0; 81 | const size_t block_size = 1024 * 1024; 82 | void* block = malloc(block_size); 83 | 84 | if (block == NULL) 85 | { 86 | exfat_error("failed to allocate erase block of %zu bytes", block_size); 87 | return 1; 88 | } 89 | memset(block, 0, block_size); 90 | 91 | for (pp = objects; *pp; pp++) 92 | { 93 | position = ROUND_UP(position, (*pp)->get_alignment()); 94 | if (erase_object(dev, block, block_size, position, 95 | (*pp)->get_size()) != 0) 96 | { 97 | free(block); 98 | return 1; 99 | } 100 | position += (*pp)->get_size(); 101 | } 102 | 103 | free(block); 104 | return 0; 105 | } 106 | 107 | static int create(struct exfat_dev* dev) 108 | { 109 | const struct fs_object** pp; 110 | off_t position = 0; 111 | 112 | for (pp = objects; *pp; pp++) 113 | { 114 | position = ROUND_UP(position, (*pp)->get_alignment()); 115 | if (exfat_seek(dev, position, SEEK_SET) == (off_t) -1) 116 | { 117 | exfat_error("seek to 0x%"PRIx64" failed", position); 118 | return 1; 119 | } 120 | if ((*pp)->write(dev) != 0) 121 | return 1; 122 | position += (*pp)->get_size(); 123 | } 124 | return 0; 125 | } 126 | 127 | int mkfs(struct exfat_dev* dev, off_t volume_size) 128 | { 129 | if (check_size(volume_size) != 0) 130 | return 1; 131 | 132 | fputs("Creating... ", stdout); 133 | fflush(stdout); 134 | if (erase(dev) != 0) 135 | return 1; 136 | if (create(dev) != 0) 137 | return 1; 138 | puts("done."); 139 | 140 | fputs("Flushing... ", stdout); 141 | fflush(stdout); 142 | if (exfat_fsync(dev) != 0) 143 | return 1; 144 | puts("done."); 145 | 146 | return 0; 147 | } 148 | 149 | off_t get_position(const struct fs_object* object) 150 | { 151 | const struct fs_object** pp; 152 | off_t position = 0; 153 | 154 | for (pp = objects; *pp; pp++) 155 | { 156 | position = ROUND_UP(position, (*pp)->get_alignment()); 157 | if (*pp == object) 158 | return position; 159 | position += (*pp)->get_size(); 160 | } 161 | exfat_bug("unknown object"); 162 | } 163 | -------------------------------------------------------------------------------- /mkfs/mkexfat.h: -------------------------------------------------------------------------------- 1 | /* 2 | mkexfat.h (09.11.10) 3 | FS creation engine. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_MKEXFAT_H_INCLUDED 24 | #define MKFS_MKEXFAT_H_INCLUDED 25 | 26 | #include 27 | 28 | struct fs_object 29 | { 30 | off_t (*get_alignment)(void); 31 | off_t (*get_size)(void); 32 | int (*write)(struct exfat_dev* dev); 33 | }; 34 | 35 | extern const struct fs_object* objects[]; 36 | 37 | int get_sector_bits(void); 38 | int get_spc_bits(void); 39 | off_t get_volume_size(void); 40 | const le16_t* get_volume_label(void); 41 | uint32_t get_volume_serial(void); 42 | uint64_t get_first_sector(void); 43 | int get_sector_size(void); 44 | int get_cluster_size(void); 45 | 46 | int mkfs(struct exfat_dev* dev, off_t volume_size); 47 | off_t get_position(const struct fs_object* object); 48 | 49 | #endif /* ifndef MKFS_MKEXFAT_H_INCLUDED */ 50 | -------------------------------------------------------------------------------- /mkfs/mkexfatfs.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2018 Andrew Nayenko 2 | .\" 3 | .TH MKEXFATFS 8 "January 2014" 4 | .SH NAME 5 | .B mkexfatfs 6 | \- create an exFAT file system 7 | .SH SYNOPSIS 8 | .B mkexfatfs 9 | [ 10 | .B \-i 11 | .I volume-id 12 | ] 13 | [ 14 | .B \-n 15 | .I volume-name 16 | ] 17 | [ 18 | .B \-p 19 | .I partition-first-sector 20 | ] 21 | [ 22 | .B \-s 23 | .I sectors-per-cluster 24 | ] 25 | [ 26 | .B \-V 27 | ] 28 | .I device 29 | 30 | .SH DESCRIPTION 31 | .B mkexfatfs 32 | creates an exFAT file system on a block device. 33 | .I device 34 | is a special file corresponding to the partition on the device. Note that if 35 | this is an MBR partition then the file system type should be set to 0x07 36 | (NTFS/exFAT) otherwise other operating systems may refuse to mount the 37 | file system. 38 | 39 | .SH OPTIONS 40 | Command line options available: 41 | .TP 42 | .BI \-i " volume-id" 43 | A 32-bit hexadecimal number. By default a value based on current time is set. 44 | .TP 45 | .BI \-n " volume-name" 46 | Volume name (label), up to 15 characters. By default no label is set. 47 | .TP 48 | .BI \-p " partition-first-sector" 49 | First sector of the partition starting from the beginning of the whole disk. 50 | exFAT super block has a field for this value but in fact it's optional and 51 | does not affect anything. Default is 0. 52 | .TP 53 | .BI \-s " sectors-per-cluster" 54 | Number of physical sectors per cluster (cluster is an allocation unit in 55 | exFAT). Must be a power of 2, i.e. 1, 2, 4, 8, etc. Cluster size can not 56 | exceed 32 MB. Default cluster sizes are: 57 | 4 KB if volume size is less than 256 MB, 58 | 32 KB if volume size is from 256 MB to 32 GB, 59 | 128 KB if volume size is 32 GB or larger. 60 | .TP 61 | .BI \-V 62 | Print version and copyright. 63 | 64 | .SH EXIT CODES 65 | Zero is returned on successful creation. Any other code means an error. 66 | 67 | .SH AUTHOR 68 | Andrew Nayenko 69 | 70 | .SH SEE ALSO 71 | .BR mkfs (8), fdisk (8) 72 | -------------------------------------------------------------------------------- /mkfs/rootdir.c: -------------------------------------------------------------------------------- 1 | /* 2 | rootdir.c (09.11.10) 3 | Root directory creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "rootdir.h" 24 | #include "uct.h" 25 | #include "cbm.h" 26 | #include "uctc.h" 27 | #include 28 | 29 | static off_t rootdir_alignment(void) 30 | { 31 | return get_cluster_size(); 32 | } 33 | 34 | static off_t rootdir_size(void) 35 | { 36 | return get_cluster_size(); 37 | } 38 | 39 | static void init_label_entry(struct exfat_entry_label* label_entry) 40 | { 41 | memset(label_entry, 0, sizeof(struct exfat_entry_label)); 42 | label_entry->type = EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID; 43 | 44 | if (utf16_length(get_volume_label()) == 0) 45 | return; 46 | 47 | memcpy(label_entry->name, get_volume_label(), 48 | EXFAT_ENAME_MAX * sizeof(le16_t)); 49 | label_entry->length = utf16_length(get_volume_label()); 50 | label_entry->type |= EXFAT_ENTRY_VALID; 51 | } 52 | 53 | static void init_bitmap_entry(struct exfat_entry_bitmap* bitmap_entry) 54 | { 55 | memset(bitmap_entry, 0, sizeof(struct exfat_entry_bitmap)); 56 | bitmap_entry->type = EXFAT_ENTRY_BITMAP; 57 | bitmap_entry->start_cluster = cpu_to_le32(EXFAT_FIRST_DATA_CLUSTER); 58 | bitmap_entry->size = cpu_to_le64(cbm.get_size()); 59 | } 60 | 61 | static void init_upcase_entry(struct exfat_entry_upcase* upcase_entry) 62 | { 63 | size_t i; 64 | uint32_t sum = 0; 65 | 66 | for (i = 0; i < sizeof(upcase_table); i++) 67 | sum = ((sum << 31) | (sum >> 1)) + upcase_table[i]; 68 | 69 | memset(upcase_entry, 0, sizeof(struct exfat_entry_upcase)); 70 | upcase_entry->type = EXFAT_ENTRY_UPCASE; 71 | upcase_entry->checksum = cpu_to_le32(sum); 72 | upcase_entry->start_cluster = cpu_to_le32( 73 | (get_position(&uct) - get_position(&cbm)) / get_cluster_size() + 74 | EXFAT_FIRST_DATA_CLUSTER); 75 | upcase_entry->size = cpu_to_le64(sizeof(upcase_table)); 76 | } 77 | 78 | static int rootdir_write(struct exfat_dev* dev) 79 | { 80 | struct exfat_entry_label label_entry; 81 | struct exfat_entry_bitmap bitmap_entry; 82 | struct exfat_entry_upcase upcase_entry; 83 | 84 | init_label_entry(&label_entry); 85 | init_bitmap_entry(&bitmap_entry); 86 | init_upcase_entry(&upcase_entry); 87 | 88 | if (exfat_write(dev, &label_entry, sizeof(struct exfat_entry)) < 0) 89 | return 1; 90 | if (exfat_write(dev, &bitmap_entry, sizeof(struct exfat_entry)) < 0) 91 | return 1; 92 | if (exfat_write(dev, &upcase_entry, sizeof(struct exfat_entry)) < 0) 93 | return 1; 94 | return 0; 95 | } 96 | 97 | const struct fs_object rootdir = 98 | { 99 | .get_alignment = rootdir_alignment, 100 | .get_size = rootdir_size, 101 | .write = rootdir_write, 102 | }; 103 | -------------------------------------------------------------------------------- /mkfs/rootdir.h: -------------------------------------------------------------------------------- 1 | /* 2 | rootdir.h (09.11.10) 3 | Root directory creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_ROOTDIR_H_INCLUDED 24 | #define MKFS_ROOTDIR_H_INCLUDED 25 | 26 | #include "mkexfat.h" 27 | 28 | extern const struct fs_object rootdir; 29 | 30 | #endif /* ifndef MKFS_ROOTDIR_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /mkfs/uct.c: -------------------------------------------------------------------------------- 1 | /* 2 | uct.c (09.11.10) 3 | Upper Case Table creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "uct.h" 24 | #include "uctc.h" 25 | 26 | static off_t uct_alignment(void) 27 | { 28 | return get_cluster_size(); 29 | } 30 | 31 | static off_t uct_size(void) 32 | { 33 | return sizeof(upcase_table); 34 | } 35 | 36 | static int uct_write(struct exfat_dev* dev) 37 | { 38 | if (exfat_write(dev, upcase_table, sizeof(upcase_table)) < 0) 39 | { 40 | exfat_error("failed to write upcase table of %zu bytes", 41 | sizeof(upcase_table)); 42 | return 1; 43 | } 44 | return 0; 45 | } 46 | 47 | const struct fs_object uct = 48 | { 49 | .get_alignment = uct_alignment, 50 | .get_size = uct_size, 51 | .write = uct_write, 52 | }; 53 | -------------------------------------------------------------------------------- /mkfs/uct.h: -------------------------------------------------------------------------------- 1 | /* 2 | uct.h (09.11.10) 3 | Upper Case Table creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_UCT_H_INCLUDED 24 | #define MKFS_UCT_H_INCLUDED 25 | 26 | #include "mkexfat.h" 27 | 28 | extern const struct fs_object uct; 29 | 30 | #endif /* ifndef MKFS_UCT_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /mkfs/uctc.h: -------------------------------------------------------------------------------- 1 | /* 2 | uctc.h (30.10.10) 3 | Upper Case Table declaration. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_UCTC_H_INCLUDED 24 | #define MKFS_UCTC_H_INCLUDED 25 | 26 | #include 27 | 28 | extern uint8_t upcase_table[5836]; 29 | 30 | #endif /* ifndef MKFS_UCTC_H_INCLUDED */ 31 | -------------------------------------------------------------------------------- /mkfs/vbr.c: -------------------------------------------------------------------------------- 1 | /* 2 | vbr.c (09.11.10) 3 | Volume Boot Record creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #include "vbr.h" 24 | #include "fat.h" 25 | #include "cbm.h" 26 | #include "uct.h" 27 | #include "rootdir.h" 28 | #include 29 | 30 | static off_t vbr_alignment(void) 31 | { 32 | return get_sector_size(); 33 | } 34 | 35 | static off_t vbr_size(void) 36 | { 37 | return 12 * get_sector_size(); 38 | } 39 | 40 | static void init_sb(struct exfat_super_block* sb) 41 | { 42 | uint32_t clusters_max; 43 | uint32_t fat_sectors; 44 | 45 | clusters_max = get_volume_size() / get_cluster_size(); 46 | fat_sectors = DIV_ROUND_UP((off_t) clusters_max * sizeof(cluster_t), 47 | get_sector_size()); 48 | 49 | memset(sb, 0, sizeof(struct exfat_super_block)); 50 | sb->jump[0] = 0xeb; 51 | sb->jump[1] = 0x76; 52 | sb->jump[2] = 0x90; 53 | memcpy(sb->oem_name, "EXFAT ", sizeof(sb->oem_name)); 54 | sb->sector_start = cpu_to_le64(get_first_sector()); 55 | sb->sector_count = cpu_to_le64(get_volume_size() / get_sector_size()); 56 | sb->fat_sector_start = cpu_to_le32( 57 | fat.get_alignment() / get_sector_size()); 58 | sb->fat_sector_count = cpu_to_le32(ROUND_UP( 59 | le32_to_cpu(sb->fat_sector_start) + fat_sectors, 60 | 1 << get_spc_bits()) - 61 | le32_to_cpu(sb->fat_sector_start)); 62 | sb->cluster_sector_start = cpu_to_le32( 63 | get_position(&cbm) / get_sector_size()); 64 | sb->cluster_count = cpu_to_le32(clusters_max - 65 | ((le32_to_cpu(sb->fat_sector_start) + 66 | le32_to_cpu(sb->fat_sector_count)) >> get_spc_bits())); 67 | sb->rootdir_cluster = cpu_to_le32( 68 | (get_position(&rootdir) - get_position(&cbm)) / get_cluster_size() 69 | + EXFAT_FIRST_DATA_CLUSTER); 70 | sb->volume_serial = cpu_to_le32(get_volume_serial()); 71 | sb->version.major = 1; 72 | sb->version.minor = 0; 73 | sb->volume_state = cpu_to_le16(0); 74 | sb->sector_bits = get_sector_bits(); 75 | sb->spc_bits = get_spc_bits(); 76 | sb->fat_count = 1; 77 | sb->drive_no = 0x80; 78 | sb->allocated_percent = 0; 79 | sb->boot_signature = cpu_to_le16(0xaa55); 80 | } 81 | 82 | static int vbr_write(struct exfat_dev* dev) 83 | { 84 | struct exfat_super_block sb; 85 | uint32_t checksum; 86 | le32_t* sector = malloc(get_sector_size()); 87 | size_t i; 88 | 89 | if (sector == NULL) 90 | { 91 | exfat_error("failed to allocate sector-sized block of memory"); 92 | return 1; 93 | } 94 | 95 | init_sb(&sb); 96 | if (exfat_write(dev, &sb, sizeof(struct exfat_super_block)) < 0) 97 | { 98 | free(sector); 99 | exfat_error("failed to write super block sector"); 100 | return 1; 101 | } 102 | checksum = exfat_vbr_start_checksum(&sb, sizeof(struct exfat_super_block)); 103 | 104 | memset(sector, 0, get_sector_size()); 105 | sector[get_sector_size() / sizeof(sector[0]) - 1] = 106 | cpu_to_le32(0xaa550000); 107 | for (i = 0; i < 8; i++) 108 | { 109 | if (exfat_write(dev, sector, get_sector_size()) < 0) 110 | { 111 | free(sector); 112 | exfat_error("failed to write a sector with boot signature"); 113 | return 1; 114 | } 115 | checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum); 116 | } 117 | 118 | memset(sector, 0, get_sector_size()); 119 | for (i = 0; i < 2; i++) 120 | { 121 | if (exfat_write(dev, sector, get_sector_size()) < 0) 122 | { 123 | free(sector); 124 | exfat_error("failed to write an empty sector"); 125 | return 1; 126 | } 127 | checksum = exfat_vbr_add_checksum(sector, get_sector_size(), checksum); 128 | } 129 | 130 | for (i = 0; i < get_sector_size() / sizeof(sector[0]); i++) 131 | sector[i] = cpu_to_le32(checksum); 132 | if (exfat_write(dev, sector, get_sector_size()) < 0) 133 | { 134 | free(sector); 135 | exfat_error("failed to write checksum sector"); 136 | return 1; 137 | } 138 | 139 | free(sector); 140 | return 0; 141 | } 142 | 143 | const struct fs_object vbr = 144 | { 145 | .get_alignment = vbr_alignment, 146 | .get_size = vbr_size, 147 | .write = vbr_write, 148 | }; 149 | -------------------------------------------------------------------------------- /mkfs/vbr.h: -------------------------------------------------------------------------------- 1 | /* 2 | vbr.h (09.11.10) 3 | Volume Boot Record creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2018 Andrew Nayenko 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License along 19 | with this program; if not, write to the Free Software Foundation, Inc., 20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 | */ 22 | 23 | #ifndef MKFS_VBR_H_INCLUDED 24 | #define MKFS_VBR_H_INCLUDED 25 | 26 | #include "mkexfat.h" 27 | 28 | extern const struct fs_object vbr; 29 | 30 | #endif /* ifndef MKFS_VBR_H_INCLUDED */ 31 | --------------------------------------------------------------------------------