2 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
3 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
5 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
33 * Written by Paul Popelka (paulp@uts.amdahl.com)
35 * You can do anything you want with this software, just don't say you wrote
36 * it, and don't remove this notice.
38 * This software is provided "as is".
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.
48 #if HAVE_NBTOOL_CONFIG_H
49 #include "nbtool_config.h"
52 #include <sys/cdefs.h>
53 __KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.9 2015/03/29 05:52:59 agc Exp $");
55 #include <sys/param.h>
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>
74 #include "mkfs_msdos.h"
77 #define DPRINTF(a) printf a
83 msdosfs_mount(struct vnode
*devvp
, int flags
)
85 struct msdosfsmount
*pmp
= NULL
;
87 union bootsector
*bsp
;
88 struct byte_bpb33
*b33
;
89 struct byte_bpb50
*b50
;
90 struct byte_bpb710
*b710
;
92 int ronly
= 0, error
, tmp
;
94 struct msdos_options
*m
= devvp
->fs
->fs_specific
;
95 uint64_t psize
= m
->create_size
;
96 unsigned secsize
= 512;
98 DPRINTF(("%s(bread 0)\n", __func__
));
99 if ((error
= bread(devvp
, 0, secsize
, 0, &bp
)) != 0)
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
;
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
));
120 pmp
= ecalloc(1, sizeof *pmp
);
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.
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
;
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 "
148 pmp
->pm_BytesPerSec
, SecPerClust
,
149 pmp
->pm_SecPerTrack
));
155 if (pmp
->pm_Sectors
== 0) {
156 pmp
->pm_HiddenSects
= getulong(b50
->bpbHiddenSecs
);
157 pmp
->pm_HugeSectors
= getulong(b50
->bpbHugeSectors
);
159 pmp
->pm_HiddenSects
= getushort(b33
->bpbHiddenSecs
);
160 pmp
->pm_HugeSectors
= pmp
->pm_Sectors
;
163 if (pmp
->pm_RootDirEnts
== 0) {
164 unsigned short vers
= getushort(b710
->bpbFSVers
);
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.
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
));
176 pmp
->pm_fatmask
= FAT32_MASK
;
179 pmp
->pm_FATsecs
= getulong(b710
->bpbBigFATsecs
);
181 /* mirrorring is enabled if the FATMIRROR bit is not set */
182 if ((getushort(b710
->bpbExtFlags
) & FATMIRROR
) == 0)
183 pmp
->pm_flags
|= MSDOSFS_FATMIRROR
;
185 pmp
->pm_curfat
= getushort(b710
->bpbExtFlags
) & FATNUM
;
187 pmp
->pm_flags
|= MSDOSFS_FATMIRROR
;
189 if (flags
& MSDOSFSMNT_GEMDOSFS
) {
191 DPRINTF(("FAT32 for GEMDOS\n"));
193 * GEMDOS doesn't know FAT32.
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
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
)
212 DPRINTF(("consistency checks for GEMDOS\n"));
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.
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
;
232 /* Check that fs has nonzero FAT size */
233 if (pmp
->pm_FATsecs
== 0) {
234 DPRINTF(("FATsecs is 0\n"));
239 pmp
->pm_fatblk
= pmp
->pm_ResSectors
;
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
);
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
;
254 pmp
->pm_nmbrofclusters
= (pmp
->pm_HugeSectors
- pmp
->pm_firstcluster
) /
256 pmp
->pm_maxcluster
= pmp
->pm_nmbrofclusters
+ 1;
257 pmp
->pm_fatsize
= pmp
->pm_FATsecs
* pmp
->pm_BytesPerSec
;
259 if (flags
& MSDOSFSMNT_GEMDOSFS
) {
260 if (pmp
->pm_nmbrofclusters
<= (0xff0 - 2)) {
261 pmp
->pm_fatmask
= FAT12_MASK
;
265 pmp
->pm_fatmask
= FAT16_MASK
;
269 } else if (pmp
->pm_fatmask
== 0) {
270 if (pmp
->pm_maxcluster
271 <= ((CLUST_RSRVD
- CLUST_FIRST
) & FAT12_MASK
)) {
273 * This will usually be a floppy disk. This size makes
274 * sure that one FAT entry will not be split across
277 pmp
->pm_fatmask
= FAT12_MASK
;
281 pmp
->pm_fatmask
= FAT16_MASK
;
287 pmp
->pm_fatblocksize
= 3 * pmp
->pm_BytesPerSec
;
289 pmp
->pm_fatblocksize
= MAXBSIZE
;
291 pmp
->pm_fatblocksec
= pmp
->pm_fatblocksize
/ pmp
->pm_BytesPerSec
;
292 pmp
->pm_bnshift
= ffs(pmp
->pm_BytesPerSec
) - 1;
295 * Compute mask and shift value for isolating cluster relative byte
296 * offsets and cluster numbers from a file offset.
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;
302 DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, "
303 "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, "
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
));
309 * Check for valid cluster size
310 * must be a power of 2
312 if (pmp
->pm_bpcluster
^ (1 << pmp
->pm_cnshift
)) {
313 DPRINTF(("bpcluster %lu cnshift %lu\n",
314 pmp
->pm_bpcluster
, pmp
->pm_cnshift
));
320 * Release the bootsector buffer.
328 if (pmp
->pm_fsinfo
) {
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?
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)
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
);
354 * Check and validate (or perhaps invalidate?) the fsinfo structure?
357 if (pmp
->pm_fsinfo
) {
358 if ((pmp
->pm_nxtfree
== 0xffffffffUL
) ||
359 (pmp
->pm_nxtfree
> pmp
->pm_maxcluster
))
364 * Allocate memory for the bitmap of allocated clusters, and then
367 pmp
->pm_inusemap
= ecalloc(sizeof(*pmp
->pm_inusemap
),
368 ((pmp
->pm_maxcluster
+ N_INUSEBITS
) / N_INUSEBITS
));
370 * fillinusemap() needs pm_devvp.
373 pmp
->pm_devvp
= devvp
;
376 * Have the inuse map filled in.
378 if ((error
= fillinusemap(pmp
)) != 0) {
379 DPRINTF(("fillinusemap %d\n", error
));
387 pmp
->pm_flags
|= MSDOSFSMNT_RONLY
;
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.
405 if (pmp
->pm_inusemap
)
406 free(pmp
->pm_inusemap
);
414 msdosfs_root(struct msdosfsmount
*pmp
, struct vnode
*vp
) {
418 *vp
= *pmp
->pm_devvp
;
419 if ((error
= deget(pmp
, MSDOSFSROOT
, MSDOSFSROOT_OFS
, &ndep
)) != 0) {