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