msdosfs_vfsops.c revision 1.9
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
82struct msdosfsmount *
83msdosfs_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
401error_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
413int
414msdosfs_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