(root)/
util-linux-2.39/
libblkid/
src/
superblocks/
exfat.c
       1  /*
       2   * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com>
       3   *
       4   * This file may be redistributed under the terms of the
       5   * GNU Lesser General Public License.
       6   */
       7  #include "superblocks.h"
       8  
       9  struct exfat_super_block {
      10  	uint8_t JumpBoot[3];
      11  	uint8_t FileSystemName[8];
      12  	uint8_t MustBeZero[53];
      13  	uint64_t PartitionOffset;
      14  	uint64_t VolumeLength;
      15  	uint32_t FatOffset;
      16  	uint32_t FatLength;
      17  	uint32_t ClusterHeapOffset;
      18  	uint32_t ClusterCount;
      19  	uint32_t FirstClusterOfRootDirectory;
      20  	uint8_t VolumeSerialNumber[4];
      21  	struct {
      22  		uint8_t vermin;
      23  		uint8_t vermaj;
      24  	} FileSystemRevision;
      25  	uint16_t VolumeFlags;
      26  	uint8_t BytesPerSectorShift;
      27  	uint8_t SectorsPerClusterShift;
      28  	uint8_t NumberOfFats;
      29  	uint8_t DriveSelect;
      30  	uint8_t PercentInUse;
      31  	uint8_t Reserved[7];
      32  	uint8_t BootCode[390];
      33  	uint16_t BootSignature;
      34  } __attribute__((__packed__));
      35  
      36  struct exfat_entry_label {
      37  	uint8_t type;
      38  	uint8_t length;
      39  	uint8_t name[22];
      40  	uint8_t reserved[8];
      41  } __attribute__((__packed__));
      42  
      43  #define BLOCK_SIZE(sb) ((sb)->BytesPerSectorShift < 32 ? (1u << (sb)->BytesPerSectorShift) : 0)
      44  #define CLUSTER_SIZE(sb) ((sb)->SectorsPerClusterShift < 32 ? (BLOCK_SIZE(sb) << (sb)->SectorsPerClusterShift) : 0)
      45  #define EXFAT_FIRST_DATA_CLUSTER 2
      46  #define EXFAT_LAST_DATA_CLUSTER 0xffffff6
      47  #define EXFAT_ENTRY_SIZE 32
      48  
      49  #define EXFAT_ENTRY_EOD		0x00
      50  #define EXFAT_ENTRY_LABEL	0x83
      51  
      52  static uint64_t block_to_offset(const struct exfat_super_block *sb,
      53  		uint64_t block)
      54  {
      55  	return block << sb->BytesPerSectorShift;
      56  }
      57  
      58  static uint64_t cluster_to_block(const struct exfat_super_block *sb,
      59  		uint32_t cluster)
      60  {
      61  	return le32_to_cpu(sb->ClusterHeapOffset) +
      62  			((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
      63  					<< sb->SectorsPerClusterShift);
      64  }
      65  
      66  static uint64_t cluster_to_offset(const struct exfat_super_block *sb,
      67  		uint32_t cluster)
      68  {
      69  	return block_to_offset(sb, cluster_to_block(sb, cluster));
      70  }
      71  
      72  static uint32_t next_cluster(blkid_probe pr,
      73  		const struct exfat_super_block *sb, uint32_t cluster)
      74  {
      75  	uint32_t *nextp, next;
      76  	uint64_t fat_offset;
      77  
      78  	fat_offset = block_to_offset(sb, le32_to_cpu(sb->FatOffset))
      79  		+ (uint64_t) cluster * sizeof(cluster);
      80  	nextp = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
      81  			sizeof(uint32_t));
      82  	if (!nextp)
      83  		return 0;
      84  	memcpy(&next, nextp, sizeof(next));
      85  	return le32_to_cpu(next);
      86  }
      87  
      88  static struct exfat_entry_label *find_label(blkid_probe pr,
      89  		const struct exfat_super_block *sb)
      90  {
      91  	uint32_t cluster = le32_to_cpu(sb->FirstClusterOfRootDirectory);
      92  	uint64_t offset = cluster_to_offset(sb, cluster);
      93  	uint8_t *entry;
      94  	const size_t max_iter = 10000;
      95  	size_t i = 0;
      96  
      97  	for (; i < max_iter; i++) {
      98  		entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
      99  				EXFAT_ENTRY_SIZE);
     100  		if (!entry)
     101  			return NULL;
     102  		if (entry[0] == EXFAT_ENTRY_EOD)
     103  			return NULL;
     104  		if (entry[0] == EXFAT_ENTRY_LABEL)
     105  			return (struct exfat_entry_label *) entry;
     106  
     107  		offset += EXFAT_ENTRY_SIZE;
     108  		if (CLUSTER_SIZE(sb) && (offset % CLUSTER_SIZE(sb)) == 0) {
     109  			cluster = next_cluster(pr, sb, cluster);
     110  			if (cluster < EXFAT_FIRST_DATA_CLUSTER)
     111  				return NULL;
     112  			if (cluster > EXFAT_LAST_DATA_CLUSTER)
     113  				return NULL;
     114  			offset = cluster_to_offset(sb, cluster);
     115  		}
     116  	}
     117  
     118  	return NULL;
     119  }
     120  
     121  /* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */
     122  static uint32_t exfat_boot_checksum(unsigned char *sectors,
     123  				    size_t sector_size)
     124  {
     125  	uint32_t n_bytes = sector_size * 11;
     126  	uint32_t checksum = 0;
     127  
     128  	for (size_t i = 0; i < n_bytes; i++) {
     129  		if ((i == 106) || (i == 107) || (i == 112))
     130  			continue;
     131  
     132  		checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1)
     133  			+ (uint32_t) sectors[i];
     134  	}
     135  
     136  	return checksum;
     137  }
     138  
     139  static int exfat_validate_checksum(blkid_probe pr,
     140  		const struct exfat_super_block *sb)
     141  {
     142  	size_t sector_size = BLOCK_SIZE(sb);
     143  	/* 11 sectors will be checksummed, the 12th contains the expected */
     144  	unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12);
     145  	if (!data)
     146  		return 0;
     147  
     148  	uint32_t checksum = exfat_boot_checksum(data, sector_size);
     149  
     150  	/* The expected checksum is repeated, check all of them */
     151  	for (size_t i = 0; i < sector_size / sizeof(uint32_t); i++) {
     152  		size_t offset = sector_size * 11 + i * 4;
     153  		uint32_t *expected_addr = (uint32_t *) &data[offset];
     154  		uint32_t expected = le32_to_cpu(*expected_addr);
     155  		if (!blkid_probe_verify_csum(pr, checksum, expected))
     156  			return 0;
     157  	};
     158  
     159  	return 1;
     160  }
     161  
     162  static int exfat_valid_superblock(blkid_probe pr, const struct exfat_super_block *sb)
     163  {
     164  	if (le16_to_cpu(sb->BootSignature) != 0xAA55)
     165  		return 0;
     166  
     167  	if (!CLUSTER_SIZE(sb))
     168  		return 0;
     169  
     170  	if (memcmp(sb->JumpBoot, "\xEB\x76\x90", 3) != 0)
     171  		return 0;
     172  
     173  	for (size_t i = 0; i < sizeof(sb->MustBeZero); i++)
     174  		if (sb->MustBeZero[i] != 0x00)
     175  			return 0;
     176  
     177  	if (!exfat_validate_checksum(pr, sb))
     178  		return 0;
     179  
     180  	return 1;
     181  }
     182  
     183  /* function prototype to avoid warnings (duplicate in partitions/dos.c) */
     184  extern int blkid_probe_is_exfat(blkid_probe pr);
     185  
     186  /*
     187   * This function is used by MBR partition table parser to avoid
     188   * misinterpretation of exFAT filesystem.
     189   */
     190  int blkid_probe_is_exfat(blkid_probe pr)
     191  {
     192  	struct exfat_super_block *sb;
     193  	const struct blkid_idmag *mag = NULL;
     194  	int rc;
     195  
     196  	rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag);
     197  	if (rc < 0)
     198  		return rc;	/* error */
     199  	if (rc != BLKID_PROBE_OK || !mag)
     200  		return 0;
     201  
     202  	sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
     203  	if (!sb)
     204  		return 0;
     205  
     206  	if (memcmp(sb->FileSystemName, "EXFAT   ", 8) != 0)
     207  		return 0;
     208  
     209  	return exfat_valid_superblock(pr, sb);
     210  }
     211  
     212  static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
     213  {
     214  	struct exfat_super_block *sb;
     215  	struct exfat_entry_label *label;
     216  
     217  	sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
     218  	if (!sb)
     219  		return errno ? -errno : BLKID_PROBE_NONE;
     220  
     221  	if (!exfat_valid_superblock(pr, sb))
     222  		return BLKID_PROBE_NONE;
     223  
     224  	label = find_label(pr, sb);
     225  	if (label)
     226  		blkid_probe_set_utf8label(pr, label->name,
     227  				min((size_t) label->length * 2, sizeof(label->name)),
     228  				UL_ENCODE_UTF16LE);
     229  	else if (errno)
     230  		return -errno;
     231  
     232  	blkid_probe_sprintf_uuid(pr, sb->VolumeSerialNumber, 4,
     233  			"%02hhX%02hhX-%02hhX%02hhX",
     234  			sb->VolumeSerialNumber[3], sb->VolumeSerialNumber[2],
     235  			sb->VolumeSerialNumber[1], sb->VolumeSerialNumber[0]);
     236  
     237  	blkid_probe_sprintf_version(pr, "%u.%u",
     238  			sb->FileSystemRevision.vermaj, sb->FileSystemRevision.vermin);
     239  
     240  	blkid_probe_set_fsblocksize(pr, BLOCK_SIZE(sb));
     241  	blkid_probe_set_block_size(pr, BLOCK_SIZE(sb));
     242  	blkid_probe_set_fssize(pr, BLOCK_SIZE(sb) * le64_to_cpu(sb->VolumeLength));
     243  
     244  	return BLKID_PROBE_OK;
     245  }
     246  
     247  const struct blkid_idinfo exfat_idinfo =
     248  {
     249  	.name		= "exfat",
     250  	.usage		= BLKID_USAGE_FILESYSTEM,
     251  	.probefunc	= probe_exfat,
     252  	.magics		=
     253  	{
     254  		{ .magic = "EXFAT   ", .len = 8, .sboff = 3 },
     255  		{ NULL }
     256  	}
     257  };