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