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