xref: /freebsd/sys/geom/label/g_label_msdosfs.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1e1237b28SPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
33728855aSPedro F. Giffuni  *
4e1237b28SPawel Jakub Dawidek  * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
518944721SPawel Jakub Dawidek  * Copyright (c) 2006 Tobias Reifenberger
6e1237b28SPawel Jakub Dawidek  * All rights reserved.
7e1237b28SPawel Jakub Dawidek  *
8e1237b28SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
9e1237b28SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
10e1237b28SPawel Jakub Dawidek  * are met:
11e1237b28SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
12e1237b28SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
13e1237b28SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
14e1237b28SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
15e1237b28SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
16e1237b28SPawel Jakub Dawidek  *
17e1237b28SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18e1237b28SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19e1237b28SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20e1237b28SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21e1237b28SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e1237b28SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23e1237b28SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24e1237b28SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25e1237b28SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26e1237b28SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27e1237b28SPawel Jakub Dawidek  * SUCH DAMAGE.
28e1237b28SPawel Jakub Dawidek  */
29e1237b28SPawel Jakub Dawidek 
30e1237b28SPawel Jakub Dawidek #include <sys/param.h>
31e1237b28SPawel Jakub Dawidek #include <sys/systm.h>
32e1237b28SPawel Jakub Dawidek #include <sys/kernel.h>
33e1237b28SPawel Jakub Dawidek #include <sys/malloc.h>
34e1237b28SPawel Jakub Dawidek 
35e1237b28SPawel Jakub Dawidek #include <geom/geom.h>
36ac03832eSConrad Meyer #include <geom/geom_dbg.h>
37e1237b28SPawel Jakub Dawidek #include <geom/label/g_label.h>
3818944721SPawel Jakub Dawidek #include <geom/label/g_label_msdosfs.h>
39e1237b28SPawel Jakub Dawidek 
4018944721SPawel Jakub Dawidek #define LABEL_NO_NAME		"NO NAME    "
41e1237b28SPawel Jakub Dawidek 
42e1237b28SPawel Jakub Dawidek static void
g_label_msdosfs_taste(struct g_consumer * cp,char * label,size_t size)43e1237b28SPawel Jakub Dawidek g_label_msdosfs_taste(struct g_consumer *cp, char *label, size_t size)
44e1237b28SPawel Jakub Dawidek {
45e1237b28SPawel Jakub Dawidek 	struct g_provider *pp;
4618944721SPawel Jakub Dawidek 	FAT_BSBPB *pfat_bsbpb;
4718944721SPawel Jakub Dawidek 	FAT32_BSBPB *pfat32_bsbpb;
4818944721SPawel Jakub Dawidek 	FAT_DES *pfat_entry;
4918944721SPawel Jakub Dawidek 	uint8_t *sector0, *sector;
5034fb1c13SJessica Clarke 	size_t copysize;
51e1237b28SPawel Jakub Dawidek 
52e1237b28SPawel Jakub Dawidek 	g_topology_assert_not();
53e1237b28SPawel Jakub Dawidek 	pp = cp->provider;
5418944721SPawel Jakub Dawidek 	sector0 = NULL;
5518944721SPawel Jakub Dawidek 	sector = NULL;
56e1237b28SPawel Jakub Dawidek 	bzero(label, size);
5718944721SPawel Jakub Dawidek 
5818944721SPawel Jakub Dawidek 	/* Check if the sector size of the medium is a valid FAT sector size. */
5918944721SPawel Jakub Dawidek 	switch(pp->sectorsize) {
6018944721SPawel Jakub Dawidek 	case 512:
6118944721SPawel Jakub Dawidek 	case 1024:
6218944721SPawel Jakub Dawidek 	case 2048:
6318944721SPawel Jakub Dawidek 	case 4096:
6418944721SPawel Jakub Dawidek 		break;
6518944721SPawel Jakub Dawidek 	default:
6618944721SPawel Jakub Dawidek 		G_LABEL_DEBUG(1, "MSDOSFS: %s: sector size %d not compatible.",
6718944721SPawel Jakub Dawidek 		    pp->name, pp->sectorsize);
6818944721SPawel Jakub Dawidek 		return;
6918944721SPawel Jakub Dawidek 	}
7018944721SPawel Jakub Dawidek 
7118944721SPawel Jakub Dawidek 	/* Load 1st sector with boot sector and boot parameter block. */
7218944721SPawel Jakub Dawidek 	sector0 = (uint8_t *)g_read_data(cp, 0, pp->sectorsize, NULL);
7318944721SPawel Jakub Dawidek 	if (sector0 == NULL)
7418944721SPawel Jakub Dawidek 		return;
7518944721SPawel Jakub Dawidek 
7618944721SPawel Jakub Dawidek 	/* Check for the FAT boot sector signature. */
7718944721SPawel Jakub Dawidek 	if (sector0[510] != 0x55 || sector0[511] != 0xaa) {
7818944721SPawel Jakub Dawidek 		G_LABEL_DEBUG(1, "MSDOSFS: %s: no FAT signature found.",
7918944721SPawel Jakub Dawidek 		    pp->name);
8018944721SPawel Jakub Dawidek 		goto error;
8118944721SPawel Jakub Dawidek 	}
8218944721SPawel Jakub Dawidek 
8318944721SPawel Jakub Dawidek 	/*
8418944721SPawel Jakub Dawidek 	 * Test if this is really a FAT volume and determine the FAT type.
8518944721SPawel Jakub Dawidek 	 */
8618944721SPawel Jakub Dawidek 
8718944721SPawel Jakub Dawidek 	pfat_bsbpb = (FAT_BSBPB *)sector0;
8818944721SPawel Jakub Dawidek 	pfat32_bsbpb = (FAT32_BSBPB *)sector0;
8918944721SPawel Jakub Dawidek 
9018944721SPawel Jakub Dawidek 	if (UINT16BYTES(pfat_bsbpb->BPB_FATSz16) != 0) {
9118944721SPawel Jakub Dawidek 		/*
9218944721SPawel Jakub Dawidek 		 * If the BPB_FATSz16 field is not zero and the string "FAT" is
9318944721SPawel Jakub Dawidek 		 * at the right place, this should be a FAT12 or FAT16 volume.
9418944721SPawel Jakub Dawidek 		 */
9518944721SPawel Jakub Dawidek 		if (strncmp(pfat_bsbpb->BS_FilSysType, "FAT", 3) != 0) {
9618944721SPawel Jakub Dawidek 			G_LABEL_DEBUG(1,
9718944721SPawel Jakub Dawidek 			    "MSDOSFS: %s: FAT12/16 volume not valid.",
9818944721SPawel Jakub Dawidek 			    pp->name);
9918944721SPawel Jakub Dawidek 			goto error;
10018944721SPawel Jakub Dawidek 		}
10118944721SPawel Jakub Dawidek 		G_LABEL_DEBUG(1, "MSDOSFS: %s: FAT12/FAT16 volume detected.",
10218944721SPawel Jakub Dawidek 		    pp->name);
10318944721SPawel Jakub Dawidek 
10418944721SPawel Jakub Dawidek 		/* A volume with no name should have "NO NAME    " as label. */
10518944721SPawel Jakub Dawidek 		if (strncmp(pfat_bsbpb->BS_VolLab, LABEL_NO_NAME,
10618944721SPawel Jakub Dawidek 		    sizeof(pfat_bsbpb->BS_VolLab)) == 0) {
10718944721SPawel Jakub Dawidek 			G_LABEL_DEBUG(1,
10818944721SPawel Jakub Dawidek 			    "MSDOSFS: %s: FAT12/16 volume has no name.",
10918944721SPawel Jakub Dawidek 			    pp->name);
11018944721SPawel Jakub Dawidek 			goto error;
11118944721SPawel Jakub Dawidek 		}
11234fb1c13SJessica Clarke 		copysize = MIN(size - 1, sizeof(pfat_bsbpb->BS_VolLab));
11334fb1c13SJessica Clarke 		memcpy(label, pfat_bsbpb->BS_VolLab, copysize);
11434fb1c13SJessica Clarke 		label[copysize] = '\0';
11518944721SPawel Jakub Dawidek 	} else if (UINT32BYTES(pfat32_bsbpb->BPB_FATSz32) != 0) {
11618944721SPawel Jakub Dawidek 		uint32_t fat_FirstDataSector, fat_BytesPerSector, offset;
11718944721SPawel Jakub Dawidek 
11818944721SPawel Jakub Dawidek 		/*
11918944721SPawel Jakub Dawidek 		 * If the BPB_FATSz32 field is not zero and the string "FAT" is
12018944721SPawel Jakub Dawidek 		 * at the right place, this should be a FAT32 volume.
12118944721SPawel Jakub Dawidek 		 */
12218944721SPawel Jakub Dawidek 		if (strncmp(pfat32_bsbpb->BS_FilSysType, "FAT", 3) != 0) {
12318944721SPawel Jakub Dawidek 			G_LABEL_DEBUG(1, "MSDOSFS: %s: FAT32 volume not valid.",
12418944721SPawel Jakub Dawidek 			    pp->name);
12518944721SPawel Jakub Dawidek 			goto error;
12618944721SPawel Jakub Dawidek 		}
12718944721SPawel Jakub Dawidek 		G_LABEL_DEBUG(1, "MSDOSFS: %s: FAT32 volume detected.",
12818944721SPawel Jakub Dawidek 		    pp->name);
12918944721SPawel Jakub Dawidek 
13018944721SPawel Jakub Dawidek 		/*
13118944721SPawel Jakub Dawidek 		 * If the volume label is not "NO NAME    " we're done.
13218944721SPawel Jakub Dawidek 		 */
13318944721SPawel Jakub Dawidek 		if (strncmp(pfat32_bsbpb->BS_VolLab, LABEL_NO_NAME,
13418944721SPawel Jakub Dawidek 		    sizeof(pfat32_bsbpb->BS_VolLab)) != 0) {
13534fb1c13SJessica Clarke 			copysize = MIN(size - 1,
13663d24336SJessica Clarke 			    sizeof(pfat32_bsbpb->BS_VolLab));
13734fb1c13SJessica Clarke 			memcpy(label, pfat32_bsbpb->BS_VolLab, copysize);
13834fb1c13SJessica Clarke 			label[copysize] = '\0';
13918944721SPawel Jakub Dawidek 			goto endofchecks;
14018944721SPawel Jakub Dawidek 		}
14118944721SPawel Jakub Dawidek 
14218944721SPawel Jakub Dawidek 		/*
14318944721SPawel Jakub Dawidek 		 * If the volume label "NO NAME    " is in the boot sector, the
14418944721SPawel Jakub Dawidek 		 * label of FAT32 volumes may be stored as a special entry in
14518944721SPawel Jakub Dawidek 		 * the root directory.
14618944721SPawel Jakub Dawidek 		 */
14718944721SPawel Jakub Dawidek 		fat_FirstDataSector =
14818944721SPawel Jakub Dawidek 		    UINT16BYTES(pfat32_bsbpb->BPB_RsvdSecCnt) +
14918944721SPawel Jakub Dawidek 		    (pfat32_bsbpb->BPB_NumFATs *
15018944721SPawel Jakub Dawidek 		     UINT32BYTES(pfat32_bsbpb->BPB_FATSz32));
15118944721SPawel Jakub Dawidek 		fat_BytesPerSector = UINT16BYTES(pfat32_bsbpb->BPB_BytsPerSec);
15218944721SPawel Jakub Dawidek 
15318944721SPawel Jakub Dawidek 		G_LABEL_DEBUG(2,
15418944721SPawel Jakub Dawidek 		    "MSDOSFS: FAT_FirstDataSector=0x%x, FAT_BytesPerSector=%d",
15518944721SPawel Jakub Dawidek 		    fat_FirstDataSector, fat_BytesPerSector);
1569f4073d4SKonstantin Belousov 		if (fat_BytesPerSector == 0 ||
1579f4073d4SKonstantin Belousov 		    fat_BytesPerSector % pp->sectorsize != 0) {
1589f4073d4SKonstantin Belousov 			G_LABEL_DEBUG(1, "MSDOSFS: %s: corrupted BPB",
1599f4073d4SKonstantin Belousov 			    pp->name);
1609f4073d4SKonstantin Belousov 			goto error;
1619f4073d4SKonstantin Belousov 		}
16218944721SPawel Jakub Dawidek 
16318944721SPawel Jakub Dawidek 		for (offset = fat_BytesPerSector * fat_FirstDataSector;;
16418944721SPawel Jakub Dawidek 		    offset += fat_BytesPerSector) {
16518944721SPawel Jakub Dawidek 			sector = (uint8_t *)g_read_data(cp, offset,
16618944721SPawel Jakub Dawidek 			    fat_BytesPerSector, NULL);
16718944721SPawel Jakub Dawidek 			if (sector == NULL)
16818944721SPawel Jakub Dawidek 				goto error;
16918944721SPawel Jakub Dawidek 
17018944721SPawel Jakub Dawidek 			pfat_entry = (FAT_DES *)sector;
17118944721SPawel Jakub Dawidek 			do {
17218944721SPawel Jakub Dawidek 				/* No more entries available. */
17318944721SPawel Jakub Dawidek 				if (pfat_entry->DIR_Name[0] == 0) {
17418944721SPawel Jakub Dawidek 					G_LABEL_DEBUG(1, "MSDOSFS: %s: "
17518944721SPawel Jakub Dawidek 					    "FAT32 volume has no name.",
17618944721SPawel Jakub Dawidek 					    pp->name);
17718944721SPawel Jakub Dawidek 					goto error;
17818944721SPawel Jakub Dawidek 				}
17918944721SPawel Jakub Dawidek 
18018944721SPawel Jakub Dawidek 				/* Skip empty or long name entries. */
18118944721SPawel Jakub Dawidek 				if (pfat_entry->DIR_Name[0] == 0xe5 ||
18218944721SPawel Jakub Dawidek 				    (pfat_entry->DIR_Attr &
18318944721SPawel Jakub Dawidek 				     FAT_DES_ATTR_LONG_NAME) ==
18418944721SPawel Jakub Dawidek 				    FAT_DES_ATTR_LONG_NAME) {
18518944721SPawel Jakub Dawidek 					continue;
18618944721SPawel Jakub Dawidek 				}
18718944721SPawel Jakub Dawidek 
18818944721SPawel Jakub Dawidek 				/*
18918944721SPawel Jakub Dawidek 				 * The name of the entry is the volume label if
19018944721SPawel Jakub Dawidek 				 * ATTR_VOLUME_ID is set.
19118944721SPawel Jakub Dawidek 				 */
19218944721SPawel Jakub Dawidek 				if (pfat_entry->DIR_Attr &
19318944721SPawel Jakub Dawidek 				    FAT_DES_ATTR_VOLUME_ID) {
19434fb1c13SJessica Clarke 					copysize = MIN(size - 1,
19534fb1c13SJessica Clarke 					    sizeof(pfat_entry->DIR_Name));
19634fb1c13SJessica Clarke 					memcpy(label, pfat_entry->DIR_Name,
19734fb1c13SJessica Clarke 					    copysize);
19834fb1c13SJessica Clarke 					label[copysize] = '\0';
19918944721SPawel Jakub Dawidek 					goto endofchecks;
20018944721SPawel Jakub Dawidek 				}
20118944721SPawel Jakub Dawidek 			} while((uint8_t *)(++pfat_entry) <
20218944721SPawel Jakub Dawidek 			    (uint8_t *)(sector + fat_BytesPerSector));
203e1237b28SPawel Jakub Dawidek 			g_free(sector);
20418944721SPawel Jakub Dawidek 		}
20518944721SPawel Jakub Dawidek 	} else {
20618944721SPawel Jakub Dawidek 		G_LABEL_DEBUG(1, "MSDOSFS: %s: no FAT volume detected.",
20718944721SPawel Jakub Dawidek 		    pp->name);
20818944721SPawel Jakub Dawidek 		goto error;
20918944721SPawel Jakub Dawidek 	}
21018944721SPawel Jakub Dawidek 
21118944721SPawel Jakub Dawidek endofchecks:
212628b7128SEdward Tomasz Napierala 	g_label_rtrim(label, size);
21318944721SPawel Jakub Dawidek 
21418944721SPawel Jakub Dawidek error:
21518944721SPawel Jakub Dawidek 	g_free(sector0);
21618944721SPawel Jakub Dawidek 	g_free(sector);
217e1237b28SPawel Jakub Dawidek }
218e1237b28SPawel Jakub Dawidek 
2193ce9ca89SEdward Tomasz Napierala struct g_label_desc g_label_msdosfs = {
220e1237b28SPawel Jakub Dawidek 	.ld_taste = g_label_msdosfs_taste,
221795c5f36SXin LI 	.ld_dirprefix = "msdosfs/",
2223ce9ca89SEdward Tomasz Napierala 	.ld_enabled = 1
223e1237b28SPawel Jakub Dawidek };
2243ce9ca89SEdward Tomasz Napierala 
2253ce9ca89SEdward Tomasz Napierala G_LABEL_INIT(msdosfs, g_label_msdosfs, "Create device nodes for MSDOSFS volumes");
226