xref: /minix3/usr.sbin/makefs/msdos/msdosfs_vfsops.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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.9 2015/03/29 05:52:59 agc 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 *
msdosfs_mount(struct vnode * devvp,int flags)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 	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 	if (flags & MSDOSFSMNT_GEMDOSFS) {
190 		if (FAT32(pmp)) {
191 			DPRINTF(("FAT32 for GEMDOS\n"));
192 			/*
193 			 * GEMDOS doesn't know FAT32.
194 			 */
195 			error = EINVAL;
196 			goto error_exit;
197 		}
198 
199 		/*
200 		 * Check a few values (could do some more):
201 		 * - logical sector size: power of 2, >= block size
202 		 * - sectors per cluster: power of 2, >= 1
203 		 * - number of sectors:   >= 1, <= size of partition
204 		 */
205 		if ( (SecPerClust == 0)
206 		  || (SecPerClust & (SecPerClust - 1))
207 		  || (pmp->pm_BytesPerSec < bsize)
208 		  || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1))
209 		  || (pmp->pm_HugeSectors == 0)
210 		  || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize)
211 		      > psize)) {
212 			DPRINTF(("consistency checks for GEMDOS\n"));
213 			error = EINVAL;
214 			goto error_exit;
215 		}
216 		/*
217 		 * XXX - Many parts of the msdosfs driver seem to assume that
218 		 * the number of bytes per logical sector (BytesPerSec) will
219 		 * always be the same as the number of bytes per disk block
220 		 * Let's pretend it is.
221 		 */
222 		tmp = pmp->pm_BytesPerSec / bsize;
223 		pmp->pm_BytesPerSec  = bsize;
224 		pmp->pm_HugeSectors *= tmp;
225 		pmp->pm_HiddenSects *= tmp;
226 		pmp->pm_ResSectors  *= tmp;
227 		pmp->pm_Sectors     *= tmp;
228 		pmp->pm_FATsecs     *= tmp;
229 		SecPerClust         *= tmp;
230 	}
231 
232 	/* Check that fs has nonzero FAT size */
233 	if (pmp->pm_FATsecs == 0) {
234 		DPRINTF(("FATsecs is 0\n"));
235 		error = EINVAL;
236 		goto error_exit;
237 	}
238 
239 	pmp->pm_fatblk = pmp->pm_ResSectors;
240 	if (FAT32(pmp)) {
241 		pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
242 		pmp->pm_firstcluster = pmp->pm_fatblk
243 			+ (pmp->pm_FATs * pmp->pm_FATsecs);
244 		pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
245 	} else {
246 		pmp->pm_rootdirblk = pmp->pm_fatblk +
247 			(pmp->pm_FATs * pmp->pm_FATsecs);
248 		pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
249 				       + pmp->pm_BytesPerSec - 1)
250 			/ pmp->pm_BytesPerSec;/* in sectors */
251 		pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
252 	}
253 
254 	pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) /
255 	    SecPerClust;
256 	pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1;
257 	pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
258 
259 	if (flags & MSDOSFSMNT_GEMDOSFS) {
260 		if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) {
261 			pmp->pm_fatmask = FAT12_MASK;
262 			pmp->pm_fatmult = 3;
263 			pmp->pm_fatdiv = 2;
264 		} else {
265 			pmp->pm_fatmask = FAT16_MASK;
266 			pmp->pm_fatmult = 2;
267 			pmp->pm_fatdiv = 1;
268 		}
269 	} else if (pmp->pm_fatmask == 0) {
270 		if (pmp->pm_maxcluster
271 		    <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
272 			/*
273 			 * This will usually be a floppy disk. This size makes
274 			 * sure that one FAT entry will not be split across
275 			 * multiple blocks.
276 			 */
277 			pmp->pm_fatmask = FAT12_MASK;
278 			pmp->pm_fatmult = 3;
279 			pmp->pm_fatdiv = 2;
280 		} else {
281 			pmp->pm_fatmask = FAT16_MASK;
282 			pmp->pm_fatmult = 2;
283 			pmp->pm_fatdiv = 1;
284 		}
285 	}
286 	if (FAT12(pmp))
287 		pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
288 	else
289 		pmp->pm_fatblocksize = MAXBSIZE;
290 
291 	pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
292 	pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
293 
294 	/*
295 	 * Compute mask and shift value for isolating cluster relative byte
296 	 * offsets and cluster numbers from a file offset.
297 	 */
298 	pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
299 	pmp->pm_crbomask = pmp->pm_bpcluster - 1;
300 	pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
301 
302 	DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, "
303 	    "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, "
304 	    "cnshift=%lu)\n",
305 	    __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv,
306 	    pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift,
307 	    pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift));
308 	/*
309 	 * Check for valid cluster size
310 	 * must be a power of 2
311 	 */
312 	if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
313 		DPRINTF(("bpcluster %lu cnshift %lu\n",
314 		    pmp->pm_bpcluster, pmp->pm_cnshift));
315 		error = EINVAL;
316 		goto error_exit;
317 	}
318 
319 	/*
320 	 * Release the bootsector buffer.
321 	 */
322 	brelse(bp, BC_AGE);
323 	bp = NULL;
324 
325 	/*
326 	 * Check FSInfo.
327 	 */
328 	if (pmp->pm_fsinfo) {
329 		struct fsinfo *fp;
330 
331 		/*
332 		 * XXX	If the fsinfo block is stored on media with
333 		 *	2KB or larger sectors, is the fsinfo structure
334 		 *	padded at the end or in the middle?
335 		 */
336 		DPRINTF(("%s(bread %lu)\n", __func__,
337 		    (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo)));
338 		if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo),
339 		    pmp->pm_BytesPerSec, 0, &bp)) != 0)
340 			goto error_exit;
341 		fp = (struct fsinfo *)bp->b_data;
342 		if (!memcmp(fp->fsisig1, "RRaA", 4)
343 		    && !memcmp(fp->fsisig2, "rrAa", 4)
344 		    && !memcmp(fp->fsisig3, "\0\0\125\252", 4)
345 		    && !memcmp(fp->fsisig4, "\0\0\125\252", 4))
346 			pmp->pm_nxtfree = getulong(fp->fsinxtfree);
347 		else
348 			pmp->pm_fsinfo = 0;
349 		brelse(bp, 0);
350 		bp = NULL;
351 	}
352 
353 	/*
354 	 * Check and validate (or perhaps invalidate?) the fsinfo structure?
355 	 * XXX
356 	 */
357 	if (pmp->pm_fsinfo) {
358 		if ((pmp->pm_nxtfree == 0xffffffffUL) ||
359 		    (pmp->pm_nxtfree > pmp->pm_maxcluster))
360 			pmp->pm_fsinfo = 0;
361 	}
362 
363 	/*
364 	 * Allocate memory for the bitmap of allocated clusters, and then
365 	 * fill it in.
366 	 */
367 	pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap),
368 	    ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS));
369 	/*
370 	 * fillinusemap() needs pm_devvp.
371 	 */
372 	pmp->pm_dev = 0;
373 	pmp->pm_devvp = devvp;
374 
375 	/*
376 	 * Have the inuse map filled in.
377 	 */
378 	if ((error = fillinusemap(pmp)) != 0) {
379 		DPRINTF(("fillinusemap %d\n", error));
380 		goto error_exit;
381 	}
382 
383 	/*
384 	 * Finish up.
385 	 */
386 	if (ronly)
387 		pmp->pm_flags |= MSDOSFSMNT_RONLY;
388 	else
389 		pmp->pm_fmod = 1;
390 
391 	/*
392 	 * If we ever do quotas for DOS filesystems this would be a place
393 	 * to fill in the info in the msdosfsmount structure. You dolt,
394 	 * quotas on dos filesystems make no sense because files have no
395 	 * owners on dos filesystems. of course there is some empty space
396 	 * in the directory entry where we could put uid's and gid's.
397 	 */
398 
399 	return pmp;
400 
401 error_exit:
402 	if (bp)
403 		brelse(bp, BC_AGE);
404 	if (pmp) {
405 		if (pmp->pm_inusemap)
406 			free(pmp->pm_inusemap);
407 		free(pmp);
408 	}
409 	errno = error;
410 	return NULL;
411 }
412 
413 int
msdosfs_root(struct msdosfsmount * pmp,struct vnode * vp)414 msdosfs_root(struct msdosfsmount *pmp, struct vnode *vp) {
415 	struct denode *ndep;
416 	int error;
417 
418 	*vp = *pmp->pm_devvp;
419 	if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) {
420 		errno = error;
421 		return -1;
422 	}
423 	vp->v_data = ndep;
424 	return 0;
425 }
426