From 37cbd64649f64ddb88b4ab3d8a494ddf96890cf8 Mon Sep 17 00:00:00 2001 From: Sven Hoexter Date: Wed, 21 Apr 2021 17:58:43 +0200 Subject: [PATCH] New upstream version 1.1.1 --- NEWS | 16 +++++++++ configure | 20 +++++------ dump/dump.c | 21 ++++++----- fsck/fsck.c | 29 ++++++++++----- include/exfat_ondisk.h | 2 +- include/libexfat.h | 4 ++- include/version.h | 2 +- lib/libexfat.c | 54 ++++++++++++++++++++++------ manpages/fsck.exfat.8 | 5 +++ manpages/mkfs.exfat.8 | 74 +++++++++++++++++++++++++++++--------- manpages/tune.exfat.8 | 2 +- mkfs/mkfs.c | 81 +++++++++++++++++++++++++++++++++--------- tune/tune.c | 2 +- 13 files changed, 237 insertions(+), 75 deletions(-) diff --git a/NEWS b/NEWS index d8bd4e4..9c4e72b 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,19 @@ +exfatprogs 1.1.1 - released 2021-04-21 +====================================== + +CHANGES : + * mkfs.exfat: adjust the boundary alignment calculations to compensate + for the volume offset. + +NEW FEATURES : + * mkfs.exfat: add the "--pack-bitmap" option to relocate the allocation + bitmap to allow the FAT and the bitmap to share the same allocation + unit on flash media. + +BUG FIXES : + * Fix wrong bit operations on 64-bit big. + * Fix memory leaks in error paths. + exfatprogs 1.1.0 - released 2021-02-09 ====================================== diff --git a/configure b/configure index be789e2..ce750ca 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for exfatprogs 1.1.0. +# Generated by GNU Autoconf 2.69 for exfatprogs 1.1.1. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='exfatprogs' PACKAGE_TARNAME='exfatprogs' -PACKAGE_VERSION='1.1.0' -PACKAGE_STRING='exfatprogs 1.1.0' +PACKAGE_VERSION='1.1.1' +PACKAGE_STRING='exfatprogs 1.1.1' PACKAGE_BUGREPORT='linkinjeon@kernel.org' PACKAGE_URL='https://github.com/exfatprogs/exfatprogs' @@ -1325,7 +1325,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures exfatprogs 1.1.0 to adapt to many kinds of systems. +\`configure' configures exfatprogs 1.1.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1396,7 +1396,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of exfatprogs 1.1.0:";; + short | recursive ) echo "Configuration of exfatprogs 1.1.1:";; esac cat <<\_ACEOF @@ -1508,7 +1508,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -exfatprogs configure 1.1.0 +exfatprogs configure 1.1.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1786,7 +1786,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by exfatprogs $as_me 1.1.0, which was +It was created by exfatprogs $as_me 1.1.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2654,7 +2654,7 @@ fi # Define the identity of the package. PACKAGE='exfatprogs' - VERSION='1.1.0' + VERSION='1.1.1' cat >>confdefs.h <<_ACEOF @@ -13256,7 +13256,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by exfatprogs $as_me 1.1.0, which was +This file was extended by exfatprogs $as_me 1.1.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -13323,7 +13323,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -exfatprogs config.status 1.1.0 +exfatprogs config.status 1.1.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/dump/dump.c b/dump/dump.c index 1bd6ab2..85d5101 100644 --- a/dump/dump.c +++ b/dump/dump.c @@ -64,7 +64,7 @@ static unsigned int exfat_count_used_clusters(unsigned char *bitmap, return count; } -int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) +static int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) { struct pbr *ppbr; struct bsx64 *pbsx; @@ -79,14 +79,14 @@ int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) ppbr = malloc(bd->sector_size); if (!ppbr) { exfat_err("Cannot allocate pbr: out of memory\n"); - return -1; + return -ENOMEM; } /* read main boot sector */ ret = exfat_read_sector(bd, (char *)ppbr, BOOT_SEC_IDX); if (ret < 0) { exfat_err("main boot sector read failed\n"); - ret = -1; + ret = -EIO; goto free_ppbr; } @@ -96,19 +96,22 @@ int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) pbsx->sect_size_bits > EXFAT_MAX_SECT_SIZE_BITS) { exfat_err("bogus sector size bits : %u\n", pbsx->sect_size_bits); - return -EINVAL; + ret = -EINVAL; + goto free_ppbr; } if (pbsx->sect_per_clus_bits > 25 - pbsx->sect_size_bits) { exfat_err("bogus sectors bits per cluster : %u\n", pbsx->sect_per_clus_bits); - return -EINVAL; + ret = -EINVAL; + goto free_ppbr; } if (bd->sector_size != 1 << pbsx->sect_size_bits) { - exfat_err("bogus sectors size : %u(sector size bits : %u)\n", + exfat_err("bogus sector size : %u (sector size bits : %u)\n", bd->sector_size, pbsx->sect_size_bits); - + ret = -EINVAL; + goto free_ppbr; } clu_offset = le32_to_cpu(pbsx->clu_offset); @@ -148,7 +151,7 @@ int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) goto free_entry; } - volume_label = exfat_conv_volume_serial(&ed[0]); + volume_label = exfat_conv_volume_label(&ed[0]); if (!volume_label) { ret = -EINVAL; goto free_entry; @@ -177,6 +180,7 @@ int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) bitmap = malloc(bitmap_len); if (!bitmap) { exfat_err("bitmap allocation failed\n"); + ret = -ENOMEM; goto free_volume_label; } @@ -195,6 +199,7 @@ int exfat_show_ondisk_all_info(struct exfat_blk_dev *bd) exfat_info("Cluster size: \t\t\t\t%u\n", bd->cluster_size); exfat_info("Total Clusters: \t\t\t%u\n", total_clus); exfat_info("Free Clusters: \t\t\t\t%u\n", total_clus-used_clus); + ret = 0; free(bitmap); diff --git a/fsck/fsck.c b/fsck/fsck.c index 2485d2e..747a771 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -87,6 +87,7 @@ static void usage(char *name) fprintf(stderr, "\t-y | --repair-yes Repair without ask\n"); fprintf(stderr, "\t-n | --repair-no No repair\n"); fprintf(stderr, "\t-p | --repair-auto Repair automatically\n"); + fprintf(stderr, "\t-a Repair automatically\n"); fprintf(stderr, "\t-V | --version Show version\n"); fprintf(stderr, "\t-v | --verbose Print debug\n"); fprintf(stderr, "\t-h | --help Show help\n"); @@ -735,6 +736,7 @@ static int restore_boot_region(struct exfat_blk_dev *bd) { int i; char *sector; + int ret; sector = malloc(bd->sector_size); if (!sector) @@ -744,21 +746,31 @@ static int restore_boot_region(struct exfat_blk_dev *bd) if (exfat_read(bd->dev_fd, sector, bd->sector_size, BACKUP_BOOT_SEC_IDX * bd->sector_size + i * bd->sector_size) != - (ssize_t)bd->sector_size) - return -EIO; + (ssize_t)bd->sector_size) { + ret = -EIO; + goto free_sector; + } if (i == 0) ((struct pbr *)sector)->bsx.perc_in_use = 0xff; if (exfat_write(bd->dev_fd, sector, bd->sector_size, BOOT_SEC_IDX * bd->sector_size + i * bd->sector_size) != - (ssize_t)bd->sector_size) - return -EIO; + (ssize_t)bd->sector_size) { + ret = -EIO; + goto free_sector; + } } - if (fsync(bd->dev_fd)) - return -EIO; + + if (fsync(bd->dev_fd)) { + ret = -EIO; + goto free_sector; + } + ret = 0; + +free_sector: free(sector); - return 0; + return ret; } static int exfat_boot_region_check(struct exfat *exfat, struct pbr **bs) @@ -1465,7 +1477,7 @@ int main(int argc, char * const argv[]) exfat_err("failed to init locale/codeset\n"); opterr = 0; - while ((c = getopt_long(argc, argv, "rynpVvh", opts, NULL)) != EOF) { + while ((c = getopt_long(argc, argv, "arynpVvh", opts, NULL)) != EOF) { switch (c) { case 'n': if (ui.options & FSCK_OPTS_REPAIR_ALL) @@ -1482,6 +1494,7 @@ int main(int argc, char * const argv[]) usage(argv[0]); ui.options |= FSCK_OPTS_REPAIR_YES; break; + case 'a': case 'p': if (ui.options & FSCK_OPTS_REPAIR_ALL) usage(argv[0]); diff --git a/include/exfat_ondisk.h b/include/exfat_ondisk.h index 163bef0..74e3fb3 100644 --- a/include/exfat_ondisk.h +++ b/include/exfat_ondisk.h @@ -93,7 +93,7 @@ #define EXFAT_BAD_CLUSTER (0xFFFFFFF7U) #define EXFAT_FREE_CLUSTER (0) #define EXFAT_FIRST_CLUSTER (2) -#define EXFAT_REVERVED_CLUSTERS (2) +#define EXFAT_RESERVED_CLUSTERS (2) /* EXFAT BIOS parameter block (64 bytes) */ diff --git a/include/libexfat.h b/include/libexfat.h index c21dc06..0357d77 100644 --- a/include/libexfat.h +++ b/include/libexfat.h @@ -55,6 +55,7 @@ enum { struct exfat_blk_dev { int dev_fd; + unsigned long long offset; unsigned long long size; unsigned int sector_size; unsigned int sector_size_bits; @@ -69,6 +70,7 @@ struct exfat_user_input { unsigned int cluster_size; unsigned int sec_per_clu; unsigned int boundary_align; + bool pack_bitmap; bool quick; __u16 volume_label[VOLUME_LABEL_MAX_LEN]; int volume_label_len; @@ -104,7 +106,7 @@ int exfat_write_sector(struct exfat_blk_dev *bd, void *buf, unsigned int sec_off); int exfat_write_checksum_sector(struct exfat_blk_dev *bd, unsigned int checksum, bool is_backup); -char *exfat_conv_volume_serial(struct exfat_dentry *vol_entry); +char *exfat_conv_volume_label(struct exfat_dentry *vol_entry); int exfat_show_volume_serial(struct exfat_blk_dev *bd, struct exfat_user_input *ui); int exfat_set_volume_serial(struct exfat_blk_dev *bd, diff --git a/include/version.h b/include/version.h index e444145..ae6291f 100644 --- a/include/version.h +++ b/include/version.h @@ -5,6 +5,6 @@ #ifndef _VERSION_H -#define EXFAT_PROGS_VERSION "1.1.0" +#define EXFAT_PROGS_VERSION "1.1.1" #endif /* !_VERSION_H */ diff --git a/lib/libexfat.c b/lib/libexfat.c index bb1226d..13dfbaf 100644 --- a/lib/libexfat.c +++ b/lib/libexfat.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -19,18 +20,20 @@ #include "libexfat.h" #include "version.h" +#define BITS_PER_LONG (sizeof(long) * CHAR_BIT) + #ifdef WORDS_BIGENDIAN -#define BITOP_LE_SWIZZLE (~0x7) +#define BITOP_LE_SWIZZLE ((BITS_PER_LONG - 1) & ~0x7) #else #define BITOP_LE_SWIZZLE 0 #endif -#define BIT_MASK(nr) ((1) << ((nr) % 32)) -#define BIT_WORD(nr) ((nr) / 32) +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) unsigned int print_level = EXFAT_INFO; -static inline void set_bit(int nr, unsigned int *addr) +static inline void set_bit(int nr, void *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); @@ -38,7 +41,7 @@ static inline void set_bit(int nr, unsigned int *addr) *p |= mask; } -static inline void clear_bit(int nr, unsigned int *addr) +static inline void clear_bit(int nr, void *addr) { unsigned long mask = BIT_MASK(nr); unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); @@ -144,6 +147,8 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui, { int fd, ret = -1; off_t blk_dev_size; + struct stat st; + unsigned long long blk_dev_offset = 0; fd = open(ui->dev_name, ui->writeable ? O_RDWR|O_EXCL : O_RDONLY); if (fd < 0) { @@ -160,7 +165,27 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui, goto out; } + if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { + char pathname[sizeof("/sys/dev/block/4294967295:4294967295/start")]; + FILE *fp; + + snprintf(pathname, sizeof(pathname), "/sys/dev/block/%u:%u/start", + major(st.st_rdev), minor(st.st_rdev)); + fp = fopen(pathname, "r"); + if (fp != NULL) { + if (fscanf(fp, "%llu", &blk_dev_offset) == 1) { + /* + * Linux kernel always reports partition offset + * in 512-byte units, regardless of sector size + */ + blk_dev_offset <<= 9; + } + fclose(fp); + } + } + bd->dev_fd = fd; + bd->offset = blk_dev_offset; bd->size = blk_dev_size; if (!ui->cluster_size) exfat_set_default_cluster_size(bd, ui); @@ -175,7 +200,8 @@ int exfat_get_blk_dev_info(struct exfat_user_input *ui, bd->num_clusters = blk_dev_size / ui->cluster_size; exfat_debug("Block device name : %s\n", ui->dev_name); - exfat_debug("Block device size : %lld\n", bd->size); + exfat_debug("Block device offset : %llu\n", bd->offset); + exfat_debug("Block device size : %llu\n", bd->size); exfat_debug("Block sector size : %u\n", bd->sector_size); exfat_debug("Number of the sectors : %llu\n", bd->num_sectors); @@ -344,19 +370,20 @@ off_t exfat_get_root_entry_offset(struct exfat_blk_dev *bd) nbytes = exfat_read(bd->dev_fd, bs, sizeof(struct pbr), 0); if (nbytes != sizeof(struct pbr)) { exfat_err("boot sector read failed: %d\n", errno); + free(bs); return -1; } cluster_size = (1 << bs->bsx.sect_per_clus_bits) * bd->sector_size; root_clu_off = le32_to_cpu(bs->bsx.clu_offset) * bd->sector_size + - le32_to_cpu(bs->bsx.root_cluster - EXFAT_REVERVED_CLUSTERS) + le32_to_cpu(bs->bsx.root_cluster - EXFAT_RESERVED_CLUSTERS) * cluster_size; free(bs); return root_clu_off; } -char *exfat_conv_volume_serial(struct exfat_dentry *vol_entry) +char *exfat_conv_volume_label(struct exfat_dentry *vol_entry) { char *volume_label; __le16 disk_label[VOLUME_LABEL_MAX_LEN]; @@ -370,6 +397,7 @@ char *exfat_conv_volume_serial(struct exfat_dentry *vol_entry) if (exfat_utf16_dec(disk_label, vol_entry->vol_char_cnt*2, volume_label, VOLUME_LABEL_BUFFER_SIZE) < 0) { exfat_err("failed to decode volume label\n"); + free(volume_label); return NULL; } @@ -392,16 +420,20 @@ int exfat_show_volume_label(struct exfat_blk_dev *bd, off_t root_clu_off) sizeof(struct exfat_dentry), root_clu_off); if (nbytes != sizeof(struct exfat_dentry)) { exfat_err("volume entry read failed: %d\n", errno); + free(vol_entry); return -1; } - volume_label = exfat_conv_volume_serial(vol_entry); - if (!volume_label) + volume_label = exfat_conv_volume_label(vol_entry); + if (!volume_label) { + free(vol_entry); return -EINVAL; + } exfat_info("label: %s\n", volume_label); free(volume_label); + free(vol_entry); return 0; } @@ -626,5 +658,5 @@ unsigned int exfat_clus_to_blk_dev_off(struct exfat_blk_dev *bd, unsigned int clu_off_sectnr, unsigned int clu) { return clu_off_sectnr * bd->sector_size + - (clu - EXFAT_REVERVED_CLUSTERS) * bd->cluster_size; + (clu - EXFAT_RESERVED_CLUSTERS) * bd->cluster_size; } diff --git a/manpages/fsck.exfat.8 b/manpages/fsck.exfat.8 index ca551d3..83f7815 100644 --- a/manpages/fsck.exfat.8 +++ b/manpages/fsck.exfat.8 @@ -4,6 +4,8 @@ fsck.exfat \- check an exFAT filesystem .SH SYNOPSIS .B fsck.exfat [ +.B \-a +] [ .B \-n ] [ .B \-r @@ -24,6 +26,9 @@ depending on the options passed. .PP .SH OPTIONS .TP +.BI \-a +This option does the same thing as the -p option. It is provided for backwards compatibility only; it is suggested that people use -p option whenever possible. +.TP .BI \-n Check the filesystem but do not attempt to repair the filesystem. .TP diff --git a/manpages/mkfs.exfat.8 b/manpages/mkfs.exfat.8 index 0bd8ffc..9f867d3 100644 --- a/manpages/mkfs.exfat.8 +++ b/manpages/mkfs.exfat.8 @@ -17,6 +17,8 @@ mkfs.exfat \- create an exFAT filesystem .B \-L .I volume_label ] [ +.B \-\-pack\-bitmap +] [ .B \-v ] .I device @@ -39,30 +41,70 @@ SCSI disk, use: .PP .SH OPTIONS .TP -.BI \-b " boundary_alignment" -Specify the alignment for FAT and start of cluster. -Boundary alignment can be specified in m/M for megabytes -and k/K for kilobytes. It should be a power of two. -Some media like sdcard need this. +.BR \-b ", " \-\-boundary\-align =\fIalignment\fR +Specifies the alignment for the FAT and the start of the cluster heap. +The \fIalignment\fR argument is specified in bytes or may be specified with +\fBm\fR/\fBM\fR suffix for mebibytes or \fBk\fR/\fBK\fR suffix for kibibytes +and should be a power of two. +Some media like SD cards need this for optimal performance and endurance, +in which case \fIalignment\fR should be set to half of the card's native +boundary unit size. +If the card's native boundary unit size is not known, refer to the following +table of boundary unit sizes recommended by the SD Card Association. +.\" source: SD Specifications Part 2: File System Specification Version 3.00 +.TS +center; +cb1s6cbcb,nnnn. +Card Capacity Range Cluster Size Boundary Unit +_ + \[<=]8 MiB 8 KiB 8 KiB +>8 MiB \[<=]64 MiB 16 KiB 16 KiB +>64 MiB \[<=]256 MiB 16 KiB 32 KiB +>256 MiB \[<=]1 GiB 16 KiB 64 KiB +>1 GiB \[<=]2 GiB 32 KiB 64 KiB +>2 GiB \[<=]32 GiB 32 KiB 4 MiB +>32 GiB \[<=]128 GiB 128 KiB 16 MiB +>128 GiB \[<=]512 GiB 256 KiB 32 MiB +>512 GiB \[<=]2 TiB 512 KiB 64 MiB +.TE .TP -.BI \-c " cluster_size" -Specify the cluster size. Cluster size can be specified in m/M for megabytes -and k/K for kilobytes. +.BR \-c ", " \-\-cluster\-size =\fIsize\fR +Specifies the cluster size of the exFAT file system. +The \fIsize\fR argument is specified in bytes or may be specified with +\fBm\fR/\fBM\fR suffix for mebibytes or \fBk\fR/\fBK\fR suffix for kibibytes +and must be a power of two. .TP -.BI \-f -Performs a full format. This zeros the entire disk device while -creating the exFAT filesystem. +.BR \-f ", " \-\-full\-format +Performs a full format. +This zeros the entire disk device while creating the exFAT filesystem. .TP -.BI \-h +.BR \-h ", " \-\-help Prints the help and exit. .TP -.BI \-L " volume_label" -Specifies the volume label associated with the exFAT filesystem. +.BR \-L ", " \-\-volume\-label =\fIlabel\fR +Specifies the volume label to be associated with the exFAT filesystem. +.TP +.B \-\-pack\-bitmap +Attempts to relocate the exFAT allocation bitmap so that it ends at the +alignment boundary immediately following the FAT rather than beginning at that +boundary. +This strictly violates the SD card specification but may improve performance +and endurance on SD cards and other flash media not designed for use with exFAT +by allowing file-system metadata updates to touch fewer flash allocation units. +Furthermore, many SD cards and other flash devices specially optimize the +allocation unit where the FAT resides so as to support tiny writes with reduced +write amplification but expect only larger writes in subsequent allocation +units \[em] where the exFAT bitmap would be placed by default. +Specifying \fB\-\-pack\-bitmap\fR attempts to avoid the potential problems +associated with issuing many small writes to the bitmap by making it share an +allocation unit with the FAT. +If there is insufficient space for the bitmap there, then this option will have +no effect, and the bitmap will be aligned at the boundary as by default. .TP -.BI \-v +.BR \-v ", " \-\-verbose Prints verbose debugging information while creating the exFAT filesystem. .TP -.B \-V +.BR \-V ", " \-\-version Prints the version number and exits. .SH SEE ALSO .BR mkfs (8), diff --git a/manpages/tune.exfat.8 b/manpages/tune.exfat.8 index 2816f45..865dc07 100644 --- a/manpages/tune.exfat.8 +++ b/manpages/tune.exfat.8 @@ -33,12 +33,12 @@ Print the volume label of the exFAT filesystem. .BI \-L " set-label" Set the volume label of the filesystem to the provided argument. .TP -.TP .BI \-i " print-serial" Print the volume serial of the exFAT filesystem. .TP .BI \-I " set-serial" Set the volume serial of the filesystem to the provided argument. +.TP .BI \-v Prints verbose debugging information while extracting or tuning parameters of the exFAT filesystem. .TP diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 4f9616d..1837669 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,7 @@ static void exfat_setup_boot_sector(struct pbr *ppbr, memset(pbpb->res_zero, 0, 53); /* Fill exfat extend BIOS paramemter block */ - pbsx->vol_offset = 0; + pbsx->vol_offset = cpu_to_le64(bd->offset / bd->sector_size); pbsx->vol_length = cpu_to_le64(bd->size / bd->sector_size); pbsx->fat_offset = cpu_to_le32(finfo.fat_byte_off / bd->sector_size); pbsx->fat_length = cpu_to_le32(finfo.fat_byte_len / bd->sector_size); @@ -76,6 +77,8 @@ static void exfat_setup_boot_sector(struct pbr *ppbr, memset(ppbr->boot_code, 0, 390); ppbr->signature = cpu_to_le16(PBR_SIGNATURE); + exfat_debug("Volume Offset(sectors) : %" PRIu64 "\n", + le64_to_cpu(pbsx->vol_offset)); exfat_debug("Volume Length(sectors) : %" PRIu64 "\n", le64_to_cpu(pbsx->vol_length)); exfat_debug("FAT Offset(sector offset) : %u\n", @@ -358,22 +361,27 @@ static int exfat_create_root_dir(struct exfat_blk_dev *bd, static void usage(void) { - fprintf(stderr, "Usage: mkfs.exfat\n"); - fprintf(stderr, "\t-L | --volume-label=label Set volume label\n"); - fprintf(stderr, "\t-c | --cluster-size=size(or suffixed by 'K' or 'M') Specify cluster size\n"); - fprintf(stderr, "\t-b | --boundary-align=size(or suffixed by 'K' or 'M') Specify boundary alignment\n"); - fprintf(stderr, "\t-f | --full-format Full format\n"); - fprintf(stderr, "\t-V | --version Show version\n"); - fprintf(stderr, "\t-v | --verbose Print debug\n"); - fprintf(stderr, "\t-h | --help Show help\n"); + fputs("Usage: mkfs.exfat\n" + "\t-L | --volume-label=label Set volume label\n" + "\t-c | --cluster-size=size(or suffixed by 'K' or 'M') Specify cluster size\n" + "\t-b | --boundary-align=size(or suffixed by 'K' or 'M') Specify boundary alignment\n" + "\t --pack-bitmap Move bitmap into FAT segment\n" + "\t-f | --full-format Full format\n" + "\t-V | --version Show version\n" + "\t-v | --verbose Print debug\n" + "\t-h | --help Show help\n", + stderr); exit(EXIT_FAILURE); } -static struct option opts[] = { +#define PACK_BITMAP (CHAR_MAX + 1) + +static const struct option opts[] = { {"volume-label", required_argument, NULL, 'L' }, {"cluster-size", required_argument, NULL, 'c' }, {"boundary-align", required_argument, NULL, 'b' }, + {"pack-bitmap", no_argument, NULL, PACK_BITMAP }, {"full-format", no_argument, NULL, 'f' }, {"version", no_argument, NULL, 'V' }, {"verbose", no_argument, NULL, 'v' }, @@ -382,9 +390,43 @@ static struct option opts[] = { {NULL, 0, NULL, 0 } }; +/* + * Moves the bitmap to just before the alignment boundary if there is space + * between the boundary and the end of the FAT. This may allow the FAT and the + * bitmap to share the same allocation unit on flash media, thereby improving + * performance and endurance. + */ +static int exfat_pack_bitmap(const struct exfat_user_input *ui) +{ + unsigned int fat_byte_end = finfo.fat_byte_off + finfo.fat_byte_len, + bitmap_byte_len = finfo.bitmap_byte_len, + bitmap_clu_len = round_up(bitmap_byte_len, ui->cluster_size), + bitmap_clu_cnt, total_clu_cnt, new_bitmap_clu_len; + + for (;;) { + bitmap_clu_cnt = bitmap_clu_len / ui->cluster_size; + if (finfo.clu_byte_off - bitmap_clu_len < fat_byte_end || + finfo.total_clu_cnt > EXFAT_MAX_NUM_CLUSTER - + bitmap_clu_cnt) + return -1; + total_clu_cnt = finfo.total_clu_cnt + bitmap_clu_cnt; + bitmap_byte_len = round_up(total_clu_cnt, 8) / 8; + new_bitmap_clu_len = round_up(bitmap_byte_len, ui->cluster_size); + if (new_bitmap_clu_len == bitmap_clu_len) { + finfo.clu_byte_off -= bitmap_clu_len; + finfo.total_clu_cnt = total_clu_cnt; + finfo.bitmap_byte_off -= bitmap_clu_len; + finfo.bitmap_byte_len = bitmap_byte_len; + return 0; + } + bitmap_clu_len = new_bitmap_clu_len; + } +} + static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, struct exfat_user_input *ui) { + unsigned long long total_clu_cnt; int clu_len; if (ui->boundary_align < bd->sector_size) { @@ -392,25 +434,27 @@ static int exfat_build_mkfs_info(struct exfat_blk_dev *bd, bd->sector_size); return -1; } - finfo.fat_byte_off = round_up(24 * bd->sector_size, - ui->boundary_align); + finfo.fat_byte_off = round_up(bd->offset + 24 * bd->sector_size, + ui->boundary_align) - bd->offset; finfo.fat_byte_len = round_up((bd->num_clusters * sizeof(int)), ui->cluster_size); - finfo.clu_byte_off = round_up(finfo.fat_byte_off + finfo.fat_byte_len, - ui->boundary_align); + finfo.clu_byte_off = round_up(bd->offset + finfo.fat_byte_off + + finfo.fat_byte_len, ui->boundary_align) - bd->offset; if (bd->size <= finfo.clu_byte_off) { exfat_err("boundary alignment is too big\n"); return -1; } - finfo.total_clu_cnt = (bd->size - finfo.clu_byte_off) / - ui->cluster_size; - if (finfo.total_clu_cnt > EXFAT_MAX_NUM_CLUSTER) { + total_clu_cnt = (bd->size - finfo.clu_byte_off) / ui->cluster_size; + if (total_clu_cnt > EXFAT_MAX_NUM_CLUSTER) { exfat_err("cluster size is too small\n"); return -1; } + finfo.total_clu_cnt = (unsigned int) total_clu_cnt; finfo.bitmap_byte_off = finfo.clu_byte_off; finfo.bitmap_byte_len = round_up(finfo.total_clu_cnt, 8) / 8; + if (ui->pack_bitmap) + exfat_pack_bitmap(ui); clu_len = round_up(finfo.bitmap_byte_len, ui->cluster_size); finfo.ut_start_clu = EXFAT_FIRST_CLUSTER + clu_len / ui->cluster_size; @@ -592,6 +636,9 @@ int main(int argc, char *argv[]) } ui.boundary_align = ret; break; + case PACK_BITMAP: + ui.pack_bitmap = true; + break; case 'f': ui.quick = false; break; diff --git a/tune/tune.c b/tune/tune.c index 7989e95..ab04e69 100644 --- a/tune/tune.c +++ b/tune/tune.c @@ -20,7 +20,7 @@ static void usage(void) fprintf(stderr, "\t-l | --print-label Print volume label\n"); fprintf(stderr, "\t-L | --set-label=label Set volume label\n"); fprintf(stderr, "\t-i | --print-serial Print volume serial\n"); - fprintf(stderr, "\t-L | --set-serial=value Set volume serial\n"); + fprintf(stderr, "\t-I | --set-serial=value Set volume serial\n"); fprintf(stderr, "\t-V | --version Show version\n"); fprintf(stderr, "\t-v | --verbose Print debug\n"); fprintf(stderr, "\t-h | --help Show help\n"); -- 2.39.5