1131476Spjd/*- 2131476Spjd * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3161246Spjd * Copyright (c) 2006 Tobias Reifenberger 4131476Spjd * All rights reserved. 5131476Spjd * 6131476Spjd * Redistribution and use in source and binary forms, with or without 7131476Spjd * modification, are permitted provided that the following conditions 8131476Spjd * are met: 9131476Spjd * 1. Redistributions of source code must retain the above copyright 10131476Spjd * notice, this list of conditions and the following disclaimer. 11131476Spjd * 2. Redistributions in binary form must reproduce the above copyright 12131476Spjd * notice, this list of conditions and the following disclaimer in the 13131476Spjd * documentation and/or other materials provided with the distribution. 14155174Spjd * 15131476Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16131476Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17131476Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18131476Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19131476Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20131476Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21131476Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22131476Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23131476Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24131476Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25131476Spjd * SUCH DAMAGE. 26131476Spjd */ 27131476Spjd 28131476Spjd#include <sys/cdefs.h> 29131476Spjd__FBSDID("$FreeBSD$"); 30131476Spjd 31131476Spjd#include <sys/param.h> 32131476Spjd#include <sys/systm.h> 33131476Spjd#include <sys/kernel.h> 34131476Spjd#include <sys/malloc.h> 35131476Spjd 36131476Spjd#include <geom/geom.h> 37131476Spjd#include <geom/label/g_label.h> 38161246Spjd#include <geom/label/g_label_msdosfs.h> 39131476Spjd 40131476Spjd#define G_LABEL_MSDOSFS_DIR "msdosfs" 41161246Spjd#define LABEL_NO_NAME "NO NAME " 42131476Spjd 43131476Spjdstatic void 44131476Spjdg_label_msdosfs_taste(struct g_consumer *cp, char *label, size_t size) 45131476Spjd{ 46131476Spjd struct g_provider *pp; 47161246Spjd FAT_BSBPB *pfat_bsbpb; 48161246Spjd FAT32_BSBPB *pfat32_bsbpb; 49161246Spjd FAT_DES *pfat_entry; 50161246Spjd uint8_t *sector0, *sector; 51131476Spjd 52131476Spjd g_topology_assert_not(); 53131476Spjd pp = cp->provider; 54161246Spjd sector0 = NULL; 55161246Spjd sector = NULL; 56161246Spjd bzero(label, size); 57131476Spjd 58161246Spjd /* Check if the sector size of the medium is a valid FAT sector size. */ 59161246Spjd switch(pp->sectorsize) { 60161246Spjd case 512: 61161246Spjd case 1024: 62161246Spjd case 2048: 63161246Spjd case 4096: 64161246Spjd break; 65161246Spjd default: 66161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: sector size %d not compatible.", 67161246Spjd pp->name, pp->sectorsize); 68131476Spjd return; 69161246Spjd } 70161246Spjd 71161246Spjd /* Load 1st sector with boot sector and boot parameter block. */ 72161246Spjd sector0 = (uint8_t *)g_read_data(cp, 0, pp->sectorsize, NULL); 73161246Spjd if (sector0 == NULL) 74161246Spjd return; 75161246Spjd 76161246Spjd /* Check for the FAT boot sector signature. */ 77161246Spjd if (sector0[510] != 0x55 || sector0[511] != 0xaa) { 78161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: no FAT signature found.", 79131476Spjd pp->name); 80161246Spjd goto error; 81161246Spjd } 82161246Spjd 83161246Spjd 84161246Spjd /* 85161246Spjd * Test if this is really a FAT volume and determine the FAT type. 86161246Spjd */ 87161246Spjd 88161246Spjd pfat_bsbpb = (FAT_BSBPB *)sector0; 89161246Spjd pfat32_bsbpb = (FAT32_BSBPB *)sector0; 90161246Spjd 91161246Spjd if (UINT16BYTES(pfat_bsbpb->BPB_FATSz16) != 0) { 92161246Spjd /* 93161246Spjd * If the BPB_FATSz16 field is not zero and the string "FAT" is 94161246Spjd * at the right place, this should be a FAT12 or FAT16 volume. 95161246Spjd */ 96161246Spjd if (strncmp(pfat_bsbpb->BS_FilSysType, "FAT", 3) != 0) { 97161246Spjd G_LABEL_DEBUG(1, 98161246Spjd "MSDOSFS: %s: FAT12/16 volume not valid.", 99161246Spjd pp->name); 100161246Spjd goto error; 101161246Spjd } 102161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: FAT12/FAT16 volume detected.", 103131476Spjd pp->name); 104161246Spjd 105161246Spjd /* A volume with no name should have "NO NAME " as label. */ 106161246Spjd if (strncmp(pfat_bsbpb->BS_VolLab, LABEL_NO_NAME, 107161246Spjd sizeof(pfat_bsbpb->BS_VolLab)) == 0) { 108161246Spjd G_LABEL_DEBUG(1, 109161246Spjd "MSDOSFS: %s: FAT12/16 volume has no name.", 110161246Spjd pp->name); 111161246Spjd goto error; 112161246Spjd } 113161246Spjd strlcpy(label, pfat_bsbpb->BS_VolLab, 114161246Spjd MIN(size, sizeof(pfat_bsbpb->BS_VolLab) + 1)); 115161246Spjd } else if (UINT32BYTES(pfat32_bsbpb->BPB_FATSz32) != 0) { 116161246Spjd uint32_t fat_FirstDataSector, fat_BytesPerSector, offset; 117161246Spjd 118161246Spjd /* 119161246Spjd * If the BPB_FATSz32 field is not zero and the string "FAT" is 120161246Spjd * at the right place, this should be a FAT32 volume. 121161246Spjd */ 122161246Spjd if (strncmp(pfat32_bsbpb->BS_FilSysType, "FAT", 3) != 0) { 123161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: FAT32 volume not valid.", 124161246Spjd pp->name); 125161246Spjd goto error; 126161246Spjd } 127161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: FAT32 volume detected.", 128131476Spjd pp->name); 129161246Spjd 130161246Spjd /* 131161246Spjd * If the volume label is not "NO NAME " we're done. 132161246Spjd */ 133161246Spjd if (strncmp(pfat32_bsbpb->BS_VolLab, LABEL_NO_NAME, 134161246Spjd sizeof(pfat32_bsbpb->BS_VolLab)) != 0) { 135161246Spjd strlcpy(label, pfat32_bsbpb->BS_VolLab, 136161246Spjd MIN(size, sizeof(pfat32_bsbpb->BS_VolLab) + 1)); 137161246Spjd goto endofchecks; 138161246Spjd } 139161246Spjd 140161246Spjd /* 141161246Spjd * If the volume label "NO NAME " is in the boot sector, the 142161246Spjd * label of FAT32 volumes may be stored as a special entry in 143161246Spjd * the root directory. 144161246Spjd */ 145161246Spjd fat_FirstDataSector = 146161246Spjd UINT16BYTES(pfat32_bsbpb->BPB_RsvdSecCnt) + 147161246Spjd (pfat32_bsbpb->BPB_NumFATs * 148161246Spjd UINT32BYTES(pfat32_bsbpb->BPB_FATSz32)); 149161246Spjd fat_BytesPerSector = UINT16BYTES(pfat32_bsbpb->BPB_BytsPerSec); 150162834Spjd 151161246Spjd G_LABEL_DEBUG(2, 152161246Spjd "MSDOSFS: FAT_FirstDataSector=0x%x, FAT_BytesPerSector=%d", 153161246Spjd fat_FirstDataSector, fat_BytesPerSector); 154161246Spjd 155161246Spjd for (offset = fat_BytesPerSector * fat_FirstDataSector;; 156161246Spjd offset += fat_BytesPerSector) { 157161246Spjd sector = (uint8_t *)g_read_data(cp, offset, 158161246Spjd fat_BytesPerSector, NULL); 159161246Spjd if (sector == NULL) 160161246Spjd goto error; 161161246Spjd 162161246Spjd pfat_entry = (FAT_DES *)sector; 163161246Spjd do { 164161246Spjd /* No more entries available. */ 165161246Spjd if (pfat_entry->DIR_Name[0] == 0) { 166161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: " 167161246Spjd "FAT32 volume has no name.", 168161246Spjd pp->name); 169161246Spjd goto error; 170161246Spjd } 171161246Spjd 172161246Spjd /* Skip empty or long name entries. */ 173161246Spjd if (pfat_entry->DIR_Name[0] == 0xe5 || 174161246Spjd (pfat_entry->DIR_Attr & 175161246Spjd FAT_DES_ATTR_LONG_NAME) == 176161246Spjd FAT_DES_ATTR_LONG_NAME) { 177161246Spjd continue; 178161246Spjd } 179161246Spjd 180161246Spjd /* 181161246Spjd * The name of the entry is the volume label if 182161246Spjd * ATTR_VOLUME_ID is set. 183161246Spjd */ 184161246Spjd if (pfat_entry->DIR_Attr & 185161246Spjd FAT_DES_ATTR_VOLUME_ID) { 186161246Spjd strlcpy(label, pfat_entry->DIR_Name, 187161246Spjd MIN(size, 188188492Slulf sizeof(pfat_entry->DIR_Name) + 1)); 189161246Spjd goto endofchecks; 190161246Spjd } 191161246Spjd } while((uint8_t *)(++pfat_entry) < 192161246Spjd (uint8_t *)(sector + fat_BytesPerSector)); 193161246Spjd g_free(sector); 194161246Spjd } 195131476Spjd } else { 196161246Spjd G_LABEL_DEBUG(1, "MSDOSFS: %s: no FAT volume detected.", 197161246Spjd pp->name); 198161246Spjd goto error; 199131476Spjd } 200161246Spjd 201161246Spjdendofchecks: 202286193Strasz g_label_rtrim(label, size); 203161246Spjd 204161246Spjderror: 205161246Spjd if (sector0 != NULL) 206161246Spjd g_free(sector0); 207161246Spjd if (sector != NULL) 208161246Spjd g_free(sector); 209131476Spjd} 210131476Spjd 211199875Straszstruct g_label_desc g_label_msdosfs = { 212131476Spjd .ld_taste = g_label_msdosfs_taste, 213199875Strasz .ld_dir = G_LABEL_MSDOSFS_DIR, 214199875Strasz .ld_enabled = 1 215131476Spjd}; 216199875Strasz 217199875StraszG_LABEL_INIT(msdosfs, g_label_msdosfs, "Create device nodes for MSDOSFS volumes"); 218