xref: /netbsd-src/usr.sbin/makefs/msdos/msdosfs_vfsops.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
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 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.10 2016/01/30 09:59:27 mlelstv Exp $");
54 
55 #include <sys/param.h>
56 
57 #include <ffs/buf.h>
58 
59 #include <fs/msdosfs/bpb.h>
60 #include <fs/msdosfs/bootsect.h>
61 #include <fs/msdosfs/direntry.h>
62 #include <fs/msdosfs/denode.h>
63 #include <fs/msdosfs/msdosfsmount.h>
64 #include <fs/msdosfs/fat.h>
65 
66 #include <stdio.h>
67 #include <errno.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <util.h>
71 
72 #include "makefs.h"
73 #include "msdos.h"
74 #include "mkfs_msdos.h"
75 
76 #ifdef MSDOSFS_DEBUG
77 #define DPRINTF(a) printf a
78 #else
79 #define DPRINTF(a)
80 #endif
81 
82 struct msdosfsmount *
83 msdosfs_mount(struct vnode *devvp, int flags)
84 {
85 	struct msdosfsmount *pmp = NULL;
86 	struct buf *bp;
87 	union bootsector *bsp;
88 	struct byte_bpb33 *b33;
89 	struct byte_bpb50 *b50;
90 	struct byte_bpb710 *b710;
91 	uint8_t SecPerClust;
92 	int	ronly = 0, error, tmp;
93 	int	bsize;
94 	struct msdos_options *m = devvp->fs->fs_specific;
95 	uint64_t psize = m->create_size;
96 	unsigned secsize = 512;
97 
98 	DPRINTF(("%s(bread 0)\n", __func__));
99 	if ((error = bread(devvp, 0, secsize, 0, &bp)) != 0)
100 		goto error_exit;
101 
102 	bsp = (union bootsector *)bp->b_data;
103 	b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
104 	b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
105 	b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
106 
107 	if (!(flags & MSDOSFSMNT_GEMDOSFS)) {
108 		if (bsp->bs50.bsBootSectSig0 != BOOTSIG0
109 		    || bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
110 			DPRINTF(("bootsig0 %d bootsig1 %d\n",
111 			    bsp->bs50.bsBootSectSig0,
112 			    bsp->bs50.bsBootSectSig1));
113 			error = EINVAL;
114 			goto error_exit;
115 		}
116 		bsize = 0;
117 	} else
118 		bsize = 512;
119 
120 	pmp = ecalloc(1, sizeof *pmp);
121 	/*
122 	 * Compute several useful quantities from the bpb in the
123 	 * bootsector.  Copy in the dos 5 variant of the bpb then fix up
124 	 * the fields that are different between dos 5 and dos 3.3.
125 	 */
126 	SecPerClust = b50->bpbSecPerClust;
127 	pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
128 	pmp->pm_ResSectors = getushort(b50->bpbResSectors);
129 	pmp->pm_FATs = b50->bpbFATs;
130 	pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
131 	pmp->pm_Sectors = getushort(b50->bpbSectors);
132 	pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
133 	pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
134 	pmp->pm_Heads = getushort(b50->bpbHeads);
135 	pmp->pm_Media = b50->bpbMedia;
136 
137 	DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, RootDirEnts=%u, "
138 	    "Sectors=%u, FATsecs=%lu, SecPerTrack=%u, Heads=%u, Media=%u)\n",
139 	    __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors, pmp->pm_FATs,
140 	    pmp->pm_RootDirEnts, pmp->pm_Sectors, pmp->pm_FATsecs,
141 	    pmp->pm_SecPerTrack, pmp->pm_Heads, pmp->pm_Media));
142 	if (!(flags & MSDOSFSMNT_GEMDOSFS)) {
143 		/* XXX - We should probably check more values here */
144     		if (!pmp->pm_BytesPerSec || !SecPerClust
145 	    		|| pmp->pm_SecPerTrack > 63) {
146 			DPRINTF(("bytespersec %d secperclust %d "
147 			    "secpertrack %d\n",
148 			    pmp->pm_BytesPerSec, SecPerClust,
149 			    pmp->pm_SecPerTrack));
150 			error = EINVAL;
151 			goto error_exit;
152 		}
153 	}
154 
155 	pmp->pm_flags = flags & MSDOSFSMNT_MNTOPT;
156 	if (pmp->pm_flags & MSDOSFSMNT_GEMDOSFS)
157 		pmp->pm_flags |= MSDOSFSMNT_NOWIN95;
158 	if (pmp->pm_flags & MSDOSFSMNT_NOWIN95)
159 		pmp->pm_flags |= MSDOSFSMNT_SHORTNAME;
160 
161 	if (pmp->pm_Sectors == 0) {
162 		pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
163 		pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
164 	} else {
165 		pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
166 		pmp->pm_HugeSectors = pmp->pm_Sectors;
167 	}
168 
169 	if (pmp->pm_RootDirEnts == 0) {
170 		unsigned short vers = getushort(b710->bpbFSVers);
171 		/*
172 		 * Some say that bsBootSectSig[23] must be zero, but
173 		 * Windows does not require this and some digital cameras
174 		 * do not set these to zero.  Therefore, do not insist.
175 		 */
176 		if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
177 			DPRINTF(("sectors %d fatsecs %lu vers %d\n",
178 			    pmp->pm_Sectors, pmp->pm_FATsecs, vers));
179 			error = EINVAL;
180 			goto error_exit;
181 		}
182 		pmp->pm_fatmask = FAT32_MASK;
183 		pmp->pm_fatmult = 4;
184 		pmp->pm_fatdiv = 1;
185 		pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
186 
187 		/* mirrorring is enabled if the FATMIRROR bit is not set */
188 		if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
189 			pmp->pm_flags |= MSDOSFS_FATMIRROR;
190 		else
191 			pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
192 	} else
193 		pmp->pm_flags |= MSDOSFS_FATMIRROR;
194 
195 	if (flags & MSDOSFSMNT_GEMDOSFS) {
196 		if (FAT32(pmp)) {
197 			DPRINTF(("FAT32 for GEMDOS\n"));
198 			/*
199 			 * GEMDOS doesn't know FAT32.
200 			 */
201 			error = EINVAL;
202 			goto error_exit;
203 		}
204 
205 		/*
206 		 * Check a few values (could do some more):
207 		 * - logical sector size: power of 2, >= block size
208 		 * - sectors per cluster: power of 2, >= 1
209 		 * - number of sectors:   >= 1, <= size of partition
210 		 */
211 		if ( (SecPerClust == 0)
212 		  || (SecPerClust & (SecPerClust - 1))
213 		  || (pmp->pm_BytesPerSec < bsize)
214 		  || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
215 		  || (pmp->pm_HugeSectors == 0)
216 		  || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
217 		      > psize)) {
218 			DPRINTF(("consistency checks for GEMDOS\n"));
219 			error = EINVAL;
220 			goto error_exit;
221 		}
222 		/*
223 		 * XXX - Many parts of the msdosfs driver seem to assume that
224 		 * the number of bytes per logical sector (BytesPerSec) will
225 		 * always be the same as the number of bytes per disk block
226 		 * Let's pretend it is.
227 		 */
228 		tmp = pmp->pm_BytesPerSec / bsize;
229 		pmp->pm_BytesPerSec  = bsize;
230 		pmp->pm_HugeSectors *= tmp;
231 		pmp->pm_HiddenSects *= tmp;
232 		pmp->pm_ResSectors  *= tmp;
233 		pmp->pm_Sectors     *= tmp;
234 		pmp->pm_FATsecs     *= tmp;
235 		SecPerClust         *= tmp;
236 	}
237 
238 	/* Check that fs has nonzero FAT size */
239 	if (pmp->pm_FATsecs == 0) {
240 		DPRINTF(("FATsecs is 0\n"));
241 		error = EINVAL;
242 		goto error_exit;
243 	}
244 
245 	pmp->pm_fatblk = pmp->pm_ResSectors;
246 	if (FAT32(pmp)) {
247 		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
248 		pmp->pm_firstcluster = pmp->pm_fatblk
249 			+ (pmp->pm_FATs * pmp->pm_FATsecs);
250 		pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
251 	} else {
252 		pmp->pm_rootdirblk = pmp->pm_fatblk +
253 			(pmp->pm_FATs * pmp->pm_FATsecs);
254 		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
255 				       + pmp->pm_BytesPerSec - 1)
256 			/ pmp->pm_BytesPerSec;/* in sectors */
257 		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
258 	}
259 
260 	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
261 	    SecPerClust;
262 	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
263 	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
264 
265 	if (flags & MSDOSFSMNT_GEMDOSFS) {
266 		if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
267 			pmp->pm_fatmask = FAT12_MASK;
268 			pmp->pm_fatmult = 3;
269 			pmp->pm_fatdiv = 2;
270 		} else {
271 			pmp->pm_fatmask = FAT16_MASK;
272 			pmp->pm_fatmult = 2;
273 			pmp->pm_fatdiv = 1;
274 		}
275 	} else if (pmp->pm_fatmask == 0) {
276 		if (pmp->pm_maxcluster
277 		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
278 			/*
279 			 * This will usually be a floppy disk. This size makes
280 			 * sure that one FAT entry will not be split across
281 			 * multiple blocks.
282 			 */
283 			pmp->pm_fatmask = FAT12_MASK;
284 			pmp->pm_fatmult = 3;
285 			pmp->pm_fatdiv = 2;
286 		} else {
287 			pmp->pm_fatmask = FAT16_MASK;
288 			pmp->pm_fatmult = 2;
289 			pmp->pm_fatdiv = 1;
290 		}
291 	}
292 	if (FAT12(pmp))
293 		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
294 	else
295 		pmp->pm_fatblocksize = MAXBSIZE;
296 
297 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
298 	pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
299 
300 	/*
301 	 * Compute mask and shift value for isolating cluster relative byte
302 	 * offsets and cluster numbers from a file offset.
303 	 */
304 	pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
305 	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
306 	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
307 
308 	DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, "
309 	    "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, "
310 	    "cnshift=%lu)\n",
311 	    __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv,
312 	    pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift,
313 	    pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift));
314 	/*
315 	 * Check for valid cluster size
316 	 * must be a power of 2
317 	 */
318 	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
319 		DPRINTF(("bpcluster %lu cnshift %lu\n",
320 		    pmp->pm_bpcluster, pmp->pm_cnshift));
321 		error = EINVAL;
322 		goto error_exit;
323 	}
324 
325 	/*
326 	 * Release the bootsector buffer.
327 	 */
328 	brelse(bp, BC_AGE);
329 	bp = NULL;
330 
331 	/*
332 	 * Check FSInfo.
333 	 */
334 	if (pmp->pm_fsinfo) {
335 		struct fsinfo *fp;
336 
337 		/*
338 		 * XXX	If the fsinfo block is stored on media with
339 		 *	2KB or larger sectors, is the fsinfo structure
340 		 *	padded at the end or in the middle?
341 		 */
342 		DPRINTF(("%s(bread %lu)\n", __func__,
343 		    (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo)));
344 		if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
345 		    pmp->pm_BytesPerSec, 0, &bp)) != 0)
346 			goto error_exit;
347 		fp = (struct fsinfo *)bp->b_data;
348 		if (!memcmp(fp->fsisig1, "RRaA", 4)
349 		    && !memcmp(fp->fsisig2, "rrAa", 4)
350 		    && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
351 		    && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
352 			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
353 		else
354 			pmp->pm_fsinfo = 0;
355 		brelse(bp, 0);
356 		bp = NULL;
357 	}
358 
359 	/*
360 	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
361 	 * XXX
362 	 */
363 	if (pmp->pm_fsinfo) {
364 		if ((pmp->pm_nxtfree == 0xffffffffUL) ||
365 		    (pmp->pm_nxtfree > pmp->pm_maxcluster))
366 			pmp->pm_fsinfo = 0;
367 	}
368 
369 	/*
370 	 * Allocate memory for the bitmap of allocated clusters, and then
371 	 * fill it in.
372 	 */
373 	pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap),
374 	    ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS));
375 	/*
376 	 * fillinusemap() needs pm_devvp.
377 	 */
378 	pmp->pm_dev = 0;
379 	pmp->pm_devvp = devvp;
380 
381 	/*
382 	 * Have the inuse map filled in.
383 	 */
384 	if ((error = fillinusemap(pmp)) != 0) {
385 		DPRINTF(("fillinusemap %d\n", error));
386 		goto error_exit;
387 	}
388 
389 	/*
390 	 * Finish up.
391 	 */
392 	if (ronly)
393 		pmp->pm_flags |= MSDOSFSMNT_RONLY;
394 	else
395 		pmp->pm_fmod = 1;
396 
397 	/*
398 	 * If we ever do quotas for DOS filesystems this would be a place
399 	 * to fill in the info in the msdosfsmount structure. You dolt,
400 	 * quotas on dos filesystems make no sense because files have no
401 	 * owners on dos filesystems. of course there is some empty space
402 	 * in the directory entry where we could put uid's and gid's.
403 	 */
404 
405 	return pmp;
406 
407 error_exit:
408 	if (bp)
409 		brelse(bp, BC_AGE);
410 	if (pmp) {
411 		if (pmp->pm_inusemap)
412 			free(pmp->pm_inusemap);
413 		free(pmp);
414 	}
415 	errno = error;
416 	return NULL;
417 }
418 
419 int
420 msdosfs_root(struct msdosfsmount *pmp, struct vnode *vp) {
421 	struct denode *ndep;
422 	int error;
423 
424 	*vp = *pmp->pm_devvp;
425 	if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) {
426 		errno = error;
427 		return -1;
428 	}
429 	vp->v_data = ndep;
430 	return 0;
431 }
432