xref: /openbsd-src/usr.sbin/makefs/msdos/msdosfs_vfsops.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1 /*	$OpenBSD: msdosfs_vfsops.c,v 1.11 2016/12/17 16:43:30 krw Exp $	*/
2 
3 /*-
4  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6  * All rights reserved.
7  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Written by Paul Popelka (paulp@uts.amdahl.com)
36  *
37  * You can do anything you want with this software, just don't say you wrote
38  * it, and don't remove this notice.
39  *
40  * This software is provided "as is".
41  *
42  * The author supplies this software to be publicly redistributed on the
43  * understanding that the author is not responsible for the correct
44  * functioning of this software in any circumstances and is not liable for
45  * any damages caused by this software.
46  *
47  * October 1992
48  */
49 
50 #include <sys/param.h>
51 #include <sys/time.h>
52 
53 #include "ffs/buf.h"
54 
55 #include <msdosfs/bpb.h>
56 #include <msdosfs/bootsect.h>
57 #include "msdos/direntry.h"
58 #include "msdos/denode.h"
59 #include "msdos/msdosfsmount.h"
60 #include "msdos/fat.h"
61 
62 #include <stdio.h>
63 #include <errno.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <util.h>
67 
68 #include "makefs.h"
69 #include "msdos.h"
70 #include "msdos/mkfs_msdos.h"
71 
72 #ifdef MSDOSFS_DEBUG
73 #define DPRINTF(a) printf a
74 #else
75 #define DPRINTF(a)
76 #endif
77 
78 struct msdosfsmount *
79 msdosfs_mount(struct mkfsvnode *devvp, int flags)
80 {
81 	struct msdosfsmount *pmp = NULL;
82 	struct mkfsbuf *bp;
83 	union bootsector *bsp;
84 	struct byte_bpb33 *b33;
85 	struct byte_bpb50 *b50;
86 	struct byte_bpb710 *b710;
87 	uint8_t SecPerClust;
88 	int	ronly = 0, error;
89 	int	bsize;
90 	struct timezone tz;
91 	unsigned secsize = 512;
92 
93 	DPRINTF(("%s(bread 0)\n", __func__));
94 	if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0)
95 		goto error_exit;
96 
97 	bsp = (union bootsector *)bp->b_data;
98 	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
99 	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
100 	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
101 
102 	if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
103 	    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
104 		DPRINTF(("bootsig0 %d bootsig1 %d\n",
105 		    bsp->bs50.bsBootSectSig0,
106 		    bsp->bs50.bsBootSectSig1));
107 		error = EINVAL;
108 		goto error_exit;
109 	}
110 	bsize = 0;
111 
112 	pmp = ecalloc(1, sizeof *pmp);
113 	/*
114 	 * Compute several useful quantities from the bpb in the
115 	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
116 	 * the fields that are different between dos 5 and dos 3.3.
117 	 */
118 	SecPerClust = b50->bpbSecPerClust;
119 	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
120 	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
121 	pmp->pm_FATs = b50->bpbFATs;
122 	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
123 	pmp->pm_Sectors = getushort(b50->bpbSectors);
124 	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
125 	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
126 	pmp->pm_Heads = getushort(b50->bpbHeads);
127 	pmp->pm_Media = b50->bpbMedia;
128 
129 	if (gettimeofday(NULL, &tz) == -1) {
130 		error = errno;
131 		goto error_exit;
132 	}
133 	pmp->pm_minuteswest = tz.tz_minuteswest;
134 
135 	DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, RootDirEnts=%u, "
136 	    "Sectors=%u, FATsecs=%lu, SecPerTrack=%u, Heads=%u, Media=%u)\n",
137 	    __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors, pmp->pm_FATs,
138 	    pmp->pm_RootDirEnts, pmp->pm_Sectors, pmp->pm_FATsecs,
139 	    pmp->pm_SecPerTrack, pmp->pm_Heads, pmp->pm_Media));
140 	/* XXX - We should probably check more values here */
141 	if (!pmp->pm_BytesPerSec || !SecPerClust
142 		|| pmp->pm_SecPerTrack > 63) {
143 		DPRINTF(("bytespersec %d secperclust %d "
144 		    "secpertrack %d\n",
145 		    pmp->pm_BytesPerSec, SecPerClust,
146 		    pmp->pm_SecPerTrack));
147 		error = EINVAL;
148 		goto error_exit;
149 	}
150 
151 	pmp->pm_flags = flags & MSDOSFSMNT_MNTOPT;
152 	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
153 		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
154 
155 	if (pmp->pm_Sectors == 0) {
156 		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
157 		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
158 	} else {
159 		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
160 		pmp->pm_HugeSectors = pmp->pm_Sectors;
161 	}
162 
163 	if (pmp->pm_RootDirEnts == 0) {
164 		unsigned short vers = getushort(b710->bpbFSVers);
165 		/*
166 		 * Some say that bsBootSectSig[23] must be zero, but
167 		 * Windows does not require this and some digital cameras
168 		 * do not set these to zero.  Therefore, do not insist.
169 		 */
170 		if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
171 			DPRINTF(("sectors %d fatsecs %lu vers %d\n",
172 			    pmp->pm_Sectors, pmp->pm_FATsecs, vers));
173 			error = EINVAL;
174 			goto error_exit;
175 		}
176 		pmp->pm_fatmask = FAT32_MASK;
177 		pmp->pm_fatmult = 4;
178 		pmp->pm_fatdiv = 1;
179 		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
180 
181 		/* mirrorring is enabled if the FATMIRROR bit is not set */
182 		if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
183 			pmp->pm_flags |= MSDOSFS_FATMIRROR;
184 		else
185 			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
186 	} else
187 		pmp->pm_flags |= MSDOSFS_FATMIRROR;
188 
189 	/* Check that fs has nonzero FAT size */
190 	if (pmp->pm_FATsecs == 0) {
191 		DPRINTF(("FATsecs is 0\n"));
192 		error = EINVAL;
193 		goto error_exit;
194 	}
195 
196 	pmp->pm_fatblk = pmp->pm_ResSectors;
197 	if (FAT32(pmp)) {
198 		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
199 		pmp->pm_firstcluster = pmp->pm_fatblk
200 			+ (pmp->pm_FATs * pmp->pm_FATsecs);
201 		pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
202 	} else {
203 		pmp->pm_rootdirblk = pmp->pm_fatblk +
204 			(pmp->pm_FATs * pmp->pm_FATsecs);
205 		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
206 				       + pmp->pm_BytesPerSec - 1)
207 			/ pmp->pm_BytesPerSec;/* in sectors */
208 		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
209 	}
210 
211 	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
212 	    SecPerClust;
213 	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
214 	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
215 
216 	if (pmp->pm_fatmask == 0) {
217 		if (pmp->pm_maxcluster
218 		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
219 			/*
220 			 * This will usually be a floppy disk. This size makes
221 			 * sure that one FAT entry will not be split across
222 			 * multiple blocks.
223 			 */
224 			pmp->pm_fatmask = FAT12_MASK;
225 			pmp->pm_fatmult = 3;
226 			pmp->pm_fatdiv = 2;
227 		} else {
228 			pmp->pm_fatmask = FAT16_MASK;
229 			pmp->pm_fatmult = 2;
230 			pmp->pm_fatdiv = 1;
231 		}
232 	}
233 	if (FAT12(pmp))
234 		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
235 	else
236 		pmp->pm_fatblocksize = MAXBSIZE;
237 
238 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
239 	pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
240 
241 	/*
242 	 * Compute mask and shift value for isolating cluster relative byte
243 	 * offsets and cluster numbers from a file offset.
244 	 */
245 	pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
246 	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
247 	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
248 
249 	DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, "
250 	    "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, "
251 	    "cnshift=%lu)\n",
252 	    __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv,
253 	    pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift,
254 	    pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift));
255 	/*
256 	 * Check for valid cluster size
257 	 * must be a power of 2
258 	 */
259 	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
260 		DPRINTF(("bpcluster %lu cnshift %lu\n",
261 		    pmp->pm_bpcluster, pmp->pm_cnshift));
262 		error = EINVAL;
263 		goto error_exit;
264 	}
265 
266 	/*
267 	 * Release the bootsector buffer.
268 	 */
269 	brelse(bp, BC_AGE);
270 	bp = NULL;
271 
272 	/*
273 	 * Check FSInfo.
274 	 */
275 	if (pmp->pm_fsinfo) {
276 		struct fsinfo *fp;
277 
278 		/*
279 		 * XXX	If the fsinfo block is stored on media with
280 		 *	2KB or larger sectors, is the fsinfo structure
281 		 *	padded at the end or in the middle?
282 		 */
283 		DPRINTF(("%s(bread %lu)\n", __func__,
284 		    (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo)));
285 		if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
286 		    pmp->pm_BytesPerSec, 0, &bp)) != 0)
287 			goto error_exit;
288 		fp = (struct fsinfo *)bp->b_data;
289 		if (!memcmp(fp->fsisig1, "RRaA", 4)
290 		    && !memcmp(fp->fsisig2, "rrAa", 4)
291 		    && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
292 		    && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
293 			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
294 		else
295 			pmp->pm_fsinfo = 0;
296 		brelse(bp, 0);
297 		bp = NULL;
298 	}
299 
300 	/*
301 	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
302 	 * XXX
303 	 */
304 	if (pmp->pm_fsinfo) {
305 		if ((pmp->pm_nxtfree == 0xffffffffUL) ||
306 		    (pmp->pm_nxtfree > pmp->pm_maxcluster))
307 			pmp->pm_fsinfo = 0;
308 	}
309 
310 	/*
311 	 * Allocate memory for the bitmap of allocated clusters, and then
312 	 * fill it in.
313 	 */
314 	pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap),
315 	    ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS));
316 	/*
317 	 * fillinusemap() needs pm_devvp.
318 	 */
319 	pmp->pm_dev = 0;
320 	pmp->pm_devvp = devvp;
321 
322 	/*
323 	 * Have the inuse map filled in.
324 	 */
325 	if ((error = fillinusemap(pmp)) != 0) {
326 		DPRINTF(("fillinusemap %d\n", error));
327 		goto error_exit;
328 	}
329 
330 	/*
331 	 * Finish up.
332 	 */
333 	if (ronly)
334 		pmp->pm_flags |= MSDOSFSMNT_RONLY;
335 	else
336 		pmp->pm_fmod = 1;
337 
338 	return pmp;
339 
340 error_exit:
341 	if (bp)
342 		brelse(bp, BC_AGE);
343 	if (pmp) {
344 		if (pmp->pm_inusemap)
345 			free(pmp->pm_inusemap);
346 		free(pmp);
347 	}
348 	errno = error;
349 	return NULL;
350 }
351 
352 int
353 msdosfs_root(struct msdosfsmount *pmp, struct mkfsvnode *vp) {
354 	struct denode *ndep;
355 	int error;
356 
357 	*vp = *pmp->pm_devvp;
358 	if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) {
359 		errno = error;
360 		return -1;
361 	}
362 	vp->v_data = ndep;
363 	return 0;
364 }
365