├── .gitignore ├── Makefile.am ├── label ├── exfatlabel.8 ├── Makefile.am └── main.c ├── mkfs ├── cbm.h ├── uct.h ├── uctc.h ├── vbr.h ├── fat.h ├── rootdir.h ├── uct.c ├── Makefile.am ├── mkexfat.h ├── mkexfatfs.8 ├── cbm.c ├── fat.c ├── rootdir.c ├── mkexfat.c ├── vbr.c └── main.c ├── fsck ├── exfatfsck.8 ├── Makefile.am └── main.c ├── dump ├── dumpexfat.8 ├── Makefile.am └── main.c ├── attrib ├── Makefile.am ├── exfatattrib.8 └── main.c ├── libexfat ├── Makefile.am ├── compiler.h ├── byteorder.h ├── platform.h ├── repair.c ├── log.c ├── lookup.c ├── time.c ├── utils.c ├── utf.c ├── exfatfs.h ├── exfat.h ├── mount.c ├── io.c └── cluster.c ├── fuse ├── Makefile.am ├── mount.exfat-fuse.8 └── main.c ├── Android.bp ├── configure.ac ├── .cirrus.yml ├── README.md ├── ChangeLog └── COPYING /.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 | /attrib/exfatattrib 24 | /dump/dumpexfat 25 | /fsck/exfatfsck 26 | /fuse/mount.exfat-fuse 27 | /label/exfatlabel 28 | /libexfat/libexfat.a 29 | /mkfs/mkexfatfs 30 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2023 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 attrib dump fsck fuse label mkfs 24 | -------------------------------------------------------------------------------- /label/exfatlabel.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /mkfs/cbm.h: -------------------------------------------------------------------------------- 1 | /* 2 | cbm.h (09.11.10) 3 | Clusters Bitmap creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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/uct.h: -------------------------------------------------------------------------------- 1 | /* 2 | uct.h (09.11.10) 3 | Upper Case Table creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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-2023 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.h: -------------------------------------------------------------------------------- 1 | /* 2 | vbr.h (09.11.10) 3 | Volume Boot Record creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /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-2023 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/rootdir.h: -------------------------------------------------------------------------------- 1 | /* 2 | rootdir.h (09.11.10) 3 | Root directory creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /fsck/exfatfsck.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /dump/dumpexfat.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2023 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/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2023 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 = -imacros $(top_srcdir)/libexfat/config.h 27 | dumpexfat_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 28 | dumpexfat_LDADD = $(top_srcdir)/libexfat/libexfat.a $(UBLIO_LIBS) 29 | -------------------------------------------------------------------------------- /label/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2023 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 = -imacros $(top_srcdir)/libexfat/config.h 27 | exfatlabel_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 28 | exfatlabel_LDADD = $(top_srcdir)/libexfat/libexfat.a $(UBLIO_LIBS) 29 | -------------------------------------------------------------------------------- /attrib/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (08.11.20) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2020 Endless OS Foundation LLC 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 = exfatattrib 24 | dist_man8_MANS = exfatattrib.8 25 | exfatattrib_SOURCES = main.c 26 | exfatattrib_CPPFLAGS = -imacros $(top_srcdir)/libexfat/config.h 27 | exfatattrib_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 28 | exfatattrib_LDADD = $(top_srcdir)/libexfat/libexfat.a $(UBLIO_LIBS) 29 | -------------------------------------------------------------------------------- /libexfat/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2023 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_a_CPPFLAGS = -imacros $(top_srcdir)/libexfat/config.h 41 | libexfat_a_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 42 | -------------------------------------------------------------------------------- /fsck/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2023 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 = -imacros $(top_srcdir)/libexfat/config.h 27 | exfatfsck_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 28 | exfatfsck_LDADD = $(top_srcdir)/libexfat/libexfat.a $(UBLIO_LIBS) 29 | 30 | install-exec-hook: 31 | ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/fsck.exfat 32 | 33 | uninstall-hook: 34 | rm -f $(DESTDIR)$(sbindir)/fsck.exfat 35 | -------------------------------------------------------------------------------- /fuse/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2023 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 = -imacros $(top_srcdir)/libexfat/config.h 27 | mount_exfat_fuse_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 28 | mount_exfat_fuse_LDADD = $(top_srcdir)/libexfat/libexfat.a $(FUSE2_LIBS) $(FUSE3_LIBS) $(UBLIO_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 | -------------------------------------------------------------------------------- /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-2023 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/Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile.am (30.03.15) 3 | # Automake source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2011-2023 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 = -imacros $(top_srcdir)/libexfat/config.h 42 | mkexfatfs_CFLAGS = $(FUSE2_CFLAGS) $(FUSE3_CFLAGS) $(UBLIO_CFLAGS) 43 | mkexfatfs_LDADD = $(top_srcdir)/libexfat/libexfat.a $(UBLIO_LIBS) 44 | 45 | install-exec-hook: 46 | ln -sf $(sbin_PROGRAMS) $(DESTDIR)$(sbindir)/mkfs.exfat 47 | 48 | uninstall-hook: 49 | rm -f $(DESTDIR)$(sbindir)/mkfs.exfat 50 | -------------------------------------------------------------------------------- /mkfs/mkexfat.h: -------------------------------------------------------------------------------- 1 | /* 2 | mkexfat.h (09.11.10) 3 | FS creation engine. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /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-2023 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-2023 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 | -------------------------------------------------------------------------------- /attrib/exfatattrib.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2020-2023 Endless OS Foundation 2 | .\" 3 | .TH EXFATATTRIB 8 "November 2020" 4 | .SH NAME 5 | .B exfatattrib 6 | \- Set or display exFAT file attributes 7 | .SH SYNOPSIS 8 | .B exfatattrib 9 | [ 10 | .B \-r 11 | | 12 | .B \-R 13 | | 14 | .B \-i 15 | | 16 | .B \-I 17 | | 18 | .B \-s 19 | | 20 | .B \-S 21 | | 22 | .B \-a 23 | | 24 | .B \-A 25 | ] 26 | .B \-d 27 | .I device 28 | .I file 29 | .SH DESCRIPTION 30 | .B exfatattrib 31 | sets or displays attributes of a file on an exFAT file system. 32 | 33 | .I device 34 | is the path to an unmounted disk partition or disk image file containing an 35 | exFAT file system. 36 | 37 | .I file 38 | is the path of a file within that file system. 39 | 40 | If run with no command line options, the current attributes of 41 | .I file 42 | are displayed; otherwise, the specified attributes are set or cleared. It is an 43 | error to set and clear the same flag. 44 | 45 | .SH COMMAND LINE OPTIONS 46 | Command line options available: 47 | 48 | .TP 49 | .BI \-d " device" 50 | The path to an unmounted disk partition or disk image file containing an exFAT 51 | file system. This option is required. 52 | .TP 53 | .BI -r 54 | Set read\-only flag 55 | .TP 56 | .BI \-R 57 | Clear read\-only flag 58 | .TP 59 | .BI \-i 60 | Set hidden flag (mnemonic: \fBi\fRnvisible) 61 | .TP 62 | .BI \-I 63 | Clear hidden flag 64 | .TP 65 | .BI \-s 66 | Set system flag 67 | .TP 68 | .BI \-S 69 | Clear system flag 70 | .TP 71 | .BI \-a 72 | Set archive flag 73 | .TP 74 | .BI \-A 75 | Clear archive flag 76 | .TP 77 | .BI \-h 78 | Display this help message 79 | .TP 80 | .BI \-V 81 | Print version and copyright. 82 | 83 | .SH EXIT CODES 84 | Zero is returned if errors were not found. Any other code means an error. 85 | 86 | .SH AUTHOR 87 | Will Thompson 88 | 89 | .SH SEE ALSO 90 | .BR chmod(1) 91 | -------------------------------------------------------------------------------- /mkfs/mkexfatfs.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /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 | "-D_FILE_OFFSET_BITS=64", 27 | "-DPACKAGE=\"exfat\"", 28 | "-DVERSION=\"1.4.0\"", 29 | ], 30 | shared_libs: ["liblog"], 31 | } 32 | 33 | cc_library_static { 34 | name: "libexfat", 35 | 36 | srcs: ["libexfat/*.c"], 37 | defaults: ["exfat_defaults"], 38 | local_include_dirs: ["libexfat"], 39 | export_include_dirs: ["libexfat"], 40 | } 41 | 42 | 43 | cc_binary { 44 | name: "mkfs.exfat", 45 | 46 | srcs: ["mkfs/*.c"], 47 | defaults: ["exfat_defaults"], 48 | local_include_dirs: ["mkfs"], 49 | static_libs: ["libexfat"], 50 | } 51 | 52 | cc_binary { 53 | name: "fsck.exfat", 54 | 55 | srcs: ["fsck/main.c"], 56 | defaults: ["exfat_defaults"], 57 | local_include_dirs: ["fsck"], 58 | static_libs: ["libexfat"], 59 | } 60 | 61 | cc_binary { 62 | name: "dumpexfat", 63 | 64 | srcs: ["dump/main.c"], 65 | defaults: ["exfat_defaults"], 66 | local_include_dirs: ["dump"], 67 | static_libs: ["libexfat"], 68 | } 69 | 70 | cc_binary { 71 | name: "exfatlabel", 72 | 73 | srcs: ["label/main.c"], 74 | defaults: ["exfat_defaults"], 75 | local_include_dirs: ["label"], 76 | static_libs: ["libexfat"], 77 | } 78 | -------------------------------------------------------------------------------- /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-2023 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 | #define UNUSED __attribute__((unused)) 37 | #if __has_extension(c_static_assert) 38 | #define USE_C11_STATIC_ASSERT 39 | #endif 40 | 41 | #elif defined(__GNUC__) 42 | 43 | #define PRINTF __attribute__((format(printf, 1, 2))) 44 | #define NORETURN __attribute__((noreturn)) 45 | #define PACKED __attribute__((packed)) 46 | #define UNUSED __attribute__((unused)) 47 | #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) 48 | #define USE_C11_STATIC_ASSERT 49 | #endif 50 | 51 | #else 52 | 53 | #define PRINTF 54 | #define NORETURN 55 | #define PACKED 56 | #define UNUSED 57 | 58 | #endif 59 | 60 | #ifdef USE_C11_STATIC_ASSERT 61 | #define STATIC_ASSERT(cond) _Static_assert(cond, #cond) 62 | #else 63 | #define CONCAT2(a, b) a ## b 64 | #define CONCAT1(a, b) CONCAT2(a, b) 65 | #define STATIC_ASSERT(cond) \ 66 | extern void CONCAT1(static_assert, __LINE__)(int x[(cond) ? 1 : -1]) 67 | #endif 68 | 69 | #endif /* ifndef COMPILER_H_INCLUDED */ 70 | -------------------------------------------------------------------------------- /mkfs/cbm.c: -------------------------------------------------------------------------------- 1 | /* 2 | cbm.c (09.11.10) 3 | Clusters Bitmap creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # configure.ac (30.03.15) 3 | # Autoconf source. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2023 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.4.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_RANLIB 31 | AM_PROG_AR 32 | AC_SYS_LARGEFILE 33 | AC_CANONICAL_HOST 34 | PKG_CHECK_MODULES([FUSE3], [fuse3], 35 | [AC_DEFINE([FUSE_USE_VERSION], [30], [Required FUSE API version.])], 36 | [PKG_CHECK_MODULES([FUSE2], [fuse >= 2.6], 37 | [AC_DEFINE([FUSE_USE_VERSION], [26], [Required FUSE API version.])])]) 38 | AC_MSG_CHECKING([whether host-specific configuration is needed for $host_os]) 39 | case "$host_os" in 40 | darwin*) 41 | AC_MSG_RESULT([yes]) 42 | AC_DEFINE([FUSE_DARWIN_ENABLE_EXTENSIONS], [0], [macFUSE extensions break compatibility with the mainline FUSE.]) 43 | ;; 44 | freebsd*) 45 | AC_MSG_RESULT([yes]) 46 | PKG_CHECK_MODULES([UBLIO], [libublio]) 47 | AC_DEFINE([USE_UBLIO], [1], [Define if block devices are not supported.]) 48 | ;; 49 | *-gnu) 50 | AC_MSG_RESULT([yes]) 51 | AC_DEFINE([_XOPEN_SOURCE], [500], [Enable pread() and pwrite().]) 52 | AC_DEFINE([_DEFAULT_SOURCE], [], [Enable vsyslog().]) 53 | ;; 54 | *) 55 | AC_MSG_RESULT([no]) 56 | ;; 57 | esac 58 | AC_CONFIG_HEADERS([libexfat/config.h]) 59 | AC_CONFIG_FILES([ 60 | libexfat/Makefile 61 | attrib/Makefile 62 | dump/Makefile 63 | fsck/Makefile 64 | fuse/Makefile 65 | label/Makefile 66 | mkfs/Makefile 67 | Makefile]) 68 | AC_OUTPUT 69 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | # 2 | # .cirrus.yml (29.06.21) 3 | # Travis Contiguous Integration configuration. 4 | # 5 | # Free exFAT implementation. 6 | # Copyright (C) 2010-2021 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 | --- 24 | task: 25 | matrix: 26 | - name: debian-fuse2 27 | container: 28 | image: gcc:12-bullseye 29 | install_script: | 30 | apt-get update 31 | apt-get install -y libfuse-dev 32 | env: 33 | EXTRA_CFLAGS: -fanalyzer 34 | 35 | - name: debian-fuse3 36 | container: 37 | image: gcc:12-bullseye 38 | install_script: | 39 | apt-get update 40 | apt-get install -y libfuse3-dev 41 | env: 42 | EXTRA_CFLAGS: -fanalyzer 43 | 44 | - name: macos 45 | macos_instance: 46 | image: ghcr.io/cirruslabs/macos-runner:sequoia 47 | install_script: | 48 | brew update 49 | brew install automake macfuse 50 | 51 | - name: freebsd 52 | freebsd_instance: 53 | image_family: freebsd-14-2 54 | install_script: | 55 | pkg install -y autoconf automake fusefs-libs libublio pkgconf 56 | 57 | compile_script: | 58 | autoreconf --install 59 | ./configure CFLAGS="-Wall -Wextra -Werror \ 60 | -fsanitize=address,undefined \ 61 | $EXTRA_CFLAGS" 62 | make -k 63 | 64 | test_script: | 65 | dd if=/dev/zero of=foo.img bs=1048576 count=1 66 | mkfs/mkexfatfs -i 12345678 foo.img 67 | fsck/exfatfsck foo.img 68 | echo 'f1b3a11f781533f5b69086596be38367d0ebfb77 foo.img' | shasum -c 69 | label/exfatlabel foo.img ○○○○○○○○○○○○○○○ 70 | echo '4720462db953d5b2094c884f4b3098a138790533 foo.img' | shasum -c 71 | -------------------------------------------------------------------------------- /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-2023 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-2023 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 | #elif defined(__sun) 60 | 61 | #include 62 | #define exfat_bswap16(x) bswap_16(x) 63 | #define exfat_bswap32(x) bswap_32(x) 64 | #define exfat_bswap64(x) bswap_64(x) 65 | #define EXFAT_BYTE_ORDER __BYTE_ORDER 66 | #define EXFAT_LITTLE_ENDIAN __LITTLE_ENDIAN 67 | #define EXFAT_BIG_ENDIAN __BIG_ENDIAN 68 | 69 | #else 70 | #error Unknown platform 71 | #endif 72 | 73 | #endif /* ifndef PLATFORM_H_INCLUDED */ 74 | -------------------------------------------------------------------------------- /fuse/mount.exfat-fuse.8: -------------------------------------------------------------------------------- 1 | .\" Copyright (C) 2010-2023 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 | -------------------------------------------------------------------------------- /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-2023 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 | -------------------------------------------------------------------------------- /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-2023 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(UNUSED 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 | -------------------------------------------------------------------------------- /mkfs/rootdir.c: -------------------------------------------------------------------------------- 1 | /* 2 | rootdir.c (09.11.10) 3 | Root directory creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 (exfat_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 = exfat_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 | -------------------------------------------------------------------------------- /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-2023 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, aq; 119 | 120 | va_start(ap, format); 121 | va_copy(aq, ap); 122 | 123 | fflush(stdout); 124 | fputs("DEBUG: ", stderr); 125 | vfprintf(stderr, format, ap); 126 | va_end(ap); 127 | fputs(".\n", stderr); 128 | 129 | #ifdef __ANDROID__ 130 | __android_log_vprint(ANDROID_LOG_DEBUG, PACKAGE, format, aq); 131 | #else 132 | if (!isatty(STDERR_FILENO)) 133 | vsyslog(LOG_DEBUG, format, aq); 134 | #endif 135 | va_end(aq); 136 | } 137 | -------------------------------------------------------------------------------- /mkfs/mkexfat.c: -------------------------------------------------------------------------------- 1 | /* 2 | mkexfat.c (22.04.12) 3 | FS creation engine. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | off_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 off_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"); 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/vbr.c: -------------------------------------------------------------------------------- 1 | /* 2 | vbr.c (09.11.10) 3 | Volume Boot Record creation code. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | -------------------------------------------------------------------------------- /attrib/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (08.11.20) 3 | Prints or changes exFAT file attributes 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2020-2023 Endless OS Foundation LLC 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 | 28 | static void usage(const char* prog) 29 | { 30 | fprintf(stderr, 31 | "Display current attributes:\n" 32 | " %1$s -d \n" 33 | "\n" 34 | "Set attributes:\n" 35 | " %1$s [FLAGS] -d \n" 36 | "\n" 37 | "Flags:\n" 38 | " -r Set read-only flag\n" 39 | " -R Clear read-only flag\n" 40 | " -i Set hidden flag\n" 41 | " -I Clear hidden flag\n" 42 | " -s Set system flag\n" 43 | " -S Clear system flag\n" 44 | " -a Set archive flag\n" 45 | " -A Clear archive flag\n" 46 | "\n" 47 | " -h Display this help message\n" 48 | " -V Display version information\n", 49 | prog); 50 | exit(1); 51 | } 52 | 53 | static void print_attribute(uint16_t attribs, uint16_t attrib, 54 | const char* label) 55 | { 56 | printf("%9s: %s\n", label, (attribs & attrib) ? "yes" : "no"); 57 | } 58 | 59 | static int attribute(struct exfat* ef, struct exfat_node* node, 60 | uint16_t add_flags, uint16_t clear_flags) 61 | { 62 | if ((add_flags | clear_flags) != 0) 63 | { 64 | uint16_t attrib = node->attrib; 65 | 66 | attrib |= add_flags; 67 | attrib &= ~clear_flags; 68 | 69 | if (node->attrib != attrib) 70 | { 71 | int ret; 72 | 73 | node->attrib = attrib; 74 | node->is_dirty = true; 75 | 76 | ret = exfat_flush_node(ef, node); 77 | if (ret != 0) 78 | { 79 | char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]; 80 | 81 | exfat_get_name(node, buffer); 82 | exfat_error("failed to flush changes to '%s': %s", buffer, 83 | strerror(-ret)); 84 | return 1; 85 | } 86 | } 87 | } 88 | else 89 | { 90 | print_attribute(node->attrib, EXFAT_ATTRIB_RO, "Read-only"); 91 | print_attribute(node->attrib, EXFAT_ATTRIB_HIDDEN, "Hidden"); 92 | print_attribute(node->attrib, EXFAT_ATTRIB_SYSTEM, "System"); 93 | print_attribute(node->attrib, EXFAT_ATTRIB_ARCH, "Archive"); 94 | /* read-only attributes */ 95 | print_attribute(node->attrib, EXFAT_ATTRIB_VOLUME, "Volume"); 96 | print_attribute(node->attrib, EXFAT_ATTRIB_DIR, "Directory"); 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | int main(int argc, char* argv[]) 103 | { 104 | int opt; 105 | int ret; 106 | const char* spec = NULL; 107 | const char* options = ""; 108 | const char* file_path = NULL; 109 | struct exfat ef; 110 | struct exfat_node* node; 111 | uint16_t add_flags = 0; 112 | uint16_t clear_flags = 0; 113 | 114 | while ((opt = getopt(argc, argv, "d:rRiIsSaAhV")) != -1) 115 | { 116 | switch (opt) 117 | { 118 | case 'V': 119 | printf("exfatattrib %s\n", VERSION); 120 | puts("Copyright (C) 2011-2023 Andrew Nayenko"); 121 | puts("Copyright (C) 2020-2023 Endless OS Foundation LLC"); 122 | return 0; 123 | /* 124 | The path to the unmounted exFAT partition is a (mandatory) named 125 | option rather than a positional parameter. If the FUSE file system 126 | ever gains an ioctl to get and set attributes, this option could be 127 | made optional, and this tool taught to use the ioctl. 128 | */ 129 | case 'd': 130 | spec = optarg; 131 | break; 132 | case 'r': 133 | add_flags |= EXFAT_ATTRIB_RO; 134 | break; 135 | case 'R': 136 | clear_flags |= EXFAT_ATTRIB_RO; 137 | break; 138 | /* "-h[elp]" is taken; i is the second letter of "hidden" and 139 | its synonym "invisible" */ 140 | case 'i': 141 | add_flags |= EXFAT_ATTRIB_HIDDEN; 142 | break; 143 | case 'I': 144 | clear_flags |= EXFAT_ATTRIB_HIDDEN; 145 | break; 146 | case 's': 147 | add_flags |= EXFAT_ATTRIB_SYSTEM; 148 | break; 149 | case 'S': 150 | clear_flags |= EXFAT_ATTRIB_SYSTEM; 151 | break; 152 | case 'a': 153 | add_flags |= EXFAT_ATTRIB_ARCH; 154 | break; 155 | case 'A': 156 | clear_flags |= EXFAT_ATTRIB_ARCH; 157 | break; 158 | default: 159 | usage(argv[0]); 160 | } 161 | } 162 | 163 | if ((add_flags & clear_flags) != 0) 164 | { 165 | exfat_error("can't set and clear the same flag"); 166 | return 1; 167 | } 168 | 169 | if (spec == NULL || argc - optind != 1) 170 | usage(argv[0]); 171 | 172 | file_path = argv[optind]; 173 | 174 | if ((add_flags | clear_flags) == 0) 175 | options = "ro"; 176 | 177 | ret = exfat_mount(&ef, spec, options); 178 | if (ret != 0) 179 | { 180 | exfat_error("failed to mount %s: %s", spec, strerror(-ret)); 181 | return 1; 182 | } 183 | 184 | ret = exfat_lookup(&ef, &node, file_path); 185 | if (ret != 0) 186 | { 187 | exfat_error("failed to look up '%s': %s", file_path, strerror(-ret)); 188 | exfat_unmount(&ef); 189 | return 1; 190 | } 191 | 192 | ret = attribute(&ef, node, add_flags, clear_flags); 193 | 194 | exfat_put_node(&ef, node); 195 | exfat_unmount(&ef); 196 | 197 | return ret; 198 | } 199 | -------------------------------------------------------------------------------- /fsck/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (02.09.09) 3 | exFAT file system checker. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 | #define exfat_debug(format, ...) do {} while (0) 31 | 32 | uint64_t files_count, directories_count; 33 | 34 | static int nodeck(struct exfat* ef, struct exfat_node* node) 35 | { 36 | const cluster_t cluster_size = CLUSTER_SIZE(*ef->sb); 37 | cluster_t clusters = DIV_ROUND_UP(node->size, cluster_size); 38 | cluster_t c = node->start_cluster; 39 | int rc = 0; 40 | 41 | while (clusters--) 42 | { 43 | if (CLUSTER_INVALID(*ef->sb, c)) 44 | { 45 | char name[EXFAT_UTF8_NAME_BUFFER_MAX]; 46 | 47 | exfat_get_name(node, name); 48 | exfat_error("file '%s' has invalid cluster 0x%x", name, c); 49 | rc = 1; 50 | break; 51 | } 52 | if (BMAP_GET(ef->cmap.chunk, c - EXFAT_FIRST_DATA_CLUSTER) == 0) 53 | { 54 | char name[EXFAT_UTF8_NAME_BUFFER_MAX]; 55 | 56 | exfat_get_name(node, name); 57 | exfat_error("cluster 0x%x of file '%s' is not allocated", c, name); 58 | rc = 1; 59 | } 60 | c = exfat_next_cluster(ef, node, c); 61 | } 62 | return rc; 63 | } 64 | 65 | static void dirck(struct exfat* ef, const char* path) 66 | { 67 | struct exfat_node* parent; 68 | struct exfat_node* node; 69 | struct exfat_iterator it; 70 | int rc; 71 | size_t path_length; 72 | char* entry_path; 73 | 74 | if (exfat_lookup(ef, &parent, path) != 0) 75 | exfat_bug("directory '%s' is not found", path); 76 | if (!(parent->attrib & EXFAT_ATTRIB_DIR)) 77 | exfat_bug("'%s' is not a directory (%#hx)", path, parent->attrib); 78 | if (nodeck(ef, parent) != 0) 79 | { 80 | exfat_put_node(ef, parent); 81 | return; 82 | } 83 | 84 | path_length = strlen(path); 85 | entry_path = malloc(path_length + 1 + EXFAT_UTF8_NAME_BUFFER_MAX); 86 | if (entry_path == NULL) 87 | { 88 | exfat_put_node(ef, parent); 89 | exfat_error("out of memory"); 90 | return; 91 | } 92 | strcpy(entry_path, path); 93 | strcat(entry_path, "/"); 94 | 95 | rc = exfat_opendir(ef, parent, &it); 96 | if (rc != 0) 97 | { 98 | free(entry_path); 99 | exfat_put_node(ef, parent); 100 | return; 101 | } 102 | while ((node = exfat_readdir(&it))) 103 | { 104 | exfat_get_name(node, entry_path + path_length + 1); 105 | exfat_debug("%s: %s, %"PRIu64" bytes, cluster %u", entry_path, 106 | node->is_contiguous ? "contiguous" : "fragmented", 107 | node->size, node->start_cluster); 108 | if (node->attrib & EXFAT_ATTRIB_DIR) 109 | { 110 | directories_count++; 111 | dirck(ef, entry_path); 112 | } 113 | else 114 | { 115 | files_count++; 116 | nodeck(ef, node); 117 | } 118 | exfat_flush_node(ef, node); 119 | exfat_put_node(ef, node); 120 | } 121 | exfat_closedir(ef, &it); 122 | exfat_flush_node(ef, parent); 123 | exfat_put_node(ef, parent); 124 | free(entry_path); 125 | } 126 | 127 | static bool fsck(struct exfat* ef, const char* spec, const char* options) 128 | { 129 | int rc; 130 | 131 | rc = exfat_mount(ef, spec, options); 132 | if (rc == -ENODEV) 133 | return false; /* failed to open the device, checking haven't started */ 134 | if (rc != 0) 135 | { 136 | fputs("File system checking stopped. ", stdout); 137 | return true; 138 | } 139 | 140 | exfat_print_info(ef->sb, exfat_count_free_clusters(ef)); 141 | exfat_soil_super_block(ef); 142 | dirck(ef, ""); 143 | exfat_unmount(ef); 144 | 145 | printf("Totally %"PRIu64" directories and %"PRIu64" files.\n", 146 | directories_count, files_count); 147 | fputs("File system checking finished. ", stdout); 148 | return true; 149 | } 150 | 151 | static void usage(const char* prog) 152 | { 153 | fprintf(stderr, "Usage: %s [-a | -n | -p | -y] \n", prog); 154 | fprintf(stderr, " %s -V\n", prog); 155 | exit(1); 156 | } 157 | 158 | int main(int argc, char* argv[]) 159 | { 160 | int opt; 161 | const char* options; 162 | const char* spec = NULL; 163 | struct exfat ef; 164 | 165 | printf("exfatfsck %s\n", VERSION); 166 | 167 | if (isatty(STDIN_FILENO)) 168 | options = "repair=1"; 169 | else 170 | options = "repair=0"; 171 | 172 | while ((opt = getopt(argc, argv, "anpVy")) != -1) 173 | { 174 | switch (opt) 175 | { 176 | case 'a': 177 | case 'p': 178 | case 'y': 179 | options = "repair=2"; 180 | break; 181 | case 'n': 182 | options = "repair=0,ro"; 183 | break; 184 | case 'V': 185 | puts("Copyright (C) 2011-2023 Andrew Nayenko"); 186 | return 0; 187 | default: 188 | usage(argv[0]); 189 | break; 190 | } 191 | } 192 | if (argc - optind != 1) 193 | usage(argv[0]); 194 | spec = argv[optind]; 195 | 196 | printf("Checking file system on %s.\n", spec); 197 | if (!fsck(&ef, spec, options)) 198 | return 1; 199 | if (exfat_errors != 0) 200 | { 201 | printf("ERRORS FOUND: %d, FIXED: %d.\n", 202 | exfat_errors, exfat_errors_fixed); 203 | return 1; 204 | } 205 | puts("No errors found."); 206 | return 0; 207 | } 208 | -------------------------------------------------------------------------------- /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-2023 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 = exfat_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 = exfat_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/time.c: -------------------------------------------------------------------------------- 1 | /* 2 | time.c (03.02.12) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2023 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/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | utils.c (04.09.09) 3 | exFAT file system implementation library. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2010-2023 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 (exfat_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 | 182 | bool exfat_match_option(const char* options, const char* option_name) 183 | { 184 | const char* p; 185 | size_t length = strlen(option_name); 186 | 187 | for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name)) 188 | if ((p == options || p[-1] == ',') && 189 | (p[length] == ',' || p[length] == '\0')) 190 | return true; 191 | return false; 192 | } 193 | -------------------------------------------------------------------------------- /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-2023 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 exfat_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 | size_t size; 143 | size_t i; 144 | 145 | if (insize == 0) 146 | exfat_bug("no input for utf8_to_wchar"); 147 | 148 | if ((input[0] & 0x80) == 0) 149 | { 150 | *wc = (wchar_t) input[0]; 151 | return input + 1; 152 | } 153 | else if ((input[0] & 0xe0) == 0xc0) 154 | { 155 | *wc = ((wchar_t) input[0] & 0x1f) << 6; 156 | size = 2; 157 | } 158 | else if ((input[0] & 0xf0) == 0xe0) 159 | { 160 | *wc = ((wchar_t) input[0] & 0x0f) << 12; 161 | size = 3; 162 | } 163 | else if ((input[0] & 0xf8) == 0xf0) 164 | { 165 | *wc = ((wchar_t) input[0] & 0x07) << 18; 166 | size = 4; 167 | } 168 | else if ((input[0] & 0xfc) == 0xf8) 169 | { 170 | *wc = ((wchar_t) input[0] & 0x03) << 24; 171 | size = 5; 172 | } 173 | else if ((input[0] & 0xfe) == 0xfc) 174 | { 175 | *wc = ((wchar_t) input[0] & 0x01) << 30; 176 | size = 6; 177 | } 178 | else 179 | return NULL; 180 | 181 | if (insize < size) 182 | return NULL; 183 | 184 | /* the first byte is handled above */ 185 | for (i = 1; i < size; i++) 186 | { 187 | if ((input[i] & 0xc0) != 0x80) 188 | return NULL; 189 | *wc |= (input[i] & 0x3f) << ((size - i - 1) * 6); 190 | } 191 | 192 | return input + size; 193 | } 194 | 195 | static le16_t* wchar_to_utf16(le16_t* output, wchar_t wc, size_t outsize) 196 | { 197 | if (wc <= 0xffff) /* if character is from BMP */ 198 | { 199 | if (outsize == 0) 200 | return NULL; 201 | output[0] = cpu_to_le16(wc); 202 | return output + 1; 203 | } 204 | if (outsize < 2) 205 | return NULL; 206 | wc -= 0x10000; 207 | output[0] = cpu_to_le16(0xd800 | ((wc >> 10) & 0x3ff)); 208 | output[1] = cpu_to_le16(0xdc00 | (wc & 0x3ff)); 209 | return output + 2; 210 | } 211 | 212 | int exfat_utf8_to_utf16(le16_t* output, const char* input, size_t outsize, 213 | size_t insize) 214 | { 215 | const char* iptr = input; 216 | const char* iend = input + insize; 217 | le16_t* optr = output; 218 | const le16_t* oend = output + outsize; 219 | wchar_t wc; 220 | 221 | while (iptr < iend) 222 | { 223 | iptr = utf8_to_wchar(iptr, &wc, iend - iptr); 224 | if (iptr == NULL) 225 | { 226 | exfat_error("illegal UTF-8 sequence"); 227 | return -EILSEQ; 228 | } 229 | optr = wchar_to_utf16(optr, wc, oend - optr); 230 | if (optr == NULL) 231 | { 232 | exfat_error("name is too long"); 233 | return -ENAMETOOLONG; 234 | } 235 | if (wc == 0) 236 | break; 237 | } 238 | if (optr >= oend) 239 | { 240 | exfat_error("name is too long"); 241 | return -ENAMETOOLONG; 242 | } 243 | *optr = cpu_to_le16(0); 244 | return 0; 245 | } 246 | 247 | size_t exfat_utf16_length(const le16_t* str) 248 | { 249 | size_t i = 0; 250 | 251 | while (le16_to_cpu(str[i])) 252 | i++; 253 | return i; 254 | } 255 | -------------------------------------------------------------------------------- /mkfs/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c (15.08.10) 3 | Creates exFAT file system. 4 | 5 | Free exFAT implementation. 6 | Copyright (C) 2011-2023 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 exfat_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-2023 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 | -------------------------------------------------------------------------------- /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-2023 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 | -------------------------------------------------------------------------------- /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-2023 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-2023 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 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 1.4.0 (2023-03-02) 2 | 3 | * Added exfatattrib utility which prints or changes file attributes [Will 4 | Thompson]. 5 | * Added FUSE 3 support. FUSE 2 continues to be supported. 6 | * Added support for timezone offsets [Nathan Hunsperger]. 7 | * Fixed device opening error being reported as FS error. 8 | * Fixed possible data leakage when uninitialized clusters were allocated using 9 | NtSetInformationFile() call under Windows and FS was not unmounted correctly. 10 | * Improved compatibility with autoconf 2.70. 11 | * Fixed disk size reading on NetBSD [Dave Vasilevsky]. 12 | * Fixed "automounted" option handling on FreeBSD [Iouri V. Ivliev]. 13 | * Allowed to specify the "nonempty" mount option. 14 | * Fixed file data contiguity handling bug which could cause performance 15 | degradation [noctis]. 16 | * Fixed possible crashes on invalid UTF-8 sequences. 17 | * Fixed a situation when FS could be erroneously marked as dirty (not cleanly 18 | unmounted). 19 | 20 | 1.3.0 (2018-09-15) 21 | 22 | * exfatfsck can now repair some errors. 23 | * Added experimental Android support for exfat-utils [liminghao, LongPingWEI, 24 | Pablo Mendez Hernandez, Pierre-Hugues Husson]. 25 | * Cleaned up FUSE code preparing for FUSE 3 support. 26 | * Removed OpenBSD support as it does not handle -o option in fuse_main(). 27 | * Re-introduced FreeBSD support [Oleksii Samorukov]. 28 | * Fixed DragonFly BSD support [Tomohiro Kusumi]. 29 | * dirent->d_type in now filled on readdir() [Mark Browning]. 30 | 31 | 1.2.8 (2018-02-03) 32 | 33 | * Fixed new files or directories creation in the root directory: ensure there 34 | are no 0x00 entries before valid ones; otherwise Windows can corrupt them. 35 | * Fixed compilation on GNU/HURD platform. 36 | 37 | 1.2.7 (2017-06-05) 38 | 39 | * Fixed handling of two last clusters: operations with files that occupy these 40 | clusters could fail. 41 | * Fixed crash when started with stdin, stdout or stderr closed. 42 | 43 | 1.2.6 (2017-01-28) 44 | 45 | * Operations with directories (except initial listing) now make less 46 | read/write system calls. 47 | * Fixed handling of files with optional tail entries (0xe0-0xff): videoclip 48 | files created by Sony cameras were missing. 49 | * Write operations now correctly return ENOSPC (instead of EIO) when there is 50 | no free disk space left. 51 | * Fixed max file name length: it's 255 16-bit code units (not 256). 52 | 53 | 1.2.5 (2016-12-05) 54 | 55 | * Added an option for dumpexfat to show file fragments [Daniel Drake]. 56 | * Fixed crash when directory starts with an invalid cluster. 57 | * Daylight saving time in now properly reflected in file timestamps. 58 | 59 | 1.2.4 (2016-06-03) 60 | 61 | * Fixed wrong files names hashes when upper case table is compressed. 62 | * Man pages are now installed by default. 63 | * Commas and backslashes in device names are now escaped. 64 | 65 | 1.2.3 (2015-12-19) 66 | 67 | * Fixed clusters loss when file renaming replaces target. 68 | 69 | 1.2.2 (2015-11-09) 70 | 71 | * Improved reliability in case of a sudden unplug: FS will be in a clean state 72 | after closing all files and performing sync(1). 73 | * Fixed compilation on Debian GNU/kFreeBSD and GNU/Hurd platforms. 74 | * Updated mount.exfat-fuse man page. 75 | 76 | 1.2.1 (2015-09-24) 77 | 78 | * Fixed compatibility with Zalman VE-200: now newly created directories do not 79 | have archive bit set. 80 | * Fixed heap corruption: malformed FS can use invalid sector or cluster size. 81 | * Fixed hang on mount: malformed FS can have cyclic references in the clusters 82 | map. 83 | 84 | 1.2.0 (2015-08-26) 85 | 86 | * Switched from SCons to autotools. 87 | * Added musl libc support [Brendan Heading]. 88 | * Worked around "FS is larger than device" error for memory cards formatted by 89 | Panasonic Lumix cameras. 90 | * Worked around "unknown entry type 0xe1" error for memory cards formatted by 91 | Sony cameras. 92 | 93 | 1.1.1 (2014-11-15) 94 | 95 | * Fixed mkfs crash on some sectors-per-cluster (-s option) values. 96 | 97 | 1.1.0 (2014-07-08) 98 | 99 | * Relicensed the project from GPLv3+ to GPLv2+. 100 | * OpenBSD support [Helg Bredow]. 101 | * Improved I/O errors handling. 102 | * Implemented fsync() and fsyncdir(). 103 | * Fixed crash on Mac OS X 10.5 caused by non-standard use of realpath(). Also 104 | fixed TrueCrypt disks unmounting. 105 | * Avoid extra erase on writes to the end of a file. This should improve linear 106 | write speed. 107 | * Allow arbitrary changing of lower 9 bits of mode. Allow owner/group changing 108 | to the same owner/group. This fixes rsync. 109 | * Fixed buffers overflows when handling lengthy file names. 110 | * Fixed "real size does not equal to size" error on volumes with pagefile.sys. 111 | * Fixed negative IUsed in "df -i" output. 112 | 113 | 1.0.1 (2013-02-02) 114 | 115 | * Fixed unexpected removal of a directory if it is moved into itself. 116 | * Fixed "Operation not permitted" error on reading an empty file. 117 | 118 | 1.0.0 (2013-01-19) 119 | 120 | * Fixed crash when renaming a file within a single directory and a new name 121 | differs only in case. 122 | * Fixed clusters allocation: a cluster beyond valid clusters range could be 123 | allocated. 124 | * Fixed crash when a volume is unmounted while some files are open. 125 | * SConscript now respects AR and RANLIB environment variables. 126 | * Improved error handling. 127 | 128 | Linux: 129 | 130 | * Enabled big_writes. This improves write speed (larger block size means less 131 | switches between kernel- and user-space). 132 | * Do BLKROGET ioctl to make sure the device is not read-only: after 133 | "blockdev --setro" kernel still allows to open the device in read-write mode 134 | but fails writes. 135 | 136 | OS X: 137 | 138 | * Fixed OS X 10.8 support. 139 | * Switched to 64-bit inode numbers (now Mac OS X 10.5 or later is required). 140 | * Switched from unmaintained MacFUSE to OSXFUSE (http://osxfuse.github.com). 141 | * Fixed device size detection. Now mkfs works. 142 | * Workarounded some utilities failures due to missing chmod() support. 143 | * Disabled (senseless) permission checks made by FUSE. 144 | 145 | 0.9.8 (2012-08-09) 146 | 147 | * The mkfs utility can now create huge file systems (up to several exabytes). 148 | * Fixed handling of characters beyond Basic Multilingual Plane. 149 | * Echo messages to syslog only if stderr is not connected to a terminal. 150 | 151 | 0.9.7 (2012-03-08) 152 | 153 | * Out-of-the-box FreeBSD support (via ublio library). 154 | * Fixed "missing EOD entry" error (could happen while reading directory that 155 | consists of several clusters). 156 | * Fixed interpretation of minutes field in files timestamps (minutes could be 157 | displayed incorrectly). 158 | * Fixed mtime seconds field initialization for newly created file (mtime could 159 | be 1 sec less than creation time). 160 | * SConscript now respects CC, CCFLAGS and LDFLAGS environment variables. 161 | 162 | 0.9.6 (2012-01-14) 163 | 164 | * Fixed write performance regression introduced in 0.9.4. 165 | * Mount in read-only mode if the device is write-protected. 166 | * Set ctime to mtime to ensure we don't break programs that rely on ctime 167 | (e.g. rsync considered that all files are outdated) [Eldad Zack]. 168 | * Indicate that FS in not clean when it was not cleanly unmounted. 169 | * Utilities are now compatible with GNU/Hurd. 170 | * Fixed several memory leaks that could occur on error handling paths. 171 | * Improved handling of corrupted file systems. 172 | 173 | 0.9.5 (2011-05-15) 174 | 175 | * Fixed erasing of the root directory cluster when creating a new FS with 176 | mkexfatfs. This bug could cause mkexfatfs to produce invalid FS. 177 | * Utilities are not linked with libfuse anymore. 178 | * Ensure that the path being opened is either a device or a regular file. 179 | 180 | 0.9.4 (2011-03-05) 181 | 182 | * Introduced exfat-utils: dumpexfat, exfatfsck, mkexfatfs, exfatlabel. 183 | * Fixed "Invalid argument" error while mounting a volume from a disk with sector size greater than 512 bytes. 184 | * Wait for all data to be flushed to disk on unmount. 185 | * Kernel cache is no longer flushed on open. This can slightly improve read performance by avoiding extra read requests from kernel to user-space. 186 | * Allow to unmount volumes as user (fusermount -u) if they were mounted from the very same user [Tino Lange]. 187 | * Errors and warnings are now duplicated to syslog. 188 | 189 | 0.9.3 (2010-09-25) 190 | 191 | * Directories now can shrink. 192 | * Improved timestamps resolution from 2 sec to 1 sec. 193 | * Fixed timestamps displaying under Mac OS X when compiled for i386 or ppc. 194 | * Fixed FS size displaying for non-GNU systems. 195 | 196 | 0.9.2 (2010-07-24) 197 | 198 | * Fixed a bug which could cause the whole directory to become unreadable after renaming a file in it. 199 | * Support for Solaris and various *BSD [Albert Lee]. 200 | * Improved error handling on corrupted volumes. 201 | * Improved allowed file name characters filter. 202 | * Added man page. 203 | 204 | 0.9.1 (2010-06-12) 205 | 206 | * Implemented automounting (util-linux-ng 2.18 or later is required). 207 | * Fixed mounting when cluster bitmap is larger than expected. 208 | * Fixed crash on statfs() when root directory contains error. 209 | * Fixed bugs specific to big-endian machines. 210 | * Other bugfixes. 211 | 212 | 0.9.0 (2010-03-21) 213 | 214 | * Initial release. 215 | -------------------------------------------------------------------------------- /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-2023 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 | #include "compiler.h" 28 | #include "exfatfs.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define EXFAT_NAME_MAX 255 37 | /* UTF-16 encodes code points up to U+FFFF as single 16-bit code units. 38 | UTF-8 uses up to 3 bytes (i.e. 8-bit code units) to encode code points 39 | up to U+FFFF. One additional character is for null terminator. */ 40 | #define EXFAT_UTF8_NAME_BUFFER_MAX (EXFAT_NAME_MAX * 3 + 1) 41 | #define EXFAT_UTF8_ENAME_BUFFER_MAX (EXFAT_ENAME_MAX * 3 + 1) 42 | 43 | #define SECTOR_SIZE(sb) (1 << (sb).sector_bits) 44 | #define CLUSTER_SIZE(sb) (SECTOR_SIZE(sb) << (sb).spc_bits) 45 | #define CLUSTER_INVALID(sb, c) ((c) < EXFAT_FIRST_DATA_CLUSTER || \ 46 | (c) - EXFAT_FIRST_DATA_CLUSTER >= le32_to_cpu((sb).cluster_count)) 47 | 48 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 49 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 50 | #define DIV_ROUND_UP(x, d) (((x) + (d) - 1) / (d)) 51 | #define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d)) 52 | 53 | #define BMAP_SIZE(count) (ROUND_UP(count, sizeof(bitmap_t) * 8) / 8) 54 | #define BMAP_BLOCK(index) ((index) / sizeof(bitmap_t) / 8) 55 | #define BMAP_MASK(index) ((bitmap_t) 1 << ((index) % (sizeof(bitmap_t) * 8))) 56 | #define BMAP_GET(bitmap, index) \ 57 | ((bitmap)[BMAP_BLOCK(index)] & BMAP_MASK(index)) 58 | #define BMAP_SET(bitmap, index) \ 59 | ((bitmap)[BMAP_BLOCK(index)] |= BMAP_MASK(index)) 60 | #define BMAP_CLR(bitmap, index) \ 61 | ((bitmap)[BMAP_BLOCK(index)] &= ~BMAP_MASK(index)) 62 | 63 | #define EXFAT_REPAIR(hook, ef, ...) \ 64 | (exfat_ask_to_fix(ef) && exfat_fix_ ## hook(ef, __VA_ARGS__)) 65 | 66 | /* The size of off_t type must be 64 bits. File systems larger than 2 GB will 67 | be corrupted with 32-bit off_t. */ 68 | STATIC_ASSERT(sizeof(off_t) == 8); 69 | 70 | struct exfat_node 71 | { 72 | struct exfat_node* parent; 73 | struct exfat_node* child; 74 | struct exfat_node* next; 75 | struct exfat_node* prev; 76 | 77 | int references; 78 | uint32_t fptr_index; 79 | cluster_t fptr_cluster; 80 | off_t entry_offset; 81 | cluster_t start_cluster; 82 | uint16_t attrib; 83 | uint8_t continuations; 84 | bool is_contiguous : 1; 85 | bool is_cached : 1; 86 | bool is_dirty : 1; 87 | bool is_unlinked : 1; 88 | uint64_t valid_size; 89 | uint64_t size; 90 | time_t mtime, atime; 91 | le16_t name[EXFAT_NAME_MAX + 1]; 92 | }; 93 | 94 | enum exfat_mode 95 | { 96 | EXFAT_MODE_RO, 97 | EXFAT_MODE_RW, 98 | EXFAT_MODE_ANY, 99 | }; 100 | 101 | struct exfat_dev; 102 | 103 | struct exfat 104 | { 105 | struct exfat_dev* dev; 106 | struct exfat_super_block* sb; 107 | uint16_t* upcase; 108 | struct exfat_node* root; 109 | struct 110 | { 111 | cluster_t start_cluster; 112 | uint32_t size; /* in bits */ 113 | bitmap_t* chunk; 114 | uint32_t chunk_size; /* in bits */ 115 | bool dirty; 116 | } 117 | cmap; 118 | char label[EXFAT_UTF8_ENAME_BUFFER_MAX]; 119 | void* zero_cluster; 120 | int dmask, fmask; 121 | uid_t uid; 122 | gid_t gid; 123 | int ro; 124 | bool noatime; 125 | enum { EXFAT_REPAIR_NO, EXFAT_REPAIR_ASK, EXFAT_REPAIR_YES } repair; 126 | }; 127 | 128 | /* in-core nodes iterator */ 129 | struct exfat_iterator 130 | { 131 | struct exfat_node* parent; 132 | struct exfat_node* current; 133 | }; 134 | 135 | struct exfat_human_bytes 136 | { 137 | uint64_t value; 138 | const char* unit; 139 | }; 140 | 141 | extern int exfat_errors; 142 | extern int exfat_errors_fixed; 143 | 144 | void exfat_bug(const char* format, ...) PRINTF NORETURN; 145 | void exfat_error(const char* format, ...) PRINTF; 146 | void exfat_warn(const char* format, ...) PRINTF; 147 | void exfat_debug(const char* format, ...) PRINTF; 148 | 149 | struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode); 150 | int exfat_close(struct exfat_dev* dev); 151 | int exfat_fsync(struct exfat_dev* dev); 152 | enum exfat_mode exfat_get_mode(const struct exfat_dev* dev); 153 | off_t exfat_get_size(const struct exfat_dev* dev); 154 | off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence); 155 | ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size); 156 | ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size); 157 | ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, 158 | off_t offset); 159 | ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, 160 | off_t offset); 161 | ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, 162 | void* buffer, size_t size, off_t offset); 163 | ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, 164 | const void* buffer, size_t size, off_t offset); 165 | 166 | int exfat_opendir(struct exfat* ef, struct exfat_node* dir, 167 | struct exfat_iterator* it); 168 | void exfat_closedir(struct exfat* ef, struct exfat_iterator* it); 169 | struct exfat_node* exfat_readdir(struct exfat_iterator* it); 170 | int exfat_lookup(struct exfat* ef, struct exfat_node** node, 171 | const char* path); 172 | int exfat_split(struct exfat* ef, struct exfat_node** parent, 173 | struct exfat_node** node, le16_t* name, const char* path); 174 | 175 | off_t exfat_c2o(const struct exfat* ef, cluster_t cluster); 176 | cluster_t exfat_next_cluster(const struct exfat* ef, 177 | const struct exfat_node* node, cluster_t cluster); 178 | cluster_t exfat_advance_cluster(const struct exfat* ef, 179 | struct exfat_node* node, uint32_t count); 180 | int exfat_flush_nodes(struct exfat* ef); 181 | int exfat_flush(struct exfat* ef); 182 | int exfat_truncate(struct exfat* ef, struct exfat_node* node, uint64_t size, 183 | bool erase); 184 | uint32_t exfat_count_free_clusters(const struct exfat* ef); 185 | int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b); 186 | 187 | void exfat_stat(const struct exfat* ef, const struct exfat_node* node, 188 | struct stat* stbuf); 189 | void exfat_get_name(const struct exfat_node* node, 190 | char buffer[EXFAT_UTF8_NAME_BUFFER_MAX]); 191 | uint16_t exfat_start_checksum(const struct exfat_entry_meta1* entry); 192 | uint16_t exfat_add_checksum(const void* entry, uint16_t sum); 193 | le16_t exfat_calc_checksum(const struct exfat_entry* entries, int n); 194 | uint32_t exfat_vbr_start_checksum(const void* sector, size_t size); 195 | uint32_t exfat_vbr_add_checksum(const void* sector, size_t size, uint32_t sum); 196 | le16_t exfat_calc_name_hash(const struct exfat* ef, const le16_t* name, 197 | size_t length); 198 | void exfat_humanize_bytes(uint64_t value, struct exfat_human_bytes* hb); 199 | void exfat_print_info(const struct exfat_super_block* sb, 200 | uint32_t free_clusters); 201 | bool exfat_match_option(const char* options, const char* option_name); 202 | 203 | int exfat_utf16_to_utf8(char* output, const le16_t* input, size_t outsize, 204 | size_t insize); 205 | int exfat_utf8_to_utf16(le16_t* output, const char* input, size_t outsize, 206 | size_t insize); 207 | size_t exfat_utf16_length(const le16_t* str); 208 | 209 | struct exfat_node* exfat_get_node(struct exfat_node* node); 210 | void exfat_put_node(struct exfat* ef, struct exfat_node* node); 211 | int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node); 212 | int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir); 213 | void exfat_reset_cache(struct exfat* ef); 214 | int exfat_flush_node(struct exfat* ef, struct exfat_node* node); 215 | int exfat_unlink(struct exfat* ef, struct exfat_node* node); 216 | int exfat_rmdir(struct exfat* ef, struct exfat_node* node); 217 | int exfat_mknod(struct exfat* ef, const char* path); 218 | int exfat_mkdir(struct exfat* ef, const char* path); 219 | int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path); 220 | void exfat_utimes(struct exfat_node* node, const struct timespec tv[2]); 221 | void exfat_update_atime(struct exfat_node* node); 222 | void exfat_update_mtime(struct exfat_node* node); 223 | const char* exfat_get_label(struct exfat* ef); 224 | int exfat_set_label(struct exfat* ef, const char* label); 225 | 226 | int exfat_soil_super_block(const struct exfat* ef); 227 | int exfat_mount(struct exfat* ef, const char* spec, const char* options); 228 | void exfat_unmount(struct exfat* ef); 229 | 230 | time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec, 231 | uint8_t tzoffset); 232 | void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, 233 | uint8_t* centisec, uint8_t* tzoffset); 234 | void exfat_tzset(void); 235 | 236 | bool exfat_ask_to_fix(const struct exfat* ef); 237 | bool exfat_fix_invalid_vbr_checksum(const struct exfat* ef, void* sector, 238 | uint32_t vbr_checksum); 239 | bool exfat_fix_invalid_node_checksum(const struct exfat* ef, 240 | struct exfat_node* node); 241 | bool exfat_fix_unknown_entry(struct exfat* ef, struct exfat_node* dir, 242 | const struct exfat_entry* entry, off_t offset); 243 | 244 | #endif /* ifndef EXFAT_H_INCLUDED */ 245 | -------------------------------------------------------------------------------- /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-2023 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 void parse_options(struct exfat* ef, const char* options) 83 | { 84 | int opt_umask; 85 | 86 | opt_umask = get_int_option(options, "umask", 8, 0); 87 | ef->dmask = get_int_option(options, "dmask", 8, opt_umask); 88 | ef->fmask = get_int_option(options, "fmask", 8, opt_umask); 89 | 90 | ef->uid = get_int_option(options, "uid", 10, geteuid()); 91 | ef->gid = get_int_option(options, "gid", 10, getegid()); 92 | 93 | ef->noatime = exfat_match_option(options, "noatime"); 94 | 95 | switch (get_int_option(options, "repair", 10, 0)) 96 | { 97 | case 1: 98 | ef->repair = EXFAT_REPAIR_ASK; 99 | break; 100 | case 2: 101 | ef->repair = EXFAT_REPAIR_YES; 102 | break; 103 | default: 104 | ef->repair = EXFAT_REPAIR_NO; 105 | break; 106 | } 107 | } 108 | 109 | static bool verify_vbr_checksum(const struct exfat* ef, void* sector) 110 | { 111 | off_t sector_size = SECTOR_SIZE(*ef->sb); 112 | uint32_t vbr_checksum; 113 | size_t i; 114 | 115 | if (exfat_pread(ef->dev, sector, sector_size, 0) < 0) 116 | { 117 | exfat_error("failed to read boot sector"); 118 | return false; 119 | } 120 | vbr_checksum = exfat_vbr_start_checksum(sector, sector_size); 121 | for (i = 1; i < 11; i++) 122 | { 123 | if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0) 124 | { 125 | exfat_error("failed to read VBR sector"); 126 | return false; 127 | } 128 | vbr_checksum = exfat_vbr_add_checksum(sector, sector_size, 129 | vbr_checksum); 130 | } 131 | if (exfat_pread(ef->dev, sector, sector_size, i * sector_size) < 0) 132 | { 133 | exfat_error("failed to read VBR checksum sector"); 134 | return false; 135 | } 136 | for (i = 0; i < sector_size / sizeof(vbr_checksum); i++) 137 | if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum) 138 | { 139 | exfat_error("invalid VBR checksum 0x%x (expected 0x%x)", 140 | le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum); 141 | if (!EXFAT_REPAIR(invalid_vbr_checksum, ef, sector, vbr_checksum)) 142 | return false; 143 | } 144 | return true; 145 | } 146 | 147 | static int commit_super_block(const struct exfat* ef) 148 | { 149 | if (exfat_pwrite(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0) 150 | { 151 | exfat_error("failed to write super block"); 152 | return 1; 153 | } 154 | return exfat_fsync(ef->dev); 155 | } 156 | 157 | int exfat_soil_super_block(const struct exfat* ef) 158 | { 159 | if (ef->ro) 160 | return 0; 161 | 162 | ef->sb->volume_state = cpu_to_le16( 163 | le16_to_cpu(ef->sb->volume_state) | EXFAT_STATE_MOUNTED); 164 | return commit_super_block(ef); 165 | } 166 | 167 | static void exfat_free(struct exfat* ef) 168 | { 169 | exfat_close(ef->dev); /* first of all, close the descriptor */ 170 | ef->dev = NULL; /* struct exfat_dev is freed by exfat_close() */ 171 | free(ef->root); 172 | ef->root = NULL; 173 | free(ef->zero_cluster); 174 | ef->zero_cluster = NULL; 175 | free(ef->cmap.chunk); 176 | ef->cmap.chunk = NULL; 177 | free(ef->upcase); 178 | ef->upcase = NULL; 179 | free(ef->sb); 180 | ef->sb = NULL; 181 | } 182 | 183 | int exfat_mount(struct exfat* ef, const char* spec, const char* options) 184 | { 185 | int rc; 186 | enum exfat_mode mode; 187 | 188 | exfat_tzset(); 189 | memset(ef, 0, sizeof(struct exfat)); 190 | 191 | parse_options(ef, options); 192 | 193 | if (exfat_match_option(options, "ro")) 194 | mode = EXFAT_MODE_RO; 195 | else if (exfat_match_option(options, "ro_fallback")) 196 | mode = EXFAT_MODE_ANY; 197 | else 198 | mode = EXFAT_MODE_RW; 199 | ef->dev = exfat_open(spec, mode); 200 | if (ef->dev == NULL) 201 | return -ENODEV; 202 | if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO) 203 | { 204 | if (mode == EXFAT_MODE_ANY) 205 | ef->ro = -1; 206 | else 207 | ef->ro = 1; 208 | } 209 | 210 | ef->sb = malloc(sizeof(struct exfat_super_block)); 211 | if (ef->sb == NULL) 212 | { 213 | exfat_error("failed to allocate memory for the super block"); 214 | exfat_free(ef); 215 | return -ENOMEM; 216 | } 217 | memset(ef->sb, 0, sizeof(struct exfat_super_block)); 218 | 219 | if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0) 220 | { 221 | exfat_error("failed to read boot sector"); 222 | exfat_free(ef); 223 | return -EIO; 224 | } 225 | if (memcmp(ef->sb->oem_name, "EXFAT ", 8) != 0) 226 | { 227 | exfat_error("exFAT file system is not found"); 228 | exfat_free(ef); 229 | return -EIO; 230 | } 231 | /* sector cannot be smaller than 512 bytes */ 232 | if (ef->sb->sector_bits < 9) 233 | { 234 | exfat_error("too small sector size: 2^%hhd", ef->sb->sector_bits); 235 | exfat_free(ef); 236 | return -EIO; 237 | } 238 | /* officially exFAT supports cluster size up to 32 MB */ 239 | if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25) 240 | { 241 | exfat_error("too big cluster size: 2^(%hhd+%hhd)", 242 | ef->sb->sector_bits, ef->sb->spc_bits); 243 | exfat_free(ef); 244 | return -EIO; 245 | } 246 | ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb)); 247 | if (ef->zero_cluster == NULL) 248 | { 249 | exfat_error("failed to allocate zero sector"); 250 | exfat_free(ef); 251 | return -ENOMEM; 252 | } 253 | /* use zero_cluster as a temporary buffer for VBR checksum verification */ 254 | if (!verify_vbr_checksum(ef, ef->zero_cluster)) 255 | { 256 | exfat_free(ef); 257 | return -EIO; 258 | } 259 | memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb)); 260 | if (ef->sb->version.major != 1 || ef->sb->version.minor != 0) 261 | { 262 | exfat_error("unsupported exFAT version: %hhu.%hhu", 263 | ef->sb->version.major, ef->sb->version.minor); 264 | exfat_free(ef); 265 | return -EIO; 266 | } 267 | if (ef->sb->fat_count != 1) 268 | { 269 | exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count); 270 | exfat_free(ef); 271 | return -EIO; 272 | } 273 | if (le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb) > 274 | (uint64_t) exfat_get_size(ef->dev)) 275 | { 276 | /* this can cause I/O errors later but we don't fail mounting to let 277 | user rescue data */ 278 | exfat_warn("file system in sectors is larger than device: " 279 | "%"PRIu64" * %d > %"PRIu64, 280 | le64_to_cpu(ef->sb->sector_count), SECTOR_SIZE(*ef->sb), 281 | exfat_get_size(ef->dev)); 282 | } 283 | if ((off_t) le32_to_cpu(ef->sb->cluster_count) * CLUSTER_SIZE(*ef->sb) > 284 | exfat_get_size(ef->dev)) 285 | { 286 | exfat_error("file system in clusters is larger than device: " 287 | "%u * %d > %"PRIu64, 288 | le32_to_cpu(ef->sb->cluster_count), CLUSTER_SIZE(*ef->sb), 289 | exfat_get_size(ef->dev)); 290 | exfat_free(ef); 291 | return -EIO; 292 | } 293 | if (le16_to_cpu(ef->sb->volume_state) & EXFAT_STATE_MOUNTED) 294 | exfat_warn("volume was not unmounted cleanly"); 295 | 296 | ef->root = malloc(sizeof(struct exfat_node)); 297 | if (ef->root == NULL) 298 | { 299 | exfat_error("failed to allocate root node"); 300 | exfat_free(ef); 301 | return -ENOMEM; 302 | } 303 | memset(ef->root, 0, sizeof(struct exfat_node)); 304 | ef->root->attrib = EXFAT_ATTRIB_DIR; 305 | ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster); 306 | ef->root->fptr_cluster = ef->root->start_cluster; 307 | ef->root->name[0] = cpu_to_le16('\0'); 308 | ef->root->valid_size = ef->root->size = rootdir_size(ef); 309 | if (ef->root->size == 0) 310 | { 311 | exfat_free(ef); 312 | return -EIO; 313 | } 314 | /* exFAT does not have time attributes for the root directory */ 315 | ef->root->mtime = 0; 316 | ef->root->atime = 0; 317 | /* always keep at least 1 reference to the root node */ 318 | exfat_get_node(ef->root); 319 | 320 | rc = exfat_cache_directory(ef, ef->root); 321 | if (rc != 0) 322 | goto error; 323 | if (ef->upcase == NULL) 324 | { 325 | exfat_error("upcase table is not found"); 326 | goto error; 327 | } 328 | if (ef->cmap.chunk == NULL) 329 | { 330 | exfat_error("clusters bitmap is not found"); 331 | goto error; 332 | } 333 | 334 | return 0; 335 | 336 | error: 337 | exfat_put_node(ef, ef->root); 338 | exfat_reset_cache(ef); 339 | exfat_free(ef); 340 | return -EIO; 341 | } 342 | 343 | static void finalize_super_block(struct exfat* ef) 344 | { 345 | if (ef->ro) 346 | return; 347 | 348 | ef->sb->volume_state = cpu_to_le16( 349 | le16_to_cpu(ef->sb->volume_state) & ~EXFAT_STATE_MOUNTED); 350 | 351 | /* Some implementations set the percentage of allocated space to 0xff 352 | on FS creation and never update it. In this case leave it as is. */ 353 | if (ef->sb->allocated_percent != 0xff) 354 | { 355 | uint32_t free, total; 356 | 357 | free = exfat_count_free_clusters(ef); 358 | total = le32_to_cpu(ef->sb->cluster_count); 359 | ef->sb->allocated_percent = ((total - free) * 100 + total / 2) / total; 360 | } 361 | 362 | commit_super_block(ef); /* ignore return code */ 363 | } 364 | 365 | void exfat_unmount(struct exfat* ef) 366 | { 367 | exfat_flush_nodes(ef); /* ignore return code */ 368 | exfat_flush(ef); /* ignore return code */ 369 | exfat_put_node(ef, ef->root); 370 | exfat_reset_cache(ef); 371 | finalize_super_block(ef); 372 | exfat_free(ef); /* will close the descriptor */ 373 | } 374 | -------------------------------------------------------------------------------- /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-2023 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 defined(__NetBSD__) 39 | #include 40 | #elif __linux__ 41 | #include 42 | #endif 43 | #ifdef USE_UBLIO 44 | #include 45 | #include 46 | #endif 47 | 48 | struct exfat_dev 49 | { 50 | int fd; 51 | enum exfat_mode mode; 52 | off_t size; /* in bytes */ 53 | #ifdef USE_UBLIO 54 | off_t pos; 55 | ublio_filehandle_t ufh; 56 | #endif 57 | }; 58 | 59 | static bool is_open(int fd) 60 | { 61 | return fcntl(fd, F_GETFD) != -1; 62 | } 63 | 64 | static int open_ro(const char* spec) 65 | { 66 | return open(spec, O_RDONLY); 67 | } 68 | 69 | static int open_rw(const char* spec) 70 | { 71 | int fd = open(spec, O_RDWR); 72 | #ifdef __linux__ 73 | int ro = 0; 74 | 75 | /* 76 | This ioctl is needed because after "blockdev --setro" kernel still 77 | allows to open the device in read-write mode but fails writes. 78 | */ 79 | if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro) 80 | { 81 | close(fd); 82 | errno = EROFS; 83 | return -1; 84 | } 85 | #endif 86 | return fd; 87 | } 88 | 89 | struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode) 90 | { 91 | struct exfat_dev* dev; 92 | struct stat stbuf; 93 | #ifdef USE_UBLIO 94 | struct ublio_param up; 95 | #endif 96 | 97 | /* The system allocates file descriptors sequentially. If we have been 98 | started with stdin (0), stdout (1) or stderr (2) closed, the system 99 | will give us descriptor 0, 1 or 2 later when we open block device, 100 | FUSE communication pipe, etc. As a result, functions using stdin, 101 | stdout or stderr will actually work with a different thing and can 102 | corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */ 103 | while (!is_open(STDIN_FILENO) 104 | || !is_open(STDOUT_FILENO) 105 | || !is_open(STDERR_FILENO)) 106 | { 107 | /* we don't need those descriptors, let them leak */ 108 | if (open("/dev/null", O_RDWR) == -1) 109 | { 110 | exfat_error("failed to open /dev/null"); 111 | return NULL; 112 | } 113 | } 114 | 115 | dev = malloc(sizeof(struct exfat_dev)); 116 | if (dev == NULL) 117 | { 118 | exfat_error("failed to allocate memory for device structure"); 119 | return NULL; 120 | } 121 | 122 | switch (mode) 123 | { 124 | case EXFAT_MODE_RO: 125 | dev->fd = open_ro(spec); 126 | if (dev->fd == -1) 127 | { 128 | free(dev); 129 | exfat_error("failed to open '%s' in read-only mode: %s", spec, 130 | strerror(errno)); 131 | return NULL; 132 | } 133 | dev->mode = EXFAT_MODE_RO; 134 | break; 135 | case EXFAT_MODE_RW: 136 | dev->fd = open_rw(spec); 137 | if (dev->fd == -1) 138 | { 139 | free(dev); 140 | exfat_error("failed to open '%s' in read-write mode: %s", spec, 141 | strerror(errno)); 142 | return NULL; 143 | } 144 | dev->mode = EXFAT_MODE_RW; 145 | break; 146 | case EXFAT_MODE_ANY: 147 | dev->fd = open_rw(spec); 148 | if (dev->fd != -1) 149 | { 150 | dev->mode = EXFAT_MODE_RW; 151 | break; 152 | } 153 | dev->fd = open_ro(spec); 154 | if (dev->fd != -1) 155 | { 156 | dev->mode = EXFAT_MODE_RO; 157 | exfat_warn("'%s' is write-protected, mounting read-only", spec); 158 | break; 159 | } 160 | free(dev); 161 | exfat_error("failed to open '%s': %s", spec, strerror(errno)); 162 | return NULL; 163 | } 164 | 165 | if (fstat(dev->fd, &stbuf) != 0) 166 | { 167 | close(dev->fd); 168 | free(dev); 169 | exfat_error("failed to fstat '%s'", spec); 170 | return NULL; 171 | } 172 | if (!S_ISBLK(stbuf.st_mode) && 173 | !S_ISCHR(stbuf.st_mode) && 174 | !S_ISREG(stbuf.st_mode)) 175 | { 176 | close(dev->fd); 177 | free(dev); 178 | exfat_error("'%s' is neither a device, nor a regular file", spec); 179 | return NULL; 180 | } 181 | 182 | #if defined(__APPLE__) 183 | if (!S_ISREG(stbuf.st_mode)) 184 | { 185 | uint32_t block_size = 0; 186 | uint64_t blocks = 0; 187 | 188 | if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0) 189 | { 190 | close(dev->fd); 191 | free(dev); 192 | exfat_error("failed to get block size"); 193 | return NULL; 194 | } 195 | if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0) 196 | { 197 | close(dev->fd); 198 | free(dev); 199 | exfat_error("failed to get blocks count"); 200 | return NULL; 201 | } 202 | dev->size = blocks * block_size; 203 | } 204 | else 205 | #elif defined(__OpenBSD__) 206 | if (!S_ISREG(stbuf.st_mode)) 207 | { 208 | struct disklabel lab; 209 | struct partition* pp; 210 | char* partition; 211 | 212 | if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1) 213 | { 214 | close(dev->fd); 215 | free(dev); 216 | exfat_error("failed to get disklabel"); 217 | return NULL; 218 | } 219 | 220 | /* Don't need to check that partition letter is valid as we won't get 221 | this far otherwise. */ 222 | partition = strchr(spec, '\0') - 1; 223 | pp = &(lab.d_partitions[*partition - 'a']); 224 | dev->size = DL_GETPSIZE(pp) * lab.d_secsize; 225 | 226 | if (pp->p_fstype != FS_NTFS) 227 | exfat_warn("partition type is not 0x07 (NTFS/exFAT); " 228 | "you can fix this with fdisk(8)"); 229 | } 230 | else 231 | #elif defined(__NetBSD__) 232 | if (!S_ISREG(stbuf.st_mode)) 233 | { 234 | off_t size; 235 | 236 | if (ioctl(dev->fd, DIOCGMEDIASIZE, &size) == -1) 237 | { 238 | close(dev->fd); 239 | free(dev); 240 | exfat_error("failed to get media size"); 241 | return NULL; 242 | } 243 | dev->size = size; 244 | } 245 | else 246 | #endif 247 | { 248 | /* works for Linux, FreeBSD, Solaris */ 249 | dev->size = exfat_seek(dev, 0, SEEK_END); 250 | if (dev->size <= 0) 251 | { 252 | close(dev->fd); 253 | free(dev); 254 | exfat_error("failed to get size of '%s'", spec); 255 | return NULL; 256 | } 257 | if (exfat_seek(dev, 0, SEEK_SET) == -1) 258 | { 259 | close(dev->fd); 260 | free(dev); 261 | exfat_error("failed to seek to the beginning of '%s'", spec); 262 | return NULL; 263 | } 264 | } 265 | 266 | #ifdef USE_UBLIO 267 | memset(&up, 0, sizeof(struct ublio_param)); 268 | up.up_blocksize = 256 * 1024; 269 | up.up_items = 64; 270 | up.up_grace = 32; 271 | up.up_priv = &dev->fd; 272 | 273 | dev->pos = 0; 274 | dev->ufh = ublio_open(&up); 275 | if (dev->ufh == NULL) 276 | { 277 | close(dev->fd); 278 | free(dev); 279 | exfat_error("failed to initialize ublio"); 280 | return NULL; 281 | } 282 | #endif 283 | 284 | return dev; 285 | } 286 | 287 | int exfat_close(struct exfat_dev* dev) 288 | { 289 | int rc = 0; 290 | 291 | #ifdef USE_UBLIO 292 | if (ublio_close(dev->ufh) != 0) 293 | { 294 | exfat_error("failed to close ublio"); 295 | rc = -EIO; 296 | } 297 | #endif 298 | if (close(dev->fd) != 0) 299 | { 300 | exfat_error("failed to close device: %s", strerror(errno)); 301 | rc = -EIO; 302 | } 303 | free(dev); 304 | return rc; 305 | } 306 | 307 | int exfat_fsync(struct exfat_dev* dev) 308 | { 309 | int rc = 0; 310 | 311 | #ifdef USE_UBLIO 312 | if (ublio_fsync(dev->ufh) != 0) 313 | { 314 | exfat_error("ublio fsync failed"); 315 | rc = -EIO; 316 | } 317 | #endif 318 | if (fsync(dev->fd) != 0) 319 | { 320 | exfat_error("fsync failed: %s", strerror(errno)); 321 | rc = -EIO; 322 | } 323 | return rc; 324 | } 325 | 326 | enum exfat_mode exfat_get_mode(const struct exfat_dev* dev) 327 | { 328 | return dev->mode; 329 | } 330 | 331 | off_t exfat_get_size(const struct exfat_dev* dev) 332 | { 333 | return dev->size; 334 | } 335 | 336 | off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence) 337 | { 338 | #ifdef USE_UBLIO 339 | /* XXX SEEK_CUR will be handled incorrectly */ 340 | return dev->pos = lseek(dev->fd, offset, whence); 341 | #else 342 | return lseek(dev->fd, offset, whence); 343 | #endif 344 | } 345 | 346 | ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size) 347 | { 348 | #ifdef USE_UBLIO 349 | ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos); 350 | if (result >= 0) 351 | dev->pos += size; 352 | return result; 353 | #else 354 | return read(dev->fd, buffer, size); 355 | #endif 356 | } 357 | 358 | ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size) 359 | { 360 | #ifdef USE_UBLIO 361 | ssize_t result = ublio_pwrite(dev->ufh, (void*) buffer, size, dev->pos); 362 | if (result >= 0) 363 | dev->pos += size; 364 | return result; 365 | #else 366 | return write(dev->fd, buffer, size); 367 | #endif 368 | } 369 | 370 | ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size, 371 | off_t offset) 372 | { 373 | #ifdef USE_UBLIO 374 | return ublio_pread(dev->ufh, buffer, size, offset); 375 | #else 376 | return pread(dev->fd, buffer, size, offset); 377 | #endif 378 | } 379 | 380 | ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size, 381 | off_t offset) 382 | { 383 | #ifdef USE_UBLIO 384 | return ublio_pwrite(dev->ufh, (void*) buffer, size, offset); 385 | #else 386 | return pwrite(dev->fd, buffer, size, offset); 387 | #endif 388 | } 389 | 390 | ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node, 391 | void* buffer, size_t size, off_t offset) 392 | { 393 | uint64_t uoffset = offset; 394 | cluster_t cluster; 395 | char* bufp = buffer; 396 | off_t lsize, loffset, remainder; 397 | 398 | if (offset < 0) 399 | return -EINVAL; 400 | if (uoffset >= node->size) 401 | return 0; 402 | if (size == 0) 403 | return 0; 404 | 405 | if (uoffset + size > node->valid_size) 406 | { 407 | ssize_t bytes = 0; 408 | 409 | if (uoffset < node->valid_size) 410 | { 411 | bytes = exfat_generic_pread(ef, node, buffer, 412 | node->valid_size - uoffset, offset); 413 | if (bytes < 0 || (size_t) bytes < node->valid_size - uoffset) 414 | return bytes; 415 | } 416 | memset(buffer + bytes, 0, 417 | MIN(size - bytes, node->size - node->valid_size)); 418 | return MIN(size, node->size - uoffset); 419 | } 420 | 421 | cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); 422 | if (CLUSTER_INVALID(*ef->sb, cluster)) 423 | { 424 | exfat_error("invalid cluster 0x%x while reading", cluster); 425 | return -EIO; 426 | } 427 | 428 | loffset = uoffset % CLUSTER_SIZE(*ef->sb); 429 | remainder = MIN(size, node->size - uoffset); 430 | while (remainder > 0) 431 | { 432 | if (CLUSTER_INVALID(*ef->sb, cluster)) 433 | { 434 | exfat_error("invalid cluster 0x%x while reading", cluster); 435 | return -EIO; 436 | } 437 | lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); 438 | if (exfat_pread(ef->dev, bufp, lsize, 439 | exfat_c2o(ef, cluster) + loffset) < 0) 440 | { 441 | exfat_error("failed to read cluster %#x", cluster); 442 | return -EIO; 443 | } 444 | bufp += lsize; 445 | loffset = 0; 446 | remainder -= lsize; 447 | cluster = exfat_next_cluster(ef, node, cluster); 448 | } 449 | if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime) 450 | exfat_update_atime(node); 451 | return MIN(size, node->size - uoffset) - remainder; 452 | } 453 | 454 | ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node, 455 | const void* buffer, size_t size, off_t offset) 456 | { 457 | uint64_t uoffset = offset; 458 | int rc; 459 | cluster_t cluster; 460 | const char* bufp = buffer; 461 | off_t lsize, loffset, remainder; 462 | 463 | if (offset < 0) 464 | return -EINVAL; 465 | if (uoffset > node->size) 466 | { 467 | rc = exfat_truncate(ef, node, uoffset, true); 468 | if (rc != 0) 469 | return rc; 470 | } 471 | if (uoffset + size > node->size) 472 | { 473 | rc = exfat_truncate(ef, node, uoffset + size, false); 474 | if (rc != 0) 475 | return rc; 476 | } 477 | if (size == 0) 478 | return 0; 479 | 480 | cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb)); 481 | if (CLUSTER_INVALID(*ef->sb, cluster)) 482 | { 483 | exfat_error("invalid cluster 0x%x while writing", cluster); 484 | return -EIO; 485 | } 486 | 487 | loffset = uoffset % CLUSTER_SIZE(*ef->sb); 488 | remainder = size; 489 | while (remainder > 0) 490 | { 491 | if (CLUSTER_INVALID(*ef->sb, cluster)) 492 | { 493 | exfat_error("invalid cluster 0x%x while writing", cluster); 494 | return -EIO; 495 | } 496 | lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder); 497 | if (exfat_pwrite(ef->dev, bufp, lsize, 498 | exfat_c2o(ef, cluster) + loffset) < 0) 499 | { 500 | exfat_error("failed to write cluster %#x", cluster); 501 | return -EIO; 502 | } 503 | bufp += lsize; 504 | loffset = 0; 505 | remainder -= lsize; 506 | node->valid_size = MAX(node->valid_size, uoffset + size - remainder); 507 | cluster = exfat_next_cluster(ef, node, cluster); 508 | } 509 | if (!(node->attrib & EXFAT_ATTRIB_DIR)) 510 | /* directory's mtime should be updated by the caller only when it 511 | creates or removes something in this directory */ 512 | exfat_update_mtime(node); 513 | return size - remainder; 514 | } 515 | -------------------------------------------------------------------------------- /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-2023 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) ~((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->valid_size, size); 430 | if (rc != 0) 431 | return rc; 432 | node->valid_size = size; 433 | } 434 | else 435 | { 436 | node->valid_size = MIN(node->valid_size, size); 437 | } 438 | 439 | exfat_update_mtime(node); 440 | node->size = size; 441 | node->is_dirty = true; 442 | return 0; 443 | } 444 | 445 | uint32_t exfat_count_free_clusters(const struct exfat* ef) 446 | { 447 | uint32_t free_clusters = 0; 448 | uint32_t i; 449 | 450 | for (i = 0; i < ef->cmap.size; i++) 451 | if (BMAP_GET(ef->cmap.chunk, i) == 0) 452 | free_clusters++; 453 | return free_clusters; 454 | } 455 | 456 | static int find_used_clusters(const struct exfat* ef, 457 | cluster_t* a, cluster_t* b) 458 | { 459 | const cluster_t end = le32_to_cpu(ef->sb->cluster_count); 460 | 461 | /* find first used cluster */ 462 | for (*a = *b + 1; *a < end; (*a)++) 463 | if (BMAP_GET(ef->cmap.chunk, *a - EXFAT_FIRST_DATA_CLUSTER)) 464 | break; 465 | if (*a >= end) 466 | return 1; 467 | 468 | /* find last contiguous used cluster */ 469 | for (*b = *a; *b < end; (*b)++) 470 | if (BMAP_GET(ef->cmap.chunk, *b - EXFAT_FIRST_DATA_CLUSTER) == 0) 471 | { 472 | (*b)--; 473 | break; 474 | } 475 | 476 | return 0; 477 | } 478 | 479 | int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b) 480 | { 481 | cluster_t ca, cb; 482 | 483 | if (*a == 0 && *b == 0) 484 | ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1; 485 | else 486 | { 487 | ca = s2c(ef, *a); 488 | cb = s2c(ef, *b); 489 | } 490 | if (find_used_clusters(ef, &ca, &cb) != 0) 491 | return 1; 492 | if (*a != 0 || *b != 0) 493 | *a = c2s(ef, ca); 494 | *b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb); 495 | return 0; 496 | } 497 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-2023 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 | /* exfat.h must be included before fuse.h because it defines macros that 25 | affect FUSE API */ 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #ifndef DEBUG 39 | #define exfat_debug(format, ...) do {} while (0) 40 | #endif 41 | 42 | #if !defined(FUSE_VERSION) || (FUSE_VERSION < 26) 43 | #error FUSE 2.6 or later is required 44 | #endif 45 | 46 | struct exfat ef; 47 | 48 | static struct exfat_node* get_node(const struct fuse_file_info* fi) 49 | { 50 | return (struct exfat_node*) (size_t) fi->fh; 51 | } 52 | 53 | static void set_node(struct fuse_file_info* fi, struct exfat_node* node) 54 | { 55 | fi->fh = (uint64_t) (size_t) node; 56 | fi->keep_cache = 1; 57 | } 58 | 59 | static int fuse_exfat_getattr(const char* path, struct stat* stbuf 60 | #if FUSE_USE_VERSION >= 30 61 | , UNUSED struct fuse_file_info* fi 62 | #endif 63 | ) 64 | { 65 | struct exfat_node* node; 66 | int rc; 67 | 68 | exfat_debug("[%s] %s", __func__, path); 69 | 70 | rc = exfat_lookup(&ef, &node, path); 71 | if (rc != 0) 72 | return rc; 73 | 74 | exfat_stat(&ef, node, stbuf); 75 | exfat_put_node(&ef, node); 76 | return 0; 77 | } 78 | 79 | static int fuse_exfat_truncate(const char* path, off_t size 80 | #if FUSE_USE_VERSION >= 30 81 | , UNUSED struct fuse_file_info* fi 82 | #endif 83 | ) 84 | { 85 | struct exfat_node* node; 86 | int rc; 87 | 88 | exfat_debug("[%s] %s, %"PRId64, __func__, path, size); 89 | 90 | rc = exfat_lookup(&ef, &node, path); 91 | if (rc != 0) 92 | return rc; 93 | 94 | rc = exfat_truncate(&ef, node, size, true); 95 | if (rc != 0) 96 | { 97 | exfat_flush_node(&ef, node); /* ignore return code */ 98 | exfat_put_node(&ef, node); 99 | return rc; 100 | } 101 | rc = exfat_flush_node(&ef, node); 102 | exfat_put_node(&ef, node); 103 | return rc; 104 | } 105 | 106 | static int fuse_exfat_readdir(const char* path, void* buffer, 107 | fuse_fill_dir_t filler, UNUSED off_t offset, 108 | UNUSED struct fuse_file_info* fi 109 | #if FUSE_USE_VERSION >= 30 110 | , UNUSED enum fuse_readdir_flags flags 111 | #endif 112 | ) 113 | { 114 | struct exfat_node* parent; 115 | struct exfat_node* node; 116 | struct exfat_iterator it; 117 | int rc; 118 | char name[EXFAT_UTF8_NAME_BUFFER_MAX]; 119 | struct stat stbuf; 120 | 121 | exfat_debug("[%s] %s", __func__, path); 122 | 123 | rc = exfat_lookup(&ef, &parent, path); 124 | if (rc != 0) 125 | return rc; 126 | if (!(parent->attrib & EXFAT_ATTRIB_DIR)) 127 | { 128 | exfat_put_node(&ef, parent); 129 | exfat_error("'%s' is not a directory (%#hx)", path, parent->attrib); 130 | return -ENOTDIR; 131 | } 132 | 133 | #if FUSE_USE_VERSION < 30 134 | filler(buffer, ".", NULL, 0); 135 | filler(buffer, "..", NULL, 0); 136 | #else 137 | filler(buffer, ".", NULL, 0, 0); 138 | filler(buffer, "..", NULL, 0, 0); 139 | #endif 140 | 141 | rc = exfat_opendir(&ef, parent, &it); 142 | if (rc != 0) 143 | { 144 | exfat_put_node(&ef, parent); 145 | exfat_error("failed to open directory '%s'", path); 146 | return rc; 147 | } 148 | while ((node = exfat_readdir(&it))) 149 | { 150 | exfat_get_name(node, name); 151 | exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__, 152 | name, node->is_contiguous ? "contiguous" : "fragmented", 153 | node->size, node->start_cluster); 154 | exfat_stat(&ef, node, &stbuf); 155 | #if FUSE_USE_VERSION < 30 156 | filler(buffer, name, &stbuf, 0); 157 | #else 158 | filler(buffer, name, &stbuf, 0, 0); 159 | #endif 160 | exfat_put_node(&ef, node); 161 | } 162 | exfat_closedir(&ef, &it); 163 | exfat_put_node(&ef, parent); 164 | return 0; 165 | } 166 | 167 | static int fuse_exfat_open(const char* path, struct fuse_file_info* fi) 168 | { 169 | struct exfat_node* node; 170 | int rc; 171 | 172 | exfat_debug("[%s] %s flags %#x%s%s%s%s%s", __func__, path, fi->flags, 173 | fi->flags & O_RDONLY ? " O_RDONLY" : "", 174 | fi->flags & O_WRONLY ? " O_WRONLY" : "", 175 | fi->flags & O_RDWR ? " O_RDWR" : "", 176 | fi->flags & O_APPEND ? " O_APPEND" : "", 177 | fi->flags & O_TRUNC ? " O_TRUNC" : ""); 178 | 179 | rc = exfat_lookup(&ef, &node, path); 180 | if (rc != 0) 181 | return rc; 182 | /* FUSE 2.x will call fuse_exfat_truncate() explicitly */ 183 | #if FUSE_USE_VERSION >= 30 184 | if (fi->flags & O_TRUNC) 185 | { 186 | rc = exfat_truncate(&ef, node, 0, true); 187 | if (rc != 0) 188 | { 189 | exfat_put_node(&ef, node); 190 | return rc; 191 | } 192 | } 193 | #endif 194 | set_node(fi, node); 195 | return 0; 196 | } 197 | 198 | static int fuse_exfat_create(const char* path, UNUSED mode_t mode, 199 | struct fuse_file_info* fi) 200 | { 201 | struct exfat_node* node; 202 | int rc; 203 | 204 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 205 | 206 | rc = exfat_mknod(&ef, path); 207 | if (rc != 0) 208 | return rc; 209 | rc = exfat_lookup(&ef, &node, path); 210 | if (rc != 0) 211 | return rc; 212 | set_node(fi, node); 213 | return 0; 214 | } 215 | 216 | static int fuse_exfat_release(UNUSED const char* path, 217 | struct fuse_file_info* fi) 218 | { 219 | /* 220 | This handler is called by FUSE on close() syscall. If the FUSE 221 | implementation does not call flush handler, we will flush node here. 222 | But in this case we will not be able to return an error to the caller. 223 | See fuse_exfat_flush() below. 224 | */ 225 | exfat_debug("[%s] %s", __func__, path); 226 | exfat_flush_node(&ef, get_node(fi)); 227 | exfat_put_node(&ef, get_node(fi)); 228 | return 0; /* FUSE ignores this return value */ 229 | } 230 | 231 | static int fuse_exfat_flush(UNUSED const char* path, struct fuse_file_info* fi) 232 | { 233 | /* 234 | This handler may be called by FUSE on close() syscall. FUSE also deals 235 | with removals of open files, so we don't free clusters on close but 236 | only on rmdir and unlink. If the FUSE implementation does not call this 237 | handler we will flush node on release. See fuse_exfat_release() above. 238 | */ 239 | exfat_debug("[%s] %s", __func__, path); 240 | return exfat_flush_node(&ef, get_node(fi)); 241 | } 242 | 243 | static int fuse_exfat_fsync(UNUSED const char* path, UNUSED int datasync, 244 | UNUSED struct fuse_file_info* fi) 245 | { 246 | int rc; 247 | 248 | exfat_debug("[%s] %s", __func__, path); 249 | rc = exfat_flush_nodes(&ef); 250 | if (rc != 0) 251 | return rc; 252 | rc = exfat_flush(&ef); 253 | if (rc != 0) 254 | return rc; 255 | return exfat_fsync(ef.dev); 256 | } 257 | 258 | static int fuse_exfat_read(UNUSED const char* path, char* buffer, 259 | size_t size, off_t offset, struct fuse_file_info* fi) 260 | { 261 | exfat_debug("[%s] %s (%zu bytes)", __func__, path, size); 262 | return exfat_generic_pread(&ef, get_node(fi), buffer, size, offset); 263 | } 264 | 265 | static int fuse_exfat_write(UNUSED const char* path, const char* buffer, 266 | size_t size, off_t offset, struct fuse_file_info* fi) 267 | { 268 | exfat_debug("[%s] %s (%zu bytes)", __func__, path, size); 269 | return exfat_generic_pwrite(&ef, get_node(fi), buffer, size, offset); 270 | } 271 | 272 | static int fuse_exfat_unlink(const char* path) 273 | { 274 | struct exfat_node* node; 275 | int rc; 276 | 277 | exfat_debug("[%s] %s", __func__, path); 278 | 279 | rc = exfat_lookup(&ef, &node, path); 280 | if (rc != 0) 281 | return rc; 282 | 283 | rc = exfat_unlink(&ef, node); 284 | exfat_put_node(&ef, node); 285 | if (rc != 0) 286 | return rc; 287 | return exfat_cleanup_node(&ef, node); 288 | } 289 | 290 | static int fuse_exfat_rmdir(const char* path) 291 | { 292 | struct exfat_node* node; 293 | int rc; 294 | 295 | exfat_debug("[%s] %s", __func__, path); 296 | 297 | rc = exfat_lookup(&ef, &node, path); 298 | if (rc != 0) 299 | return rc; 300 | 301 | rc = exfat_rmdir(&ef, node); 302 | exfat_put_node(&ef, node); 303 | if (rc != 0) 304 | return rc; 305 | return exfat_cleanup_node(&ef, node); 306 | } 307 | 308 | static int fuse_exfat_mknod(const char* path, UNUSED mode_t mode, 309 | UNUSED dev_t dev) 310 | { 311 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 312 | return exfat_mknod(&ef, path); 313 | } 314 | 315 | static int fuse_exfat_mkdir(const char* path, UNUSED mode_t mode) 316 | { 317 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 318 | return exfat_mkdir(&ef, path); 319 | } 320 | 321 | static int fuse_exfat_rename(const char* old_path, const char* new_path 322 | #if FUSE_USE_VERSION >= 30 323 | , UNUSED unsigned int flags 324 | #endif 325 | ) 326 | { 327 | exfat_debug("[%s] %s => %s", __func__, old_path, new_path); 328 | return exfat_rename(&ef, old_path, new_path); 329 | } 330 | 331 | static int fuse_exfat_utimens(const char* path, const struct timespec tv[2] 332 | #if FUSE_USE_VERSION >= 30 333 | , UNUSED struct fuse_file_info* fi 334 | #endif 335 | ) 336 | { 337 | struct exfat_node* node; 338 | int rc; 339 | 340 | exfat_debug("[%s] %s", __func__, path); 341 | 342 | rc = exfat_lookup(&ef, &node, path); 343 | if (rc != 0) 344 | return rc; 345 | 346 | exfat_utimes(node, tv); 347 | rc = exfat_flush_node(&ef, node); 348 | exfat_put_node(&ef, node); 349 | return rc; 350 | } 351 | 352 | static int fuse_exfat_chmod(UNUSED const char* path, mode_t mode 353 | #if FUSE_USE_VERSION >= 30 354 | , UNUSED struct fuse_file_info* fi 355 | #endif 356 | ) 357 | { 358 | const mode_t VALID_MODE_MASK = S_IFREG | S_IFDIR | 359 | S_IRWXU | S_IRWXG | S_IRWXO; 360 | 361 | exfat_debug("[%s] %s 0%ho", __func__, path, mode); 362 | if (mode & ~VALID_MODE_MASK) 363 | return -EPERM; 364 | return 0; 365 | } 366 | 367 | static int fuse_exfat_chown(UNUSED const char* path, uid_t uid, gid_t gid 368 | #if FUSE_USE_VERSION >= 30 369 | , UNUSED struct fuse_file_info* fi 370 | #endif 371 | ) 372 | { 373 | exfat_debug("[%s] %s %u:%u", __func__, path, uid, gid); 374 | if (uid != ef.uid || gid != ef.gid) 375 | return -EPERM; 376 | return 0; 377 | } 378 | 379 | static int fuse_exfat_statfs(UNUSED const char* path, struct statvfs* sfs) 380 | { 381 | exfat_debug("[%s]", __func__); 382 | 383 | sfs->f_bsize = CLUSTER_SIZE(*ef.sb); 384 | sfs->f_frsize = CLUSTER_SIZE(*ef.sb); 385 | sfs->f_blocks = le64_to_cpu(ef.sb->sector_count) >> ef.sb->spc_bits; 386 | sfs->f_bavail = exfat_count_free_clusters(&ef); 387 | sfs->f_bfree = sfs->f_bavail; 388 | sfs->f_namemax = EXFAT_NAME_MAX; 389 | 390 | /* 391 | Below are fake values because in exFAT there is 392 | a) no simple way to count files; 393 | b) no such thing as inode; 394 | So here we assume that inode = cluster. 395 | */ 396 | sfs->f_files = le32_to_cpu(ef.sb->cluster_count); 397 | sfs->f_favail = sfs->f_bfree >> ef.sb->spc_bits; 398 | sfs->f_ffree = sfs->f_bavail; 399 | 400 | return 0; 401 | } 402 | 403 | static void* fuse_exfat_init( 404 | #ifdef FUSE_CAP_BIG_WRITES 405 | struct fuse_conn_info* fci 406 | #else 407 | UNUSED struct fuse_conn_info* fci 408 | #endif 409 | #if FUSE_USE_VERSION >= 30 410 | , UNUSED struct fuse_config* cfg 411 | #endif 412 | ) 413 | { 414 | exfat_debug("[%s]", __func__); 415 | #ifdef FUSE_CAP_BIG_WRITES 416 | fci->want |= FUSE_CAP_BIG_WRITES; 417 | #endif 418 | 419 | /* mark super block as dirty; failure isn't a big deal */ 420 | exfat_soil_super_block(&ef); 421 | 422 | return NULL; 423 | } 424 | 425 | static void fuse_exfat_destroy(UNUSED void* unused) 426 | { 427 | exfat_debug("[%s]", __func__); 428 | exfat_unmount(&ef); 429 | } 430 | 431 | static void usage(const char* prog) 432 | { 433 | fprintf(stderr, "Usage: %s [-d] [-o options] [-V] \n", prog); 434 | exit(1); 435 | } 436 | 437 | static struct fuse_operations fuse_exfat_ops = 438 | { 439 | .getattr = fuse_exfat_getattr, 440 | .truncate = fuse_exfat_truncate, 441 | .readdir = fuse_exfat_readdir, 442 | .open = fuse_exfat_open, 443 | .create = fuse_exfat_create, 444 | .release = fuse_exfat_release, 445 | .flush = fuse_exfat_flush, 446 | .fsync = fuse_exfat_fsync, 447 | .fsyncdir = fuse_exfat_fsync, 448 | .read = fuse_exfat_read, 449 | .write = fuse_exfat_write, 450 | .unlink = fuse_exfat_unlink, 451 | .rmdir = fuse_exfat_rmdir, 452 | .mknod = fuse_exfat_mknod, 453 | .mkdir = fuse_exfat_mkdir, 454 | .rename = fuse_exfat_rename, 455 | .utimens = fuse_exfat_utimens, 456 | .chmod = fuse_exfat_chmod, 457 | .chown = fuse_exfat_chown, 458 | .statfs = fuse_exfat_statfs, 459 | .init = fuse_exfat_init, 460 | .destroy = fuse_exfat_destroy, 461 | }; 462 | 463 | static char* add_option(char* options, const char* name, const char* value) 464 | { 465 | size_t size; 466 | char* optionsf = options; 467 | 468 | if (value) 469 | size = strlen(options) + strlen(name) + strlen(value) + 3; 470 | else 471 | size = strlen(options) + strlen(name) + 2; 472 | 473 | options = realloc(options, size); 474 | if (options == NULL) 475 | { 476 | free(optionsf); 477 | exfat_error("failed to reallocate options string"); 478 | return NULL; 479 | } 480 | strcat(options, ","); 481 | strcat(options, name); 482 | if (value) 483 | { 484 | strcat(options, "="); 485 | strcat(options, value); 486 | } 487 | return options; 488 | } 489 | 490 | static void escape(char* escaped, const char* orig) 491 | { 492 | do 493 | { 494 | if (*orig == ',' || *orig == '\\') 495 | *escaped++ = '\\'; 496 | } 497 | while ((*escaped++ = *orig++)); 498 | } 499 | 500 | static char* add_fsname_option(char* options, const char* spec) 501 | { 502 | /* escaped string cannot be more than twice as big as the original one */ 503 | char* escaped = malloc(strlen(spec) * 2 + 1); 504 | 505 | if (escaped == NULL) 506 | { 507 | free(options); 508 | exfat_error("failed to allocate escaped string for %s", spec); 509 | return NULL; 510 | } 511 | 512 | /* on some platforms (e.g. Android, Solaris) device names can contain 513 | commas */ 514 | escape(escaped, spec); 515 | options = add_option(options, "fsname", escaped); 516 | free(escaped); 517 | return options; 518 | } 519 | 520 | static char* add_ro_option(char* options, bool ro) 521 | { 522 | return ro ? add_option(options, "ro", NULL) : options; 523 | } 524 | 525 | #if defined(__linux__) 526 | static char* add_user_option(char* options) 527 | { 528 | struct passwd* pw; 529 | 530 | if (getuid() == 0) 531 | return options; 532 | 533 | pw = getpwuid(getuid()); 534 | if (pw == NULL || pw->pw_name == NULL) 535 | { 536 | free(options); 537 | exfat_error("failed to determine username"); 538 | return NULL; 539 | } 540 | return add_option(options, "user", pw->pw_name); 541 | } 542 | #endif 543 | 544 | #if defined(__linux__) 545 | static char* add_blksize_option(char* options, long cluster_size) 546 | { 547 | long page_size = sysconf(_SC_PAGESIZE); 548 | char blksize[20]; 549 | 550 | if (page_size < 1) 551 | page_size = 0x1000; 552 | 553 | snprintf(blksize, sizeof(blksize), "%ld", MIN(page_size, cluster_size)); 554 | return add_option(options, "blksize", blksize); 555 | } 556 | #endif 557 | 558 | static char* add_fuse_options(char* options, const char* spec, bool ro) 559 | { 560 | options = add_fsname_option(options, spec); 561 | if (options == NULL) 562 | return NULL; 563 | options = add_ro_option(options, ro); 564 | if (options == NULL) 565 | return NULL; 566 | #if defined(__linux__) 567 | options = add_user_option(options); 568 | if (options == NULL) 569 | return NULL; 570 | options = add_blksize_option(options, CLUSTER_SIZE(*ef.sb)); 571 | if (options == NULL) 572 | return NULL; 573 | #endif 574 | return options; 575 | } 576 | 577 | static char* add_passthrough_fuse_options(char* fuse_options, 578 | const char* options) 579 | { 580 | const char* passthrough_list[] = 581 | { 582 | #if defined(__FreeBSD__) 583 | "automounted", 584 | #endif 585 | "nonempty", 586 | NULL 587 | }; 588 | int i; 589 | 590 | for (i = 0; passthrough_list[i] != NULL; i++) 591 | if (exfat_match_option(options, passthrough_list[i])) 592 | { 593 | fuse_options = add_option(fuse_options, passthrough_list[i], NULL); 594 | if (fuse_options == NULL) 595 | return NULL; 596 | } 597 | 598 | return fuse_options; 599 | } 600 | 601 | static int fuse_exfat_main(char* mount_options, char* mount_point) 602 | { 603 | char* argv[] = {"exfat", "-s", "-o", mount_options, mount_point, NULL}; 604 | return fuse_main(sizeof(argv) / sizeof(argv[0]) - 1, argv, 605 | &fuse_exfat_ops, NULL); 606 | } 607 | 608 | int main(int argc, char* argv[]) 609 | { 610 | const char* spec = NULL; 611 | char* mount_point = NULL; 612 | char* fuse_options; 613 | char* exfat_options; 614 | int opt; 615 | int rc; 616 | 617 | printf("FUSE exfat %s (libfuse%d)\n", VERSION, FUSE_USE_VERSION / 10); 618 | 619 | fuse_options = strdup("allow_other," 620 | #if FUSE_USE_VERSION < 30 && (defined(__linux__) || defined(__FreeBSD__)) 621 | "big_writes," 622 | #endif 623 | #if defined(__linux__) 624 | "blkdev," 625 | #endif 626 | "default_permissions"); 627 | exfat_options = strdup("ro_fallback"); 628 | if (fuse_options == NULL || exfat_options == NULL) 629 | { 630 | free(fuse_options); 631 | free(exfat_options); 632 | exfat_error("failed to allocate options string"); 633 | return 1; 634 | } 635 | 636 | while ((opt = getopt(argc, argv, "dno:Vv")) != -1) 637 | { 638 | switch (opt) 639 | { 640 | case 'd': 641 | fuse_options = add_option(fuse_options, "debug", NULL); 642 | if (fuse_options == NULL) 643 | { 644 | free(exfat_options); 645 | return 1; 646 | } 647 | break; 648 | case 'n': 649 | break; 650 | case 'o': 651 | exfat_options = add_option(exfat_options, optarg, NULL); 652 | if (exfat_options == NULL) 653 | { 654 | free(fuse_options); 655 | return 1; 656 | } 657 | fuse_options = add_passthrough_fuse_options(fuse_options, optarg); 658 | if (fuse_options == NULL) 659 | { 660 | free(exfat_options); 661 | return 1; 662 | } 663 | break; 664 | case 'V': 665 | free(exfat_options); 666 | free(fuse_options); 667 | puts("Copyright (C) 2010-2023 Andrew Nayenko"); 668 | return 0; 669 | case 'v': 670 | break; 671 | default: 672 | free(exfat_options); 673 | free(fuse_options); 674 | usage(argv[0]); 675 | break; 676 | } 677 | } 678 | if (argc - optind != 2) 679 | { 680 | free(exfat_options); 681 | free(fuse_options); 682 | usage(argv[0]); 683 | } 684 | spec = argv[optind]; 685 | mount_point = argv[optind + 1]; 686 | 687 | if (exfat_mount(&ef, spec, exfat_options) != 0) 688 | { 689 | free(exfat_options); 690 | free(fuse_options); 691 | return 1; 692 | } 693 | 694 | free(exfat_options); 695 | 696 | fuse_options = add_fuse_options(fuse_options, spec, ef.ro != 0); 697 | if (fuse_options == NULL) 698 | { 699 | exfat_unmount(&ef); 700 | return 1; 701 | } 702 | 703 | /* let FUSE do all its wizardry */ 704 | rc = fuse_exfat_main(fuse_options, mount_point); 705 | 706 | free(fuse_options); 707 | return rc; 708 | } 709 | --------------------------------------------------------------------------------