xref: /dragonfly/usr.sbin/fstyp/msdosfs.c (revision 655933d6)
1 /*-
2  * Copyright (c) 2016 The DragonFly Project
3  * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4  * Copyright (c) 2006 Tobias Reifenberger
5  * Copyright (c) 2014 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * This software was developed by Edward Tomasz Napierala under sponsorship
9  * from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "fstyp.h"
39 #include "msdosfs.h"
40 
41 #define LABEL_NO_NAME		"NO NAME    "
42 
43 int
44 fstyp_msdosfs(FILE *fp, char *label, size_t size, const char *devpath)
45 {
46 	FAT_BSBPB *pfat_bsbpb;
47 	FAT32_BSBPB *pfat32_bsbpb;
48 	FAT_DES *pfat_entry;
49 	uint8_t *sector0, *sector;
50 	size_t copysize;
51 
52 	sector0 = NULL;
53 	sector = NULL;
54 
55 	/* Load 1st sector with boot sector and boot parameter block. */
56 	sector0 = (uint8_t *)read_buf(fp, 0, 512);
57 	if (sector0 == NULL)
58 		return (1);
59 
60 	/* Check for the FAT boot sector signature. */
61 	if (sector0[510] != 0x55 || sector0[511] != 0xaa) {
62 		goto error;
63 	}
64 
65 	/*
66 	 * Test if this is really a FAT volume and determine the FAT type.
67 	 */
68 
69 	pfat_bsbpb = (FAT_BSBPB *)sector0;
70 	pfat32_bsbpb = (FAT32_BSBPB *)sector0;
71 
72 	if (UINT16BYTES(pfat_bsbpb->BPB_FATSz16) != 0) {
73 		/*
74 		 * If the BPB_FATSz16 field is not zero and the string "FAT" is
75 		 * at the right place, this should be a FAT12 or FAT16 volume.
76 		 */
77 		if (strncmp(pfat_bsbpb->BS_FilSysType, "FAT", 3) != 0) {
78 			goto error;
79 		}
80 
81 		/* A volume with no name should have "NO NAME    " as label. */
82 		if (strncmp(pfat_bsbpb->BS_VolLab, LABEL_NO_NAME,
83 		    sizeof(pfat_bsbpb->BS_VolLab)) == 0) {
84 			goto endofchecks;
85 		}
86 		copysize = MIN(size - 1, sizeof(pfat_bsbpb->BS_VolLab));
87 		memcpy(label, pfat_bsbpb->BS_VolLab, copysize);
88 		label[copysize] = '\0';
89 	} else if (UINT32BYTES(pfat32_bsbpb->BPB_FATSz32) != 0) {
90 		uint32_t fat_FirstDataSector, fat_BytesPerSector, offset;
91 
92 		/*
93 		 * If the BPB_FATSz32 field is not zero and the string "FAT" is
94 		 * at the right place, this should be a FAT32 volume.
95 		 */
96 		if (strncmp(pfat32_bsbpb->BS_FilSysType, "FAT", 3) != 0) {
97 			goto error;
98 		}
99 
100 		/*
101 		 * If the volume label is not "NO NAME    " we're done.
102 		 */
103 		if (strncmp(pfat32_bsbpb->BS_VolLab, LABEL_NO_NAME,
104 		    sizeof(pfat32_bsbpb->BS_VolLab)) != 0) {
105 			copysize = MIN(size - 1,
106 			    sizeof(pfat32_bsbpb->BS_VolLab));
107 			memcpy(label, pfat32_bsbpb->BS_VolLab, copysize);
108 			label[copysize] = '\0';
109 			goto endofchecks;
110 		}
111 
112 		/*
113 		 * If the volume label "NO NAME    " is in the boot sector, the
114 		 * label of FAT32 volumes may be stored as a special entry in
115 		 * the root directory.
116 		 */
117 		fat_FirstDataSector =
118 		    UINT16BYTES(pfat32_bsbpb->BPB_RsvdSecCnt) +
119 		    (pfat32_bsbpb->BPB_NumFATs *
120 		     UINT32BYTES(pfat32_bsbpb->BPB_FATSz32));
121 		fat_BytesPerSector = UINT16BYTES(pfat32_bsbpb->BPB_BytsPerSec);
122 
123 		//    fat_FirstDataSector, fat_BytesPerSector);
124 
125 		for (offset = fat_BytesPerSector * fat_FirstDataSector;;
126 		    offset += fat_BytesPerSector) {
127 			sector = (uint8_t *)read_buf(fp, offset, fat_BytesPerSector);
128 			if (sector == NULL)
129 				goto error;
130 
131 			pfat_entry = (FAT_DES *)sector;
132 			do {
133 				/* No more entries available. */
134 				if (pfat_entry->DIR_Name[0] == 0) {
135 					goto endofchecks;
136 				}
137 
138 				/* Skip empty or long name entries. */
139 				if (pfat_entry->DIR_Name[0] == 0xe5 ||
140 				    (pfat_entry->DIR_Attr &
141 				     FAT_DES_ATTR_LONG_NAME) ==
142 				    FAT_DES_ATTR_LONG_NAME) {
143 					continue;
144 				}
145 
146 				/*
147 				 * The name of the entry is the volume label if
148 				 * ATTR_VOLUME_ID is set.
149 				 */
150 				if (pfat_entry->DIR_Attr &
151 				    FAT_DES_ATTR_VOLUME_ID) {
152 					copysize = MIN(size - 1,
153 					    sizeof(pfat_entry->DIR_Name));
154 					memcpy(label, pfat_entry->DIR_Name,
155 					    copysize);
156 					label[copysize] = '\0';
157 					goto endofchecks;
158 				}
159 			} while((uint8_t *)(++pfat_entry) <
160 			    (uint8_t *)(sector + fat_BytesPerSector));
161 			free(sector);
162 		}
163 	} else {
164 		goto error;
165 	}
166 
167 endofchecks:
168 	rtrim(label, size);
169 
170 	free(sector0);
171 	free(sector);
172 
173 	return (0);
174 
175 error:
176 	free(sector0);
177 	free(sector);
178 
179 	return (1);
180 }
181