1 /*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6 * All rights reserved.
7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by TooLs GmbH.
20 * 4. The name of TooLs GmbH may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34 /*-
35 * Written by Paul Popelka (paulp@uts.amdahl.com)
36 *
37 * You can do anything you want with this software, just don't say you wrote
38 * it, and don't remove this notice.
39 *
40 * This software is provided "as is".
41 *
42 * The author supplies this software to be publicly redistributed on the
43 * understanding that the author is not responsible for the correct
44 * functioning of this software in any circumstances and is not liable for
45 * any damages caused by this software.
46 *
47 * October 1992
48 */
49
50 #include <sys/cdefs.h>
51 /* $NetBSD: msdosfs_vfsops.c,v 1.10 2016/01/30 09:59:27 mlelstv Exp $ */
52 #include <sys/param.h>
53 #include <sys/mount.h>
54
55 #include <errno.h>
56 #include <stdbool.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <stdlib.h>
60 #include <util.h>
61
62 #include <fs/msdosfs/bootsect.h>
63 #include <fs/msdosfs/bpb.h>
64 #include "msdos/denode.h"
65 #include <fs/msdosfs/fat.h>
66 #include <fs/msdosfs/msdosfsmount.h>
67
68 #include <mkfs_msdos.h>
69
70 #include "makefs.h"
71 #include "msdos.h"
72
73 struct msdosfsmount *
m_msdosfs_mount(struct m_vnode * devvp)74 m_msdosfs_mount(struct m_vnode *devvp)
75 {
76 struct msdosfsmount *pmp = NULL;
77 struct m_buf *bp;
78 union bootsector *bsp;
79 struct byte_bpb33 *b33;
80 struct byte_bpb50 *b50;
81 struct byte_bpb710 *b710;
82 uint8_t SecPerClust;
83 int ronly = 0, error;
84 unsigned secsize = 512;
85
86 MSDOSFS_DPRINTF(("%s(bread 0)\n", __func__));
87 if ((error = bread((void *)devvp, 0, secsize, 0, &bp)) != 0)
88 goto error_exit;
89
90 bsp = (union bootsector *)bp->b_data;
91 b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB;
92 b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB;
93 b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB;
94
95 if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 ||
96 bsp->bs50.bsBootSectSig1 != BOOTSIG1) {
97 MSDOSFS_DPRINTF(("bootsig0 %d bootsig1 %d\n",
98 bsp->bs50.bsBootSectSig0,
99 bsp->bs50.bsBootSectSig1));
100 error = EINVAL;
101 goto error_exit;
102 }
103
104 pmp = ecalloc(1, sizeof(*pmp));
105 /*
106 * Compute several useful quantities from the bpb in the
107 * bootsector. Copy in the dos 5 variant of the bpb then fix up
108 * the fields that are different between dos 5 and dos 3.3.
109 */
110 SecPerClust = b50->bpbSecPerClust;
111 pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec);
112 pmp->pm_ResSectors = getushort(b50->bpbResSectors);
113 pmp->pm_FATs = b50->bpbFATs;
114 pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts);
115 pmp->pm_Sectors = getushort(b50->bpbSectors);
116 pmp->pm_FATsecs = getushort(b50->bpbFATsecs);
117 pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack);
118 pmp->pm_Heads = getushort(b50->bpbHeads);
119 pmp->pm_Media = b50->bpbMedia;
120
121 MSDOSFS_DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, "
122 "RootDirEnts=%u, Sectors=%u, FATsecs=%lu, SecPerTrack=%u, "
123 "Heads=%u, Media=%u)\n",
124 __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors,
125 pmp->pm_FATs, pmp->pm_RootDirEnts, pmp->pm_Sectors,
126 pmp->pm_FATsecs, pmp->pm_SecPerTrack, pmp->pm_Heads,
127 pmp->pm_Media));
128
129 /* XXX - We should probably check more values here */
130 if (!pmp->pm_BytesPerSec || !SecPerClust
131 || pmp->pm_SecPerTrack > 63) {
132 MSDOSFS_DPRINTF(("bytespersec %d secperclust %d "
133 "secpertrack %d\n", pmp->pm_BytesPerSec,
134 SecPerClust, pmp->pm_SecPerTrack));
135 error = EINVAL;
136 goto error_exit;
137 }
138
139 if (pmp->pm_Sectors == 0) {
140 pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
141 pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors);
142 } else {
143 pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
144 pmp->pm_HugeSectors = pmp->pm_Sectors;
145 }
146
147 pmp->pm_flags = 0;
148 if (pmp->pm_RootDirEnts == 0) {
149 unsigned short vers = getushort(b710->bpbFSVers);
150 /*
151 * Some say that bsBootSectSig[23] must be zero, but
152 * Windows does not require this and some digital cameras
153 * do not set these to zero. Therefore, do not insist.
154 */
155 if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) {
156 MSDOSFS_DPRINTF(("sectors %d fatsecs %lu vers %d\n",
157 pmp->pm_Sectors, pmp->pm_FATsecs, vers));
158 error = EINVAL;
159 goto error_exit;
160 }
161 pmp->pm_fatmask = FAT32_MASK;
162 pmp->pm_fatmult = 4;
163 pmp->pm_fatdiv = 1;
164 pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs);
165
166 /* mirrorring is enabled if the FATMIRROR bit is not set */
167 if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0)
168 pmp->pm_flags |= MSDOSFS_FATMIRROR;
169 else
170 pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
171 } else
172 pmp->pm_flags |= MSDOSFS_FATMIRROR;
173
174 /* Check that fs has nonzero FAT size */
175 if (pmp->pm_FATsecs == 0) {
176 MSDOSFS_DPRINTF(("FATsecs is 0\n"));
177 error = EINVAL;
178 goto error_exit;
179 }
180
181 pmp->pm_fatblk = pmp->pm_ResSectors;
182 if (FAT32(pmp)) {
183 pmp->pm_rootdirblk = getulong(b710->bpbRootClust);
184 pmp->pm_firstcluster = pmp->pm_fatblk
185 + (pmp->pm_FATs * pmp->pm_FATsecs);
186 pmp->pm_fsinfo = getushort(b710->bpbFSInfo);
187 } else {
188 pmp->pm_rootdirblk = pmp->pm_fatblk +
189 (pmp->pm_FATs * pmp->pm_FATsecs);
190 pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry)
191 + pmp->pm_BytesPerSec - 1)
192 / pmp->pm_BytesPerSec;/* in sectors */
193 pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize;
194 }
195
196 pmp->pm_maxcluster = ((pmp->pm_HugeSectors - pmp->pm_firstcluster) /
197 SecPerClust) + 1;
198 pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec;
199
200 if (pmp->pm_fatmask == 0) {
201 if (pmp->pm_maxcluster
202 <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) {
203 /*
204 * This will usually be a floppy disk. This size makes
205 * sure that one FAT entry will not be split across
206 * multiple blocks.
207 */
208 pmp->pm_fatmask = FAT12_MASK;
209 pmp->pm_fatmult = 3;
210 pmp->pm_fatdiv = 2;
211 } else {
212 pmp->pm_fatmask = FAT16_MASK;
213 pmp->pm_fatmult = 2;
214 pmp->pm_fatdiv = 1;
215 }
216 }
217 if (FAT12(pmp))
218 pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec;
219 else
220 pmp->pm_fatblocksize = MAXBSIZE;
221
222 pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec;
223 pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1;
224
225 /*
226 * Compute mask and shift value for isolating cluster relative byte
227 * offsets and cluster numbers from a file offset.
228 */
229 pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec;
230 pmp->pm_crbomask = pmp->pm_bpcluster - 1;
231 pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1;
232
233 MSDOSFS_DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, "
234 "fatblocksize=%lu, fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, "
235 "crbomask=%lu, cnshift=%lu)\n",
236 __func__, (unsigned long)pmp->pm_fatmask, pmp->pm_fatmult,
237 pmp->pm_fatdiv, pmp->pm_fatblocksize, pmp->pm_fatblocksec,
238 pmp->pm_bnshift, pmp->pm_bpcluster, pmp->pm_crbomask,
239 pmp->pm_cnshift));
240 /*
241 * Check for valid cluster size
242 * must be a power of 2
243 */
244 if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) {
245 MSDOSFS_DPRINTF(("bpcluster %lu cnshift %lu\n",
246 pmp->pm_bpcluster, pmp->pm_cnshift));
247 error = EINVAL;
248 goto error_exit;
249 }
250
251 /*
252 * Release the bootsector buffer.
253 */
254 brelse(bp);
255 bp = NULL;
256
257 /*
258 * Check FSInfo.
259 */
260 if (pmp->pm_fsinfo) {
261 struct fsinfo *fp;
262
263 /*
264 * XXX If the fsinfo block is stored on media with
265 * 2KB or larger sectors, is the fsinfo structure
266 * padded at the end or in the middle?
267 */
268 if ((error = bread((void *)devvp, pmp->pm_fsinfo,
269 pmp->pm_BytesPerSec, 0, &bp)) != 0)
270 goto error_exit;
271 fp = (struct fsinfo *)bp->b_data;
272 if (!memcmp(fp->fsisig1, "RRaA", 4)
273 && !memcmp(fp->fsisig2, "rrAa", 4)
274 && !memcmp(fp->fsisig3, "\0\0\125\252", 4))
275 pmp->pm_nxtfree = getulong(fp->fsinxtfree);
276 else
277 pmp->pm_fsinfo = 0;
278 brelse(bp);
279 bp = NULL;
280 }
281
282 /*
283 * Check and validate (or perhaps invalidate?) the fsinfo structure?
284 * XXX
285 */
286 if (pmp->pm_fsinfo) {
287 if ((pmp->pm_nxtfree == 0xffffffffUL) ||
288 (pmp->pm_nxtfree > pmp->pm_maxcluster))
289 pmp->pm_fsinfo = 0;
290 }
291
292 /*
293 * Allocate memory for the bitmap of allocated clusters, and then
294 * fill it in.
295 */
296 pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap),
297 ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS));
298 /*
299 * fillinusemap() needs pm_devvp.
300 */
301 pmp->pm_dev = 0;
302 pmp->pm_devvp = (void *)devvp;
303
304 /*
305 * Have the inuse map filled in.
306 */
307 if ((error = fillinusemap(pmp)) != 0) {
308 MSDOSFS_DPRINTF(("fillinusemap %d\n", error));
309 goto error_exit;
310 }
311
312 /*
313 * Finish up.
314 */
315 if (ronly)
316 pmp->pm_flags |= MSDOSFSMNT_RONLY;
317 else
318 pmp->pm_fmod = 1;
319
320 /*
321 * If we ever do quotas for DOS filesystems this would be a place
322 * to fill in the info in the msdosfsmount structure. You dolt,
323 * quotas on dos filesystems make no sense because files have no
324 * owners on dos filesystems. of course there is some empty space
325 * in the directory entry where we could put uid's and gid's.
326 */
327
328 return pmp;
329
330 error_exit:
331 if (bp)
332 brelse(bp);
333 if (pmp) {
334 if (pmp->pm_inusemap)
335 free(pmp->pm_inusemap);
336 free(pmp);
337 }
338 errno = error;
339 return NULL;
340 }
341
342 int
msdosfs_root(struct msdosfsmount * pmp,struct m_vnode * vp)343 msdosfs_root(struct msdosfsmount *pmp, struct m_vnode *vp) {
344 struct denode *ndep;
345 int error;
346
347 *vp = *(struct m_vnode *)pmp->pm_devvp;
348 if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, 0, &ndep)) != 0) {
349 errno = error;
350 return -1;
351 }
352 vp->v_data = ndep;
353 return 0;
354 }
355
356 /*
357 * If we have an FSInfo block, update it.
358 */
359 int
msdosfs_fsiflush(struct msdosfsmount * pmp)360 msdosfs_fsiflush(struct msdosfsmount *pmp)
361 {
362 struct fsinfo *fp;
363 struct m_buf *bp;
364 int error;
365
366 if (pmp->pm_fsinfo == 0 || (pmp->pm_flags & MSDOSFS_FSIMOD) == 0) {
367 error = 0;
368 goto out;
369 }
370 error = bread((void *)pmp->pm_devvp, pmp->pm_fsinfo,
371 pmp->pm_BytesPerSec, NOCRED, &bp);
372 if (error != 0) {
373 brelse(bp);
374 goto out;
375 }
376 fp = (struct fsinfo *)bp->b_data;
377 putulong(fp->fsinfree, pmp->pm_freeclustercount);
378 putulong(fp->fsinxtfree, pmp->pm_nxtfree);
379 pmp->pm_flags &= ~MSDOSFS_FSIMOD;
380 error = bwrite(bp);
381
382 out:
383 return (error);
384 }
385