Print this page
7127 remove -Wno-missing-braces from Makefile.uts
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/fs/pcfs/pc_vfsops.c
+++ new/usr/src/uts/common/fs/pcfs/pc_vfsops.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 #include <sys/param.h>
28 28 #include <sys/systm.h>
29 29 #include <sys/kmem.h>
30 30 #include <sys/user.h>
31 31 #include <sys/proc.h>
32 32 #include <sys/cred.h>
33 33 #include <sys/disp.h>
34 34 #include <sys/buf.h>
35 35 #include <sys/vfs.h>
36 36 #include <sys/vfs_opreg.h>
37 37 #include <sys/vnode.h>
38 38 #include <sys/fdio.h>
39 39 #include <sys/file.h>
40 40 #include <sys/uio.h>
41 41 #include <sys/conf.h>
42 42 #include <sys/statvfs.h>
43 43 #include <sys/mount.h>
44 44 #include <sys/pathname.h>
45 45 #include <sys/cmn_err.h>
46 46 #include <sys/debug.h>
47 47 #include <sys/sysmacros.h>
48 48 #include <sys/conf.h>
49 49 #include <sys/mkdev.h>
50 50 #include <sys/swap.h>
51 51 #include <sys/sunddi.h>
52 52 #include <sys/sunldi.h>
53 53 #include <sys/dktp/fdisk.h>
54 54 #include <sys/fs/pc_label.h>
55 55 #include <sys/fs/pc_fs.h>
56 56 #include <sys/fs/pc_dir.h>
57 57 #include <sys/fs/pc_node.h>
58 58 #include <fs/fs_subr.h>
59 59 #include <sys/modctl.h>
60 60 #include <sys/dkio.h>
61 61 #include <sys/open.h>
62 62 #include <sys/mntent.h>
63 63 #include <sys/policy.h>
64 64 #include <sys/atomic.h>
65 65 #include <sys/sdt.h>
66 66
67 67 /*
68 68 * The majority of PC media use a 512 sector size, but
69 69 * occasionally you will run across a 1k sector size.
70 70 * For media with a 1k sector size, fd_strategy() requires
71 71 * the I/O size to be a 1k multiple; so when the sector size
72 72 * is not yet known, always read 1k.
73 73 */
74 74 #define PC_SAFESECSIZE (PC_SECSIZE * 2)
75 75
76 76 static int pcfs_pseudo_floppy(dev_t);
77 77
78 78 static int pcfsinit(int, char *);
79 79 static int pcfs_mount(struct vfs *, struct vnode *, struct mounta *,
80 80 struct cred *);
81 81 static int pcfs_unmount(struct vfs *, int, struct cred *);
82 82 static int pcfs_root(struct vfs *, struct vnode **);
83 83 static int pcfs_statvfs(struct vfs *, struct statvfs64 *);
84 84 static int pc_syncfsnodes(struct pcfs *);
85 85 static int pcfs_sync(struct vfs *, short, struct cred *);
86 86 static int pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp);
87 87 static void pcfs_freevfs(vfs_t *vfsp);
88 88
89 89 static int pc_readfat(struct pcfs *fsp, uchar_t *fatp);
90 90 static int pc_writefat(struct pcfs *fsp, daddr_t start);
91 91
92 92 static int pc_getfattype(struct pcfs *fsp);
93 93 static void pcfs_parse_mntopts(struct pcfs *fsp);
94 94
95 95
96 96 /*
97 97 * pcfs mount options table
98 98 */
99 99
100 100 static char *nohidden_cancel[] = { MNTOPT_PCFS_HIDDEN, NULL };
101 101 static char *hidden_cancel[] = { MNTOPT_PCFS_NOHIDDEN, NULL };
102 102 static char *nofoldcase_cancel[] = { MNTOPT_PCFS_FOLDCASE, NULL };
103 103 static char *foldcase_cancel[] = { MNTOPT_PCFS_NOFOLDCASE, NULL };
104 104 static char *clamptime_cancel[] = { MNTOPT_PCFS_NOCLAMPTIME, NULL };
105 105 static char *noclamptime_cancel[] = { MNTOPT_PCFS_CLAMPTIME, NULL };
106 106 static char *atime_cancel[] = { MNTOPT_NOATIME, NULL };
107 107 static char *noatime_cancel[] = { MNTOPT_ATIME, NULL };
108 108
109 109 static mntopt_t mntopts[] = {
110 110 /*
111 111 * option name cancel option default arg flags opt data
112 112 */
113 113 { MNTOPT_PCFS_NOHIDDEN, nohidden_cancel, NULL, 0, NULL },
114 114 { MNTOPT_PCFS_HIDDEN, hidden_cancel, NULL, MO_DEFAULT, NULL },
115 115 { MNTOPT_PCFS_NOFOLDCASE, nofoldcase_cancel, NULL, MO_DEFAULT, NULL },
116 116 { MNTOPT_PCFS_FOLDCASE, foldcase_cancel, NULL, 0, NULL },
117 117 { MNTOPT_PCFS_CLAMPTIME, clamptime_cancel, NULL, MO_DEFAULT, NULL },
118 118 { MNTOPT_PCFS_NOCLAMPTIME, noclamptime_cancel, NULL, NULL, NULL },
119 119 { MNTOPT_NOATIME, noatime_cancel, NULL, NULL, NULL },
120 120 { MNTOPT_ATIME, atime_cancel, NULL, NULL, NULL },
121 121 { MNTOPT_PCFS_TIMEZONE, NULL, "+0", MO_DEFAULT | MO_HASVALUE, NULL },
122 122 { MNTOPT_PCFS_SECSIZE, NULL, NULL, MO_HASVALUE, NULL }
123 123 };
124 124
125 125 static mntopts_t pcfs_mntopts = {
126 126 sizeof (mntopts) / sizeof (mntopt_t),
127 127 mntopts
128 128 };
129 129
130 130 int pcfsdebuglevel = 0;
131 131
132 132 /*
133 133 * pcfslock: protects the list of mounted pc filesystems "pc_mounttab.
134 134 * pcfs_lock: (inside per filesystem structure "pcfs")
135 135 * per filesystem lock. Most of the vfsops and vnodeops are
136 136 * protected by this lock.
137 137 * pcnodes_lock: protects the pcnode hash table "pcdhead", "pcfhead".
138 138 *
139 139 * Lock hierarchy: pcfslock > pcfs_lock > pcnodes_lock
140 140 *
141 141 * pcfs_mountcount: used to prevent module unloads while there is still
142 142 * pcfs state from a former mount hanging around. With
143 143 * forced umount support, the filesystem module must not
144 144 * be allowed to go away before the last VFS_FREEVFS()
145 145 * call has been made.
146 146 * Since this is just an atomic counter, there's no need
147 147 * for locking.
148 148 */
149 149 kmutex_t pcfslock;
150 150 krwlock_t pcnodes_lock;
151 151 uint32_t pcfs_mountcount;
152 152
153 153 static int pcfstype;
154 154
155 155 static vfsdef_t vfw = {
156 156 VFSDEF_VERSION,
157 157 "pcfs",
158 158 pcfsinit,
159 159 VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_CANLOFI,
160 160 &pcfs_mntopts
161 161 };
162 162
↓ open down ↓ |
162 lines elided |
↑ open up ↑ |
163 163 extern struct mod_ops mod_fsops;
164 164
165 165 static struct modlfs modlfs = {
166 166 &mod_fsops,
167 167 "PC filesystem",
168 168 &vfw
169 169 };
170 170
171 171 static struct modlinkage modlinkage = {
172 172 MODREV_1,
173 - &modlfs,
174 - NULL
173 + { &modlfs, NULL }
175 174 };
176 175
177 176 int
178 177 _init(void)
179 178 {
180 179 int error;
181 180
182 181 #if !defined(lint)
183 182 /* make sure the on-disk structures are sane */
184 183 ASSERT(sizeof (struct pcdir) == 32);
185 184 ASSERT(sizeof (struct pcdir_lfn) == 32);
186 185 #endif
187 186 mutex_init(&pcfslock, NULL, MUTEX_DEFAULT, NULL);
188 187 rw_init(&pcnodes_lock, NULL, RW_DEFAULT, NULL);
189 188 error = mod_install(&modlinkage);
190 189 if (error) {
191 190 mutex_destroy(&pcfslock);
192 191 rw_destroy(&pcnodes_lock);
193 192 }
194 193 return (error);
195 194 }
196 195
197 196 int
198 197 _fini(void)
199 198 {
200 199 int error;
201 200
202 201 /*
203 202 * If a forcedly unmounted instance is still hanging around,
204 203 * we cannot allow the module to be unloaded because that would
205 204 * cause panics once the VFS framework decides it's time to call
206 205 * into VFS_FREEVFS().
207 206 */
208 207 if (pcfs_mountcount)
209 208 return (EBUSY);
210 209
211 210 error = mod_remove(&modlinkage);
212 211 if (error)
213 212 return (error);
214 213 mutex_destroy(&pcfslock);
215 214 rw_destroy(&pcnodes_lock);
216 215 /*
217 216 * Tear down the operations vectors
218 217 */
219 218 (void) vfs_freevfsops_by_type(pcfstype);
220 219 vn_freevnodeops(pcfs_fvnodeops);
221 220 vn_freevnodeops(pcfs_dvnodeops);
222 221 return (0);
223 222 }
224 223
225 224 int
↓ open down ↓ |
41 lines elided |
↑ open up ↑ |
226 225 _info(struct modinfo *modinfop)
227 226 {
228 227 return (mod_info(&modlinkage, modinfop));
229 228 }
230 229
231 230 /* ARGSUSED1 */
232 231 static int
233 232 pcfsinit(int fstype, char *name)
234 233 {
235 234 static const fs_operation_def_t pcfs_vfsops_template[] = {
236 - VFSNAME_MOUNT, { .vfs_mount = pcfs_mount },
237 - VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount },
238 - VFSNAME_ROOT, { .vfs_root = pcfs_root },
239 - VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs },
240 - VFSNAME_SYNC, { .vfs_sync = pcfs_sync },
241 - VFSNAME_VGET, { .vfs_vget = pcfs_vget },
242 - VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs },
243 - NULL, NULL
235 + { VFSNAME_MOUNT, { .vfs_mount = pcfs_mount } },
236 + { VFSNAME_UNMOUNT, { .vfs_unmount = pcfs_unmount } },
237 + { VFSNAME_ROOT, { .vfs_root = pcfs_root } },
238 + { VFSNAME_STATVFS, { .vfs_statvfs = pcfs_statvfs } },
239 + { VFSNAME_SYNC, { .vfs_sync = pcfs_sync } },
240 + { VFSNAME_VGET, { .vfs_vget = pcfs_vget } },
241 + { VFSNAME_FREEVFS, { .vfs_freevfs = pcfs_freevfs } },
242 + { NULL, { NULL } }
244 243 };
245 244 int error;
246 245
247 246 error = vfs_setfsops(fstype, pcfs_vfsops_template, NULL);
248 247 if (error != 0) {
249 248 cmn_err(CE_WARN, "pcfsinit: bad vfs ops template");
250 249 return (error);
251 250 }
252 251
253 252 error = vn_make_ops("pcfs", pcfs_fvnodeops_template, &pcfs_fvnodeops);
254 253 if (error != 0) {
255 254 (void) vfs_freevfsops_by_type(fstype);
256 255 cmn_err(CE_WARN, "pcfsinit: bad file vnode ops template");
257 256 return (error);
258 257 }
259 258
260 259 error = vn_make_ops("pcfsd", pcfs_dvnodeops_template, &pcfs_dvnodeops);
261 260 if (error != 0) {
262 261 (void) vfs_freevfsops_by_type(fstype);
263 262 vn_freevnodeops(pcfs_fvnodeops);
264 263 cmn_err(CE_WARN, "pcfsinit: bad dir vnode ops template");
265 264 return (error);
266 265 }
267 266
268 267 pcfstype = fstype;
269 268 (void) pc_init();
270 269 pcfs_mountcount = 0;
271 270 return (0);
272 271 }
273 272
274 273 static struct pcfs *pc_mounttab = NULL;
275 274
276 275 extern struct pcfs_args pc_tz;
277 276
278 277 /*
279 278 * Define some special logical drives we use internal to this file.
280 279 */
281 280 #define BOOT_PARTITION_DRIVE 99
282 281 #define PRIMARY_DOS_DRIVE 1
283 282 #define UNPARTITIONED_DRIVE 0
284 283
285 284 static int
286 285 pcfs_device_identify(
287 286 struct vfs *vfsp,
288 287 struct mounta *uap,
289 288 struct cred *cr,
290 289 int *dos_ldrive,
291 290 dev_t *xdev)
292 291 {
293 292 struct pathname special;
294 293 char *c;
295 294 struct vnode *svp = NULL;
296 295 struct vnode *lvp = NULL;
297 296 int oflag, aflag;
298 297 int error;
299 298
300 299 /*
301 300 * Resolve path name of special file being mounted.
302 301 */
303 302 if (error = pn_get(uap->spec, UIO_USERSPACE, &special)) {
304 303 return (error);
305 304 }
306 305
307 306 *dos_ldrive = -1;
308 307
309 308 if (error =
310 309 lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW, NULLVPP, &svp)) {
311 310 /*
312 311 * If there's no device node, the name specified most likely
313 312 * maps to a PCFS-style "partition specifier" to select a
314 313 * harddisk primary/logical partition. Disable floppy-specific
315 314 * checks in such cases unless an explicit :A or :B is
316 315 * requested.
317 316 */
318 317
319 318 /*
320 319 * Split the pathname string at the last ':' separator.
321 320 * If there's no ':' in the device name, or the ':' is the
322 321 * last character in the string, the name is invalid and
323 322 * the error from the previous lookup will be returned.
324 323 */
325 324 c = strrchr(special.pn_path, ':');
326 325 if (c == NULL || strlen(c) == 0)
327 326 goto devlookup_done;
328 327
329 328 *c++ = '\0';
330 329
331 330 /*
332 331 * PCFS partition name suffixes can be:
333 332 * - "boot" to indicate the X86BOOT partition
334 333 * - a drive letter [c-z] for the "DOS logical drive"
335 334 * - a drive number 1..24 for the "DOS logical drive"
336 335 * - a "floppy name letter", 'a' or 'b' (just strip this)
337 336 */
338 337 if (strcasecmp(c, "boot") == 0) {
339 338 /*
340 339 * The Solaris boot partition is requested.
341 340 */
342 341 *dos_ldrive = BOOT_PARTITION_DRIVE;
343 342 } else if (strspn(c, "0123456789") == strlen(c)) {
344 343 /*
345 344 * All digits - parse the partition number.
346 345 */
347 346 long drvnum = 0;
348 347
349 348 if ((error = ddi_strtol(c, NULL, 10, &drvnum)) == 0) {
350 349 /*
351 350 * A number alright - in the allowed range ?
352 351 */
353 352 if (drvnum > 24 || drvnum == 0)
354 353 error = ENXIO;
355 354 }
356 355 if (error)
357 356 goto devlookup_done;
358 357 *dos_ldrive = (int)drvnum;
359 358 } else if (strlen(c) == 1) {
360 359 /*
361 360 * A single trailing character was specified.
362 361 * - [c-zC-Z] means a harddisk partition, and
363 362 * we retrieve the partition number.
364 363 * - [abAB] means a floppy drive, so we swallow
365 364 * the "drive specifier" and test later
366 365 * whether the physical device is a floppy.
367 366 */
368 367 *c = tolower(*c);
369 368 if (*c == 'a' || *c == 'b') {
370 369 *dos_ldrive = UNPARTITIONED_DRIVE;
371 370 } else if (*c < 'c' || *c > 'z') {
372 371 error = ENXIO;
373 372 goto devlookup_done;
374 373 } else {
375 374 *dos_ldrive = 1 + *c - 'c';
376 375 }
377 376 } else {
378 377 /*
379 378 * Can't parse this - pass through previous error.
380 379 */
381 380 goto devlookup_done;
382 381 }
383 382
384 383
385 384 error = lookupname(special.pn_path, UIO_SYSSPACE, FOLLOW,
386 385 NULLVPP, &svp);
387 386 } else {
388 387 *dos_ldrive = UNPARTITIONED_DRIVE;
389 388 }
390 389 devlookup_done:
391 390 pn_free(&special);
392 391 if (error)
393 392 return (error);
394 393
395 394 ASSERT(*dos_ldrive >= UNPARTITIONED_DRIVE);
396 395
397 396 /*
398 397 * Verify caller's permission to open the device special file.
399 398 */
400 399 if ((vfsp->vfs_flag & VFS_RDONLY) != 0 ||
401 400 ((uap->flags & MS_RDONLY) != 0)) {
402 401 oflag = FREAD;
403 402 aflag = VREAD;
404 403 } else {
405 404 oflag = FREAD | FWRITE;
406 405 aflag = VREAD | VWRITE;
407 406 }
408 407
409 408 error = vfs_get_lofi(vfsp, &lvp);
410 409
411 410 if (error > 0) {
412 411 if (error == ENOENT)
413 412 error = ENODEV;
414 413 goto out;
415 414 } else if (error == 0) {
416 415 *xdev = lvp->v_rdev;
417 416 } else {
418 417 *xdev = svp->v_rdev;
419 418
420 419 if (svp->v_type != VBLK) {
421 420 error = ENOTBLK;
422 421 goto out;
423 422 }
424 423
425 424 if ((error = secpolicy_spec_open(cr, svp, oflag)) != 0)
426 425 goto out;
427 426 }
428 427
429 428 if (getmajor(*xdev) >= devcnt) {
430 429 error = ENXIO;
431 430 goto out;
432 431 }
433 432
434 433 if ((error = VOP_ACCESS(svp, aflag, 0, cr, NULL)) != 0)
435 434 goto out;
436 435
437 436 out:
438 437 if (svp != NULL)
439 438 VN_RELE(svp);
440 439 if (lvp != NULL)
441 440 VN_RELE(lvp);
442 441 return (error);
443 442 }
444 443
445 444 static int
446 445 pcfs_device_ismounted(
447 446 struct vfs *vfsp,
448 447 int dos_ldrive,
449 448 dev_t xdev,
450 449 int *remounting,
451 450 dev_t *pseudodev)
452 451 {
453 452 struct pcfs *fsp;
454 453 int remount = *remounting;
455 454
456 455 /*
457 456 * Ensure that this logical drive isn't already mounted, unless
458 457 * this is a REMOUNT request.
459 458 * Note: The framework will perform this check if the "...:c"
460 459 * PCFS-style "logical drive" syntax has not been used and an
461 460 * actually existing physical device is backing this filesystem.
462 461 * Once all block device drivers support PC-style partitioning,
463 462 * this codeblock can be dropped.
464 463 */
465 464 *pseudodev = xdev;
466 465
467 466 if (dos_ldrive) {
468 467 mutex_enter(&pcfslock);
469 468 for (fsp = pc_mounttab; fsp; fsp = fsp->pcfs_nxt)
470 469 if (fsp->pcfs_xdev == xdev &&
471 470 fsp->pcfs_ldrive == dos_ldrive) {
472 471 mutex_exit(&pcfslock);
473 472 if (remount) {
474 473 return (0);
475 474 } else {
476 475 return (EBUSY);
477 476 }
478 477 }
479 478 /*
480 479 * Assign a unique device number for the vfs
481 480 * The old way (getudev() + a constantly incrementing
482 481 * major number) was wrong because it changes vfs_dev
483 482 * across mounts and reboots, which breaks nfs file handles.
484 483 * UFS just uses the real dev_t. We can't do that because
485 484 * of the way pcfs opens fdisk partitons (the :c and :d
486 485 * partitions are on the same dev_t). Though that _might_
487 486 * actually be ok, since the file handle contains an
488 487 * absolute block number, it's probably better to make them
489 488 * different. So I think we should retain the original
490 489 * dev_t, but come up with a different minor number based
491 490 * on the logical drive that will _always_ come up the same.
492 491 * For now, we steal the upper 6 bits.
493 492 */
494 493 #ifdef notdef
495 494 /* what should we do here? */
496 495 if (((getminor(xdev) >> 12) & 0x3F) != 0)
497 496 printf("whoops - upper bits used!\n");
498 497 #endif
499 498 *pseudodev = makedevice(getmajor(xdev),
500 499 ((dos_ldrive << 12) | getminor(xdev)) & MAXMIN32);
501 500 if (vfs_devmounting(*pseudodev, vfsp)) {
502 501 mutex_exit(&pcfslock);
503 502 return (EBUSY);
504 503 }
505 504 if (vfs_devismounted(*pseudodev)) {
506 505 mutex_exit(&pcfslock);
507 506 if (remount) {
508 507 return (0);
509 508 } else {
510 509 return (EBUSY);
511 510 }
512 511 }
513 512 mutex_exit(&pcfslock);
514 513 } else {
515 514 *pseudodev = xdev;
516 515 if (vfs_devmounting(*pseudodev, vfsp)) {
517 516 return (EBUSY);
518 517 }
519 518 if (vfs_devismounted(*pseudodev))
520 519 if (remount) {
521 520 return (0);
522 521 } else {
523 522 return (EBUSY);
524 523 }
525 524 }
526 525
527 526 /*
528 527 * This is not a remount. Even if MS_REMOUNT was requested,
529 528 * the caller needs to proceed as it would on an ordinary
530 529 * mount.
531 530 */
532 531 *remounting = 0;
533 532
534 533 ASSERT(*pseudodev);
535 534 return (0);
536 535 }
537 536
538 537 /*
539 538 * Get the PCFS-specific mount options from the VFS framework.
540 539 * For "timezone" and "secsize", we need to parse the number
541 540 * ourselves and ensure its validity.
542 541 * Note: "secsize" is deliberately undocumented at this time,
543 542 * it's a workaround for devices (particularly: lofi image files)
544 543 * that don't support the DKIOCGMEDIAINFO ioctl for autodetection.
545 544 */
546 545 static void
547 546 pcfs_parse_mntopts(struct pcfs *fsp)
548 547 {
549 548 char *c;
550 549 char *endptr;
551 550 long l;
552 551 struct vfs *vfsp = fsp->pcfs_vfs;
553 552
554 553 ASSERT(fsp->pcfs_secondswest == 0);
555 554 ASSERT(fsp->pcfs_secsize == 0);
556 555
557 556 if (vfs_optionisset(vfsp, MNTOPT_PCFS_HIDDEN, NULL))
558 557 fsp->pcfs_flags |= PCFS_HIDDEN;
559 558 if (vfs_optionisset(vfsp, MNTOPT_PCFS_FOLDCASE, NULL))
560 559 fsp->pcfs_flags |= PCFS_FOLDCASE;
561 560 if (vfs_optionisset(vfsp, MNTOPT_PCFS_NOCLAMPTIME, NULL))
562 561 fsp->pcfs_flags |= PCFS_NOCLAMPTIME;
563 562 if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL))
564 563 fsp->pcfs_flags |= PCFS_NOATIME;
565 564
566 565 if (vfs_optionisset(vfsp, MNTOPT_PCFS_TIMEZONE, &c)) {
567 566 if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
568 567 endptr == c + strlen(c)) {
569 568 /*
570 569 * A number alright - in the allowed range ?
571 570 */
572 571 if (l <= -12*3600 || l >= 12*3600) {
573 572 cmn_err(CE_WARN, "!pcfs: invalid use of "
574 573 "'timezone' mount option - %ld "
575 574 "is out of range. Assuming 0.", l);
576 575 l = 0;
577 576 }
578 577 } else {
579 578 cmn_err(CE_WARN, "!pcfs: invalid use of "
580 579 "'timezone' mount option - argument %s "
581 580 "is not a valid number. Assuming 0.", c);
582 581 l = 0;
583 582 }
584 583 fsp->pcfs_secondswest = l;
585 584 }
586 585
587 586 /*
588 587 * The "secsize=..." mount option is a workaround for the lack of
589 588 * lofi(7d) support for DKIOCGMEDIAINFO. If PCFS wants to parse the
590 589 * partition table of a disk image and it has been partitioned with
591 590 * sector sizes other than 512 bytes, we'd fail on loopback'ed disk
592 591 * images.
593 592 * That should really be fixed in lofi ... this is a workaround.
594 593 */
595 594 if (vfs_optionisset(vfsp, MNTOPT_PCFS_SECSIZE, &c)) {
596 595 if (ddi_strtol(c, &endptr, 10, &l) == 0 &&
597 596 endptr == c + strlen(c)) {
598 597 /*
599 598 * A number alright - a valid sector size as well ?
600 599 */
601 600 if (!VALID_SECSIZE(l)) {
602 601 cmn_err(CE_WARN, "!pcfs: invalid use of "
603 602 "'secsize' mount option - %ld is "
604 603 "unsupported. Autodetecting.", l);
605 604 l = 0;
606 605 }
607 606 } else {
608 607 cmn_err(CE_WARN, "!pcfs: invalid use of "
609 608 "'secsize' mount option - argument %s "
610 609 "is not a valid number. Autodetecting.", c);
611 610 l = 0;
612 611 }
613 612 fsp->pcfs_secsize = l;
614 613 fsp->pcfs_sdshift = ddi_ffs(l / DEV_BSIZE) - 1;
615 614 }
616 615 }
617 616
618 617 /*
619 618 * vfs operations
620 619 */
621 620
622 621 /*
623 622 * pcfs_mount - backend for VFS_MOUNT() on PCFS.
624 623 */
625 624 static int
626 625 pcfs_mount(
627 626 struct vfs *vfsp,
628 627 struct vnode *mvp,
629 628 struct mounta *uap,
630 629 struct cred *cr)
631 630 {
632 631 struct pcfs *fsp;
633 632 struct vnode *devvp;
634 633 dev_t pseudodev;
635 634 dev_t xdev;
636 635 int dos_ldrive = 0;
637 636 int error;
638 637 int remounting;
639 638
640 639 if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
641 640 return (error);
642 641
643 642 if (mvp->v_type != VDIR)
644 643 return (ENOTDIR);
645 644
646 645 mutex_enter(&mvp->v_lock);
647 646 if ((uap->flags & MS_REMOUNT) == 0 &&
648 647 (uap->flags & MS_OVERLAY) == 0 &&
649 648 (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
650 649 mutex_exit(&mvp->v_lock);
651 650 return (EBUSY);
652 651 }
653 652 mutex_exit(&mvp->v_lock);
654 653
655 654 /*
656 655 * PCFS doesn't do mount arguments anymore - everything's a mount
657 656 * option these days. In order not to break existing callers, we
658 657 * don't reject it yet, just warn that the data (if any) is ignored.
659 658 */
660 659 if (uap->datalen != 0)
661 660 cmn_err(CE_WARN, "!pcfs: deprecated use of mount(2) with "
662 661 "mount argument structures instead of mount options. "
663 662 "Ignoring mount(2) 'dataptr' argument.");
664 663
665 664 /*
666 665 * This is needed early, to make sure the access / open calls
667 666 * are done using the correct mode. Processing this mount option
668 667 * only when calling pcfs_parse_mntopts() would lead us to attempt
669 668 * a read/write access to a possibly writeprotected device, and
670 669 * a readonly mount attempt might fail because of that.
671 670 */
672 671 if (uap->flags & MS_RDONLY) {
673 672 vfsp->vfs_flag |= VFS_RDONLY;
674 673 vfs_setmntopt(vfsp, MNTOPT_RO, NULL, 0);
675 674 }
676 675
677 676 /*
678 677 * For most filesystems, this is just a lookupname() on the
679 678 * mount pathname string. PCFS historically has to do its own
680 679 * partition table parsing because not all Solaris architectures
681 680 * support all styles of partitioning that PC media can have, and
682 681 * hence PCFS understands "device names" that don't map to actual
683 682 * physical device nodes. Parsing the "PCFS syntax" for device
684 683 * names is done in pcfs_device_identify() - see there.
685 684 *
686 685 * Once all block device drivers that can host FAT filesystems have
687 686 * been enhanced to create device nodes for all PC-style partitions,
688 687 * this code can go away.
689 688 */
690 689 if (error = pcfs_device_identify(vfsp, uap, cr, &dos_ldrive, &xdev))
691 690 return (error);
692 691
693 692 /*
694 693 * As with looking up the actual device to mount, PCFS cannot rely
695 694 * on just the checks done by vfs_ismounted() whether a given device
696 695 * is mounted already. The additional check against the "PCFS syntax"
697 696 * is done in pcfs_device_ismounted().
698 697 */
699 698 remounting = (uap->flags & MS_REMOUNT);
700 699
701 700 if (error = pcfs_device_ismounted(vfsp, dos_ldrive, xdev, &remounting,
702 701 &pseudodev))
703 702 return (error);
704 703
705 704 if (remounting)
706 705 return (0);
707 706
708 707 /*
709 708 * Mount the filesystem.
710 709 * An instance structure is required before the attempt to locate
711 710 * and parse the FAT BPB. This is because mount options may change
712 711 * the behaviour of the filesystem type matching code. Precreate
713 712 * it and fill it in to a degree that allows parsing the mount
714 713 * options.
715 714 */
716 715 devvp = makespecvp(xdev, VBLK);
717 716 if (IS_SWAPVP(devvp)) {
718 717 VN_RELE(devvp);
719 718 return (EBUSY);
720 719 }
721 720 error = VOP_OPEN(&devvp,
722 721 (vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD | FWRITE, cr, NULL);
723 722 if (error) {
724 723 VN_RELE(devvp);
725 724 return (error);
726 725 }
727 726
728 727 fsp = kmem_zalloc(sizeof (*fsp), KM_SLEEP);
729 728 fsp->pcfs_vfs = vfsp;
730 729 fsp->pcfs_xdev = xdev;
731 730 fsp->pcfs_devvp = devvp;
732 731 fsp->pcfs_ldrive = dos_ldrive;
733 732 mutex_init(&fsp->pcfs_lock, NULL, MUTEX_DEFAULT, NULL);
734 733
735 734 pcfs_parse_mntopts(fsp);
736 735
737 736 /*
738 737 * This is the actual "mount" - the PCFS superblock check.
739 738 *
740 739 * Find the requested logical drive and the FAT BPB therein.
741 740 * Check device type and flag the instance if media is removeable.
742 741 *
743 742 * Initializes most members of the filesystem instance structure.
744 743 * Returns EINVAL if no valid BPB can be found. Other errors may
745 744 * occur after I/O failures, or when invalid / unparseable partition
746 745 * tables are encountered.
747 746 */
748 747 if (error = pc_getfattype(fsp))
749 748 goto errout;
750 749
751 750 /*
752 751 * Now that the BPB has been parsed, this structural information
753 752 * is available and known to be valid. Initialize the VFS.
754 753 */
755 754 vfsp->vfs_data = fsp;
756 755 vfsp->vfs_dev = pseudodev;
757 756 vfsp->vfs_fstype = pcfstype;
758 757 vfs_make_fsid(&vfsp->vfs_fsid, pseudodev, pcfstype);
759 758 vfsp->vfs_bcount = 0;
760 759 vfsp->vfs_bsize = fsp->pcfs_clsize;
761 760
762 761 /*
763 762 * Validate that we can access the FAT and that it is, to the
764 763 * degree we can verify here, self-consistent.
765 764 */
766 765 if (error = pc_verify(fsp))
767 766 goto errout;
768 767
769 768 /*
770 769 * Record the time of the mount, to return as an "approximate"
771 770 * timestamp for the FAT root directory. Since FAT roots don't
772 771 * have timestamps, this is less confusing to the user than
773 772 * claiming "zero" / Jan/01/1970.
774 773 */
775 774 gethrestime(&fsp->pcfs_mounttime);
776 775
777 776 /*
778 777 * Fix up the mount options. Because "noatime" is made default on
779 778 * removeable media only, a fixed disk will have neither "atime"
780 779 * nor "noatime" set. We set the options explicitly depending on
781 780 * the PCFS_NOATIME flag, to inform the user of what applies.
782 781 * Mount option cancellation will take care that the mutually
783 782 * exclusive 'other' is cleared.
784 783 */
785 784 vfs_setmntopt(vfsp,
786 785 fsp->pcfs_flags & PCFS_NOATIME ? MNTOPT_NOATIME : MNTOPT_ATIME,
787 786 NULL, 0);
788 787
789 788 /*
790 789 * All clear - insert the FS instance into PCFS' list.
791 790 */
792 791 mutex_enter(&pcfslock);
793 792 fsp->pcfs_nxt = pc_mounttab;
794 793 pc_mounttab = fsp;
795 794 mutex_exit(&pcfslock);
796 795 atomic_inc_32(&pcfs_mountcount);
797 796 return (0);
798 797
799 798 errout:
800 799 (void) VOP_CLOSE(devvp,
801 800 vfsp->vfs_flag & VFS_RDONLY ? FREAD : FREAD | FWRITE,
802 801 1, (offset_t)0, cr, NULL);
803 802 VN_RELE(devvp);
804 803 mutex_destroy(&fsp->pcfs_lock);
805 804 kmem_free(fsp, sizeof (*fsp));
806 805 return (error);
807 806
808 807 }
809 808
810 809 static int
811 810 pcfs_unmount(
812 811 struct vfs *vfsp,
813 812 int flag,
814 813 struct cred *cr)
815 814 {
816 815 struct pcfs *fsp, *fsp1;
817 816
818 817 if (secpolicy_fs_unmount(cr, vfsp) != 0)
819 818 return (EPERM);
820 819
821 820 fsp = VFSTOPCFS(vfsp);
822 821
823 822 /*
824 823 * We don't have to lock fsp because the VVFSLOCK in vfs layer will
825 824 * prevent lookuppn from crossing the mount point.
826 825 * If this is not a forced umount request and there's ongoing I/O,
827 826 * don't allow the mount to proceed.
828 827 */
829 828 if (flag & MS_FORCE)
830 829 vfsp->vfs_flag |= VFS_UNMOUNTED;
831 830 else if (fsp->pcfs_nrefs)
832 831 return (EBUSY);
833 832
834 833 mutex_enter(&pcfslock);
835 834
836 835 /*
837 836 * If this is a forced umount request or if the fs instance has
838 837 * been marked as beyond recovery, allow the umount to proceed
839 838 * regardless of state. pc_diskchanged() forcibly releases all
840 839 * inactive vnodes/pcnodes.
841 840 */
842 841 if (flag & MS_FORCE || fsp->pcfs_flags & PCFS_IRRECOV) {
843 842 rw_enter(&pcnodes_lock, RW_WRITER);
844 843 pc_diskchanged(fsp);
845 844 rw_exit(&pcnodes_lock);
846 845 }
847 846
848 847 /* now there should be no pcp node on pcfhead or pcdhead. */
849 848
850 849 if (fsp == pc_mounttab) {
851 850 pc_mounttab = fsp->pcfs_nxt;
852 851 } else {
853 852 for (fsp1 = pc_mounttab; fsp1 != NULL; fsp1 = fsp1->pcfs_nxt)
854 853 if (fsp1->pcfs_nxt == fsp)
855 854 fsp1->pcfs_nxt = fsp->pcfs_nxt;
856 855 }
857 856
858 857 mutex_exit(&pcfslock);
859 858
860 859 /*
861 860 * Since we support VFS_FREEVFS(), there's no need to
862 861 * free the fsp right now. The framework will tell us
863 862 * when the right time to do so has arrived by calling
864 863 * into pcfs_freevfs.
865 864 */
866 865 return (0);
867 866 }
868 867
869 868 /*
870 869 * find root of pcfs
871 870 */
872 871 static int
873 872 pcfs_root(
874 873 struct vfs *vfsp,
875 874 struct vnode **vpp)
876 875 {
877 876 struct pcfs *fsp;
878 877 struct pcnode *pcp;
879 878 int error;
880 879
881 880 fsp = VFSTOPCFS(vfsp);
882 881 if (error = pc_lockfs(fsp, 0, 0))
883 882 return (error);
884 883
885 884 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
886 885 pc_unlockfs(fsp);
887 886 *vpp = PCTOV(pcp);
888 887 pcp->pc_flags |= PC_EXTERNAL;
889 888 return (0);
890 889 }
891 890
892 891 /*
893 892 * Get file system statistics.
894 893 */
895 894 static int
896 895 pcfs_statvfs(
897 896 struct vfs *vfsp,
898 897 struct statvfs64 *sp)
899 898 {
900 899 struct pcfs *fsp;
901 900 int error;
902 901 dev32_t d32;
903 902
904 903 fsp = VFSTOPCFS(vfsp);
905 904 error = pc_getfat(fsp);
906 905 if (error)
907 906 return (error);
908 907 bzero(sp, sizeof (*sp));
909 908 sp->f_bsize = sp->f_frsize = fsp->pcfs_clsize;
910 909 sp->f_blocks = (fsblkcnt64_t)fsp->pcfs_ncluster;
911 910 sp->f_bavail = sp->f_bfree = (fsblkcnt64_t)pc_freeclusters(fsp);
912 911 sp->f_files = (fsfilcnt64_t)-1;
913 912 sp->f_ffree = (fsfilcnt64_t)-1;
914 913 sp->f_favail = (fsfilcnt64_t)-1;
915 914 #ifdef notdef
916 915 (void) cmpldev(&d32, fsp->pcfs_devvp->v_rdev);
917 916 #endif /* notdef */
918 917 (void) cmpldev(&d32, vfsp->vfs_dev);
919 918 sp->f_fsid = d32;
920 919 (void) strcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name);
921 920 sp->f_flag = vf_to_stf(vfsp->vfs_flag);
922 921 sp->f_namemax = PCMAXNAMLEN;
923 922 return (0);
924 923 }
925 924
926 925 static int
927 926 pc_syncfsnodes(struct pcfs *fsp)
928 927 {
929 928 struct pchead *hp;
930 929 struct pcnode *pcp;
931 930 int error;
932 931
933 932 if (error = pc_lockfs(fsp, 0, 0))
934 933 return (error);
935 934
936 935 if (!(error = pc_syncfat(fsp))) {
937 936 hp = pcfhead;
938 937 while (hp < & pcfhead [ NPCHASH ]) {
939 938 rw_enter(&pcnodes_lock, RW_READER);
940 939 pcp = hp->pch_forw;
941 940 while (pcp != (struct pcnode *)hp) {
942 941 if (VFSTOPCFS(PCTOV(pcp) -> v_vfsp) == fsp)
943 942 if (error = pc_nodesync(pcp))
944 943 break;
945 944 pcp = pcp -> pc_forw;
946 945 }
947 946 rw_exit(&pcnodes_lock);
948 947 if (error)
949 948 break;
950 949 hp++;
951 950 }
952 951 }
953 952 pc_unlockfs(fsp);
954 953 return (error);
955 954 }
956 955
957 956 /*
958 957 * Flush any pending I/O.
959 958 */
960 959 /*ARGSUSED*/
961 960 static int
962 961 pcfs_sync(
963 962 struct vfs *vfsp,
964 963 short flag,
965 964 struct cred *cr)
966 965 {
967 966 struct pcfs *fsp;
968 967 int error = 0;
969 968
970 969 /* this prevents the filesystem from being umounted. */
971 970 mutex_enter(&pcfslock);
972 971 if (vfsp != NULL) {
973 972 fsp = VFSTOPCFS(vfsp);
974 973 if (!(fsp->pcfs_flags & PCFS_IRRECOV)) {
975 974 error = pc_syncfsnodes(fsp);
976 975 } else {
977 976 rw_enter(&pcnodes_lock, RW_WRITER);
978 977 pc_diskchanged(fsp);
979 978 rw_exit(&pcnodes_lock);
980 979 error = EIO;
981 980 }
982 981 } else {
983 982 fsp = pc_mounttab;
984 983 while (fsp != NULL) {
985 984 if (fsp->pcfs_flags & PCFS_IRRECOV) {
986 985 rw_enter(&pcnodes_lock, RW_WRITER);
987 986 pc_diskchanged(fsp);
988 987 rw_exit(&pcnodes_lock);
989 988 error = EIO;
990 989 break;
991 990 }
992 991 error = pc_syncfsnodes(fsp);
993 992 if (error) break;
994 993 fsp = fsp->pcfs_nxt;
995 994 }
996 995 }
997 996 mutex_exit(&pcfslock);
998 997 return (error);
999 998 }
1000 999
1001 1000 int
1002 1001 pc_lockfs(struct pcfs *fsp, int diskchanged, int releasing)
1003 1002 {
1004 1003 int err;
1005 1004
1006 1005 if ((fsp->pcfs_flags & PCFS_IRRECOV) && !releasing)
1007 1006 return (EIO);
1008 1007
1009 1008 if ((fsp->pcfs_flags & PCFS_LOCKED) && (fsp->pcfs_owner == curthread)) {
1010 1009 fsp->pcfs_count++;
1011 1010 } else {
1012 1011 mutex_enter(&fsp->pcfs_lock);
1013 1012 if (fsp->pcfs_flags & PCFS_LOCKED)
1014 1013 panic("pc_lockfs");
1015 1014 /*
1016 1015 * We check the IRRECOV bit again just in case somebody
1017 1016 * snuck past the initial check but then got held up before
1018 1017 * they could grab the lock. (And in the meantime someone
1019 1018 * had grabbed the lock and set the bit)
1020 1019 */
1021 1020 if (!diskchanged && !(fsp->pcfs_flags & PCFS_IRRECOV)) {
1022 1021 if ((err = pc_getfat(fsp))) {
1023 1022 mutex_exit(&fsp->pcfs_lock);
1024 1023 return (err);
1025 1024 }
1026 1025 }
1027 1026 fsp->pcfs_flags |= PCFS_LOCKED;
1028 1027 fsp->pcfs_owner = curthread;
1029 1028 fsp->pcfs_count++;
1030 1029 }
1031 1030 return (0);
1032 1031 }
1033 1032
1034 1033 void
1035 1034 pc_unlockfs(struct pcfs *fsp)
1036 1035 {
1037 1036
1038 1037 if ((fsp->pcfs_flags & PCFS_LOCKED) == 0)
1039 1038 panic("pc_unlockfs");
1040 1039 if (--fsp->pcfs_count < 0)
1041 1040 panic("pc_unlockfs: count");
1042 1041 if (fsp->pcfs_count == 0) {
1043 1042 fsp->pcfs_flags &= ~PCFS_LOCKED;
1044 1043 fsp->pcfs_owner = 0;
1045 1044 mutex_exit(&fsp->pcfs_lock);
1046 1045 }
1047 1046 }
1048 1047
1049 1048 int
1050 1049 pc_syncfat(struct pcfs *fsp)
1051 1050 {
1052 1051 struct buf *bp;
1053 1052 int nfat;
1054 1053 int error = 0;
1055 1054 struct fat_od_fsi *fsinfo_disk;
1056 1055
1057 1056 if ((fsp->pcfs_fatp == (uchar_t *)0) ||
1058 1057 !(fsp->pcfs_flags & PCFS_FATMOD))
1059 1058 return (0);
1060 1059 /*
1061 1060 * write out all copies of FATs
1062 1061 */
1063 1062 fsp->pcfs_flags &= ~PCFS_FATMOD;
1064 1063 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
1065 1064 for (nfat = 0; nfat < fsp->pcfs_numfat; nfat++) {
1066 1065 error = pc_writefat(fsp, pc_dbdaddr(fsp,
1067 1066 fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec));
1068 1067 if (error) {
1069 1068 pc_mark_irrecov(fsp);
1070 1069 return (EIO);
1071 1070 }
1072 1071 }
1073 1072 pc_clear_fatchanges(fsp);
1074 1073
1075 1074 /*
1076 1075 * Write out fsinfo sector.
1077 1076 */
1078 1077 if (IS_FAT32(fsp)) {
1079 1078 bp = bread(fsp->pcfs_xdev,
1080 1079 pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
1081 1080 if (bp->b_flags & (B_ERROR | B_STALE)) {
1082 1081 error = geterror(bp);
1083 1082 }
1084 1083 fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
1085 1084 if (!error && FSISIG_OK(fsinfo_disk)) {
1086 1085 fsinfo_disk->fsi_incore.fs_free_clusters =
1087 1086 LE_32(fsp->pcfs_fsinfo.fs_free_clusters);
1088 1087 fsinfo_disk->fsi_incore.fs_next_free =
1089 1088 LE_32(FSINFO_UNKNOWN);
1090 1089 bwrite2(bp);
1091 1090 error = geterror(bp);
1092 1091 }
1093 1092 brelse(bp);
1094 1093 if (error) {
1095 1094 pc_mark_irrecov(fsp);
1096 1095 return (EIO);
1097 1096 }
1098 1097 }
1099 1098 return (0);
1100 1099 }
1101 1100
1102 1101 void
1103 1102 pc_invalfat(struct pcfs *fsp)
1104 1103 {
1105 1104 struct pcfs *xfsp;
1106 1105 int mount_cnt = 0;
1107 1106
1108 1107 if (fsp->pcfs_fatp == (uchar_t *)0)
1109 1108 panic("pc_invalfat");
1110 1109 /*
1111 1110 * Release FAT
1112 1111 */
1113 1112 kmem_free(fsp->pcfs_fatp, fsp->pcfs_fatsec * fsp->pcfs_secsize);
1114 1113 fsp->pcfs_fatp = NULL;
1115 1114 kmem_free(fsp->pcfs_fat_changemap, fsp->pcfs_fat_changemapsize);
1116 1115 fsp->pcfs_fat_changemap = NULL;
1117 1116 /*
1118 1117 * Invalidate all the blocks associated with the device.
1119 1118 * Not needed if stateless.
1120 1119 */
1121 1120 for (xfsp = pc_mounttab; xfsp; xfsp = xfsp->pcfs_nxt)
1122 1121 if (xfsp != fsp && xfsp->pcfs_xdev == fsp->pcfs_xdev)
1123 1122 mount_cnt++;
1124 1123
1125 1124 if (!mount_cnt)
1126 1125 binval(fsp->pcfs_xdev);
1127 1126 /*
1128 1127 * close mounted device
1129 1128 */
1130 1129 (void) VOP_CLOSE(fsp->pcfs_devvp,
1131 1130 (PCFSTOVFS(fsp)->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE,
1132 1131 1, (offset_t)0, CRED(), NULL);
1133 1132 }
1134 1133
1135 1134 void
1136 1135 pc_badfs(struct pcfs *fsp)
1137 1136 {
1138 1137 cmn_err(CE_WARN, "corrupted PC file system on dev (%x.%x):%d\n",
1139 1138 getmajor(fsp->pcfs_devvp->v_rdev),
1140 1139 getminor(fsp->pcfs_devvp->v_rdev), fsp->pcfs_ldrive);
1141 1140 }
1142 1141
1143 1142 /*
1144 1143 * The problem with supporting NFS on the PCFS filesystem is that there
1145 1144 * is no good place to keep the generation number. The only possible
1146 1145 * place is inside a directory entry. There are a few words that we
1147 1146 * don't use - they store NT & OS/2 attributes, and the creation/last access
1148 1147 * time of the file - but it seems wrong to use them. In addition, directory
1149 1148 * entries come and go. If a directory is removed completely, its directory
1150 1149 * blocks are freed and the generation numbers are lost. Whereas in ufs,
1151 1150 * inode blocks are dedicated for inodes, so the generation numbers are
1152 1151 * permanently kept on the disk.
1153 1152 */
1154 1153 static int
1155 1154 pcfs_vget(struct vfs *vfsp, struct vnode **vpp, struct fid *fidp)
1156 1155 {
1157 1156 struct pcnode *pcp;
1158 1157 struct pc_fid *pcfid;
1159 1158 struct pcfs *fsp;
1160 1159 struct pcdir *ep;
1161 1160 daddr_t eblkno;
1162 1161 int eoffset;
1163 1162 struct buf *bp;
1164 1163 int error;
1165 1164 pc_cluster32_t cn;
1166 1165
1167 1166 pcfid = (struct pc_fid *)fidp;
1168 1167 fsp = VFSTOPCFS(vfsp);
1169 1168
1170 1169 error = pc_lockfs(fsp, 0, 0);
1171 1170 if (error) {
1172 1171 *vpp = NULL;
1173 1172 return (error);
1174 1173 }
1175 1174
1176 1175 if (pcfid->pcfid_block == 0) {
1177 1176 pcp = pc_getnode(fsp, (daddr_t)0, 0, (struct pcdir *)0);
1178 1177 pcp->pc_flags |= PC_EXTERNAL;
1179 1178 *vpp = PCTOV(pcp);
1180 1179 pc_unlockfs(fsp);
1181 1180 return (0);
1182 1181 }
1183 1182 eblkno = pcfid->pcfid_block;
1184 1183 eoffset = pcfid->pcfid_offset;
1185 1184
1186 1185 if ((pc_dbtocl(fsp,
1187 1186 eblkno - fsp->pcfs_dosstart) >= fsp->pcfs_ncluster) ||
1188 1187 (eoffset > fsp->pcfs_clsize)) {
1189 1188 pc_unlockfs(fsp);
1190 1189 *vpp = NULL;
1191 1190 return (EINVAL);
1192 1191 }
1193 1192
1194 1193 if (eblkno >= fsp->pcfs_datastart || (eblkno - fsp->pcfs_rdirstart)
1195 1194 < (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) {
1196 1195 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1197 1196 fsp->pcfs_clsize);
1198 1197 } else {
1199 1198 /*
1200 1199 * This is an access "backwards" into the FAT12/FAT16
1201 1200 * root directory. A better code structure would
1202 1201 * significantly improve maintainability here ...
1203 1202 */
1204 1203 bp = bread(fsp->pcfs_xdev, pc_dbdaddr(fsp, eblkno),
1205 1204 (int)(fsp->pcfs_datastart - eblkno) * fsp->pcfs_secsize);
1206 1205 }
1207 1206 if (bp->b_flags & (B_ERROR | B_STALE)) {
1208 1207 error = geterror(bp);
1209 1208 brelse(bp);
1210 1209 if (error)
1211 1210 pc_mark_irrecov(fsp);
1212 1211 *vpp = NULL;
1213 1212 pc_unlockfs(fsp);
1214 1213 return (error);
1215 1214 }
1216 1215 ep = (struct pcdir *)(bp->b_un.b_addr + eoffset);
1217 1216 /*
1218 1217 * Ok, if this is a valid file handle that we gave out,
1219 1218 * then simply ensuring that the creation time matches,
1220 1219 * the entry has not been deleted, and it has a valid first
1221 1220 * character should be enough.
1222 1221 *
1223 1222 * Unfortunately, verifying that the <blkno, offset> _still_
1224 1223 * refers to a directory entry is not easy, since we'd have
1225 1224 * to search _all_ directories starting from root to find it.
1226 1225 * That's a high price to pay just in case somebody is forging
1227 1226 * file handles. So instead we verify that as much of the
1228 1227 * entry is valid as we can:
1229 1228 *
1230 1229 * 1. The starting cluster is 0 (unallocated) or valid
1231 1230 * 2. It is not an LFN entry
1232 1231 * 3. It is not hidden (unless mounted as such)
1233 1232 * 4. It is not the label
1234 1233 */
1235 1234 cn = pc_getstartcluster(fsp, ep);
1236 1235 /*
1237 1236 * if the starting cluster is valid, but not valid according
1238 1237 * to pc_validcl(), force it to be to simplify the following if.
1239 1238 */
1240 1239 if (cn == 0)
1241 1240 cn = PCF_FIRSTCLUSTER;
1242 1241 if (IS_FAT32(fsp)) {
1243 1242 if (cn >= PCF_LASTCLUSTER32)
1244 1243 cn = PCF_FIRSTCLUSTER;
1245 1244 } else {
1246 1245 if (cn >= PCF_LASTCLUSTER)
1247 1246 cn = PCF_FIRSTCLUSTER;
1248 1247 }
1249 1248 if ((!pc_validcl(fsp, cn)) ||
1250 1249 (PCDL_IS_LFN(ep)) ||
1251 1250 (PCA_IS_HIDDEN(fsp, ep->pcd_attr)) ||
1252 1251 ((ep->pcd_attr & PCA_LABEL) == PCA_LABEL)) {
1253 1252 bp->b_flags |= B_STALE | B_AGE;
1254 1253 brelse(bp);
1255 1254 pc_unlockfs(fsp);
1256 1255 return (EINVAL);
1257 1256 }
1258 1257 if ((ep->pcd_crtime.pct_time == pcfid->pcfid_ctime) &&
1259 1258 (ep->pcd_filename[0] != PCD_ERASED) &&
1260 1259 (pc_validchar(ep->pcd_filename[0]) ||
1261 1260 (ep->pcd_filename[0] == '.' && ep->pcd_filename[1] == '.'))) {
1262 1261 pcp = pc_getnode(fsp, eblkno, eoffset, ep);
1263 1262 pcp->pc_flags |= PC_EXTERNAL;
1264 1263 *vpp = PCTOV(pcp);
1265 1264 } else {
1266 1265 *vpp = NULL;
1267 1266 }
1268 1267 bp->b_flags |= B_STALE | B_AGE;
1269 1268 brelse(bp);
1270 1269 pc_unlockfs(fsp);
1271 1270 return (0);
1272 1271 }
1273 1272
1274 1273 /*
1275 1274 * Unfortunately, FAT32 fat's can be pretty big (On a 1 gig jaz drive, about
1276 1275 * a meg), so we can't bread() it all in at once. This routine reads a
1277 1276 * fat a chunk at a time.
1278 1277 */
1279 1278 static int
1280 1279 pc_readfat(struct pcfs *fsp, uchar_t *fatp)
1281 1280 {
1282 1281 struct buf *bp;
1283 1282 size_t off;
1284 1283 size_t readsize;
1285 1284 daddr_t diskblk;
1286 1285 size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1287 1286 daddr_t start = fsp->pcfs_fatstart;
1288 1287
1289 1288 readsize = fsp->pcfs_clsize;
1290 1289 for (off = 0; off < fatsize; off += readsize, fatp += readsize) {
1291 1290 if (readsize > (fatsize - off))
1292 1291 readsize = fatsize - off;
1293 1292 diskblk = pc_dbdaddr(fsp, start +
1294 1293 pc_cltodb(fsp, pc_lblkno(fsp, off)));
1295 1294 bp = bread(fsp->pcfs_xdev, diskblk, readsize);
1296 1295 if (bp->b_flags & (B_ERROR | B_STALE)) {
1297 1296 brelse(bp);
1298 1297 return (EIO);
1299 1298 }
1300 1299 bp->b_flags |= B_STALE | B_AGE;
1301 1300 bcopy(bp->b_un.b_addr, fatp, readsize);
1302 1301 brelse(bp);
1303 1302 }
1304 1303 return (0);
1305 1304 }
1306 1305
1307 1306 /*
1308 1307 * We write the FAT out a _lot_, in order to make sure that it
1309 1308 * is up-to-date. But on a FAT32 system (large drive, small clusters)
1310 1309 * the FAT might be a couple of megabytes, and writing it all out just
1311 1310 * because we created or deleted a small file is painful (especially
1312 1311 * since we do it for each alternate FAT too). So instead, for FAT16 and
1313 1312 * FAT32 we only write out the bit that has changed. We don't clear
1314 1313 * the 'updated' fields here because the caller might be writing out
1315 1314 * several FATs, so the caller must use pc_clear_fatchanges() after
1316 1315 * all FATs have been updated.
1317 1316 * This function doesn't take "start" from fsp->pcfs_dosstart because
1318 1317 * callers can use it to write either the primary or any of the alternate
1319 1318 * FAT tables.
1320 1319 */
1321 1320 static int
1322 1321 pc_writefat(struct pcfs *fsp, daddr_t start)
1323 1322 {
1324 1323 struct buf *bp;
1325 1324 size_t off;
1326 1325 size_t writesize;
1327 1326 int error;
1328 1327 uchar_t *fatp = fsp->pcfs_fatp;
1329 1328 size_t fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
1330 1329
1331 1330 writesize = fsp->pcfs_clsize;
1332 1331 for (off = 0; off < fatsize; off += writesize, fatp += writesize) {
1333 1332 if (writesize > (fatsize - off))
1334 1333 writesize = fatsize - off;
1335 1334 if (!pc_fat_is_changed(fsp, pc_lblkno(fsp, off))) {
1336 1335 continue;
1337 1336 }
1338 1337 bp = ngeteblk(writesize);
1339 1338 bp->b_edev = fsp->pcfs_xdev;
1340 1339 bp->b_dev = cmpdev(bp->b_edev);
1341 1340 bp->b_blkno = pc_dbdaddr(fsp, start +
1342 1341 pc_cltodb(fsp, pc_lblkno(fsp, off)));
1343 1342 bcopy(fatp, bp->b_un.b_addr, writesize);
1344 1343 bwrite2(bp);
1345 1344 error = geterror(bp);
1346 1345 brelse(bp);
1347 1346 if (error) {
1348 1347 return (error);
1349 1348 }
1350 1349 }
1351 1350 return (0);
1352 1351 }
1353 1352
1354 1353 /*
1355 1354 * Mark the FAT cluster that 'cn' is stored in as modified.
1356 1355 */
1357 1356 void
1358 1357 pc_mark_fat_updated(struct pcfs *fsp, pc_cluster32_t cn)
1359 1358 {
1360 1359 pc_cluster32_t bn;
1361 1360 size_t size;
1362 1361
1363 1362 /* which fat block is the cluster number stored in? */
1364 1363 if (IS_FAT32(fsp)) {
1365 1364 size = sizeof (pc_cluster32_t);
1366 1365 bn = pc_lblkno(fsp, cn * size);
1367 1366 fsp->pcfs_fat_changemap[bn] = 1;
1368 1367 } else if (IS_FAT16(fsp)) {
1369 1368 size = sizeof (pc_cluster16_t);
1370 1369 bn = pc_lblkno(fsp, cn * size);
1371 1370 fsp->pcfs_fat_changemap[bn] = 1;
1372 1371 } else {
1373 1372 offset_t off;
1374 1373 pc_cluster32_t nbn;
1375 1374
1376 1375 ASSERT(IS_FAT12(fsp));
1377 1376 off = cn + (cn >> 1);
1378 1377 bn = pc_lblkno(fsp, off);
1379 1378 fsp->pcfs_fat_changemap[bn] = 1;
1380 1379 /* does this field wrap into the next fat cluster? */
1381 1380 nbn = pc_lblkno(fsp, off + 1);
1382 1381 if (nbn != bn) {
1383 1382 fsp->pcfs_fat_changemap[nbn] = 1;
1384 1383 }
1385 1384 }
1386 1385 }
1387 1386
1388 1387 /*
1389 1388 * return whether the FAT cluster 'bn' is updated and needs to
1390 1389 * be written out.
1391 1390 */
1392 1391 int
1393 1392 pc_fat_is_changed(struct pcfs *fsp, pc_cluster32_t bn)
1394 1393 {
1395 1394 return (fsp->pcfs_fat_changemap[bn] == 1);
1396 1395 }
1397 1396
1398 1397 /*
1399 1398 * Implementation of VFS_FREEVFS() to support forced umounts.
1400 1399 * This is called by the vfs framework after umount, to trigger
1401 1400 * the release of any resources still associated with the given
1402 1401 * vfs_t once the need to keep them has gone away.
1403 1402 */
1404 1403 void
1405 1404 pcfs_freevfs(vfs_t *vfsp)
1406 1405 {
1407 1406 struct pcfs *fsp = VFSTOPCFS(vfsp);
1408 1407
1409 1408 mutex_enter(&pcfslock);
1410 1409 /*
1411 1410 * Purging the FAT closes the device - can't do any more
1412 1411 * I/O after this.
1413 1412 */
1414 1413 if (fsp->pcfs_fatp != (uchar_t *)0)
1415 1414 pc_invalfat(fsp);
1416 1415 mutex_exit(&pcfslock);
1417 1416
1418 1417 VN_RELE(fsp->pcfs_devvp);
1419 1418 mutex_destroy(&fsp->pcfs_lock);
1420 1419 kmem_free(fsp, sizeof (*fsp));
1421 1420
1422 1421 /*
1423 1422 * Allow _fini() to succeed now, if so desired.
1424 1423 */
1425 1424 atomic_dec_32(&pcfs_mountcount);
1426 1425 }
1427 1426
1428 1427
1429 1428 /*
1430 1429 * PC-style partition parsing and FAT BPB identification/validation code.
1431 1430 * The partition parsers here assume:
1432 1431 * - a FAT filesystem will be in a partition that has one of a set of
1433 1432 * recognized partition IDs
1434 1433 * - the user wants the 'numbering' (C:, D:, ...) that one would get
1435 1434 * on MSDOS 6.x.
1436 1435 * That means any non-FAT partition type (NTFS, HPFS, or any Linux fs)
1437 1436 * will not factor in the enumeration.
1438 1437 * These days, such assumptions should be revisited. FAT is no longer the
1439 1438 * only game in 'PC town'.
1440 1439 */
1441 1440 /*
1442 1441 * isDosDrive()
1443 1442 * Boolean function. Give it the systid field for an fdisk partition
1444 1443 * and it decides if that's a systid that describes a DOS drive. We
1445 1444 * use systid values defined in sys/dktp/fdisk.h.
1446 1445 */
1447 1446 static int
1448 1447 isDosDrive(uchar_t checkMe)
1449 1448 {
1450 1449 return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
1451 1450 (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
1452 1451 (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
1453 1452 (checkMe == DIAGPART));
1454 1453 }
1455 1454
1456 1455
1457 1456 /*
1458 1457 * isDosExtended()
1459 1458 * Boolean function. Give it the systid field for an fdisk partition
1460 1459 * and it decides if that's a systid that describes an extended DOS
1461 1460 * partition.
1462 1461 */
1463 1462 static int
1464 1463 isDosExtended(uchar_t checkMe)
1465 1464 {
1466 1465 return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
1467 1466 }
1468 1467
1469 1468
1470 1469 /*
1471 1470 * isBootPart()
1472 1471 * Boolean function. Give it the systid field for an fdisk partition
1473 1472 * and it decides if that's a systid that describes a Solaris boot
1474 1473 * partition.
1475 1474 */
1476 1475 static int
1477 1476 isBootPart(uchar_t checkMe)
1478 1477 {
1479 1478 return (checkMe == X86BOOT);
1480 1479 }
1481 1480
1482 1481
1483 1482 /*
1484 1483 * noLogicalDrive()
1485 1484 * Display error message about not being able to find a logical
1486 1485 * drive.
1487 1486 */
1488 1487 static void
1489 1488 noLogicalDrive(int ldrive)
1490 1489 {
1491 1490 if (ldrive == BOOT_PARTITION_DRIVE) {
1492 1491 cmn_err(CE_NOTE, "!pcfs: no boot partition");
1493 1492 } else {
1494 1493 cmn_err(CE_NOTE, "!pcfs: %d: no such logical drive", ldrive);
1495 1494 }
1496 1495 }
1497 1496
1498 1497
1499 1498 /*
1500 1499 * findTheDrive()
1501 1500 * Discover offset of the requested logical drive, and return
1502 1501 * that offset (startSector), the systid of that drive (sysid),
1503 1502 * and a buffer pointer (bp), with the buffer contents being
1504 1503 * the first sector of the logical drive (i.e., the sector that
1505 1504 * contains the BPB for that drive).
1506 1505 *
1507 1506 * Note: this code is not capable of addressing >2TB disks, as it uses
1508 1507 * daddr_t not diskaddr_t, some of the calculations would overflow
1509 1508 */
1510 1509 #define COPY_PTBL(mbr, ptblp) \
1511 1510 bcopy(&(((struct mboot *)(mbr))->parts), (ptblp), \
1512 1511 FD_NUMPART * sizeof (struct ipart))
1513 1512
1514 1513 static int
1515 1514 findTheDrive(struct pcfs *fsp, buf_t **bp)
1516 1515 {
1517 1516 int ldrive = fsp->pcfs_ldrive;
1518 1517 dev_t dev = fsp->pcfs_devvp->v_rdev;
1519 1518
1520 1519 struct ipart dosp[FD_NUMPART]; /* incore fdisk partition structure */
1521 1520 daddr_t lastseek = 0; /* Disk block we sought previously */
1522 1521 daddr_t diskblk = 0; /* Disk block to get */
1523 1522 daddr_t xstartsect; /* base of Extended DOS partition */
1524 1523 int logicalDriveCount = 0; /* Count of logical drives seen */
1525 1524 int extendedPart = -1; /* index of extended dos partition */
1526 1525 int primaryPart = -1; /* index of primary dos partition */
1527 1526 int bootPart = -1; /* index of a Solaris boot partition */
1528 1527 uint32_t xnumsect = 0; /* length of extended DOS partition */
1529 1528 int driveIndex; /* computed FDISK table index */
1530 1529 daddr_t startsec;
1531 1530 len_t mediasize;
1532 1531 int i;
1533 1532 /*
1534 1533 * Count of drives in the current extended partition's
1535 1534 * FDISK table, and indexes of the drives themselves.
1536 1535 */
1537 1536 int extndDrives[FD_NUMPART];
1538 1537 int numDrives = 0;
1539 1538
1540 1539 /*
1541 1540 * Count of drives (beyond primary) in master boot record's
1542 1541 * FDISK table, and indexes of the drives themselves.
1543 1542 */
1544 1543 int extraDrives[FD_NUMPART];
1545 1544 int numExtraDrives = 0;
1546 1545
1547 1546 /*
1548 1547 * "ldrive == 0" should never happen, as this is a request to
1549 1548 * mount the physical device (and ignore partitioning). The code
1550 1549 * in pcfs_mount() should have made sure that a logical drive number
1551 1550 * is at least 1, meaning we're looking for drive "C:". It is not
1552 1551 * safe (and a bug in the callers of this function) to request logical
1553 1552 * drive number 0; we could ASSERT() but a graceful EIO is a more
1554 1553 * polite way.
1555 1554 */
1556 1555 if (ldrive == 0) {
1557 1556 cmn_err(CE_NOTE, "!pcfs: request for logical partition zero");
1558 1557 noLogicalDrive(ldrive);
1559 1558 return (EIO);
1560 1559 }
1561 1560
1562 1561 /*
1563 1562 * Copy from disk block into memory aligned structure for fdisk usage.
1564 1563 */
1565 1564 COPY_PTBL((*bp)->b_un.b_addr, dosp);
1566 1565
1567 1566 /*
1568 1567 * This check is ok because a FAT BPB and a master boot record (MBB)
1569 1568 * have the same signature, in the same position within the block.
1570 1569 */
1571 1570 if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1572 1571 cmn_err(CE_NOTE, "!pcfs: MBR partition table signature err, "
1573 1572 "device (%x.%x):%d\n",
1574 1573 getmajor(dev), getminor(dev), ldrive);
1575 1574 return (EINVAL);
1576 1575 }
1577 1576
1578 1577 /*
1579 1578 * Get a summary of what is in the Master FDISK table.
1580 1579 * Normally we expect to find one partition marked as a DOS drive.
1581 1580 * This partition is the one Windows calls the primary dos partition.
1582 1581 * If the machine has any logical drives then we also expect
1583 1582 * to find a partition marked as an extended DOS partition.
1584 1583 *
1585 1584 * Sometimes we'll find multiple partitions marked as DOS drives.
1586 1585 * The Solaris fdisk program allows these partitions
1587 1586 * to be created, but Windows fdisk no longer does. We still need
1588 1587 * to support these, though, since Windows does. We also need to fix
1589 1588 * our fdisk to behave like the Windows version.
1590 1589 *
1591 1590 * It turns out that some off-the-shelf media have *only* an
1592 1591 * Extended partition, so we need to deal with that case as well.
1593 1592 *
1594 1593 * Only a single (the first) Extended or Boot Partition will
1595 1594 * be recognized. Any others will be ignored.
1596 1595 */
1597 1596 for (i = 0; i < FD_NUMPART; i++) {
1598 1597 DTRACE_PROBE4(primarypart, struct pcfs *, fsp,
1599 1598 uint_t, (uint_t)dosp[i].systid,
1600 1599 uint_t, LE_32(dosp[i].relsect),
1601 1600 uint_t, LE_32(dosp[i].numsect));
1602 1601
1603 1602 if (isDosDrive(dosp[i].systid)) {
1604 1603 if (primaryPart < 0) {
1605 1604 logicalDriveCount++;
1606 1605 primaryPart = i;
1607 1606 } else {
1608 1607 extraDrives[numExtraDrives++] = i;
1609 1608 }
1610 1609 continue;
1611 1610 }
1612 1611 if ((extendedPart < 0) && isDosExtended(dosp[i].systid)) {
1613 1612 extendedPart = i;
1614 1613 continue;
1615 1614 }
1616 1615 if ((bootPart < 0) && isBootPart(dosp[i].systid)) {
1617 1616 bootPart = i;
1618 1617 continue;
1619 1618 }
1620 1619 }
1621 1620
1622 1621 if (ldrive == BOOT_PARTITION_DRIVE) {
1623 1622 if (bootPart < 0) {
1624 1623 noLogicalDrive(ldrive);
1625 1624 return (EINVAL);
1626 1625 }
1627 1626 startsec = LE_32(dosp[bootPart].relsect);
1628 1627 mediasize = LE_32(dosp[bootPart].numsect);
1629 1628 goto found;
1630 1629 }
1631 1630
1632 1631 if (ldrive == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
1633 1632 startsec = LE_32(dosp[primaryPart].relsect);
1634 1633 mediasize = LE_32(dosp[primaryPart].numsect);
1635 1634 goto found;
1636 1635 }
1637 1636
1638 1637 /*
1639 1638 * We are not looking for the C: drive (or the primary drive
1640 1639 * was not found), so we had better have an extended partition
1641 1640 * or extra drives in the Master FDISK table.
1642 1641 */
1643 1642 if ((extendedPart < 0) && (numExtraDrives == 0)) {
1644 1643 cmn_err(CE_NOTE, "!pcfs: no extended dos partition");
1645 1644 noLogicalDrive(ldrive);
1646 1645 return (EINVAL);
1647 1646 }
1648 1647
1649 1648 if (extendedPart >= 0) {
1650 1649 diskblk = xstartsect = LE_32(dosp[extendedPart].relsect);
1651 1650 xnumsect = LE_32(dosp[extendedPart].numsect);
1652 1651 do {
1653 1652 /*
1654 1653 * If the seek would not cause us to change
1655 1654 * position on the drive, then we're out of
1656 1655 * extended partitions to examine.
1657 1656 */
1658 1657 if (diskblk == lastseek)
1659 1658 break;
1660 1659 logicalDriveCount += numDrives;
1661 1660 /*
1662 1661 * Seek the next extended partition, and find
1663 1662 * logical drives within it.
1664 1663 */
1665 1664 brelse(*bp);
1666 1665 /*
1667 1666 * bread() block numbers are multiples of DEV_BSIZE
1668 1667 * but the device sector size (the unit of partitioning)
1669 1668 * might be larger than that; pcfs_get_device_info()
1670 1669 * has calculated the multiplicator for us.
1671 1670 */
1672 1671 *bp = bread(dev,
1673 1672 pc_dbdaddr(fsp, diskblk), fsp->pcfs_secsize);
1674 1673 if ((*bp)->b_flags & B_ERROR) {
1675 1674 return (EIO);
1676 1675 }
1677 1676
1678 1677 lastseek = diskblk;
1679 1678 COPY_PTBL((*bp)->b_un.b_addr, dosp);
1680 1679 if (bpb_get_BPBSig((*bp)->b_un.b_addr) != MBB_MAGIC) {
1681 1680 cmn_err(CE_NOTE, "!pcfs: "
1682 1681 "extended partition table signature err, "
1683 1682 "device (%x.%x):%d, LBA %u",
1684 1683 getmajor(dev), getminor(dev), ldrive,
1685 1684 (uint_t)pc_dbdaddr(fsp, diskblk));
1686 1685 return (EINVAL);
1687 1686 }
1688 1687 /*
1689 1688 * Count up drives, and track where the next
1690 1689 * extended partition is in case we need it. We
1691 1690 * are expecting only one extended partition. If
1692 1691 * there is more than one we'll only go to the
1693 1692 * first one we see, but warn about ignoring.
1694 1693 */
1695 1694 numDrives = 0;
1696 1695 for (i = 0; i < FD_NUMPART; i++) {
1697 1696 DTRACE_PROBE4(extendedpart,
1698 1697 struct pcfs *, fsp,
1699 1698 uint_t, (uint_t)dosp[i].systid,
1700 1699 uint_t, LE_32(dosp[i].relsect),
1701 1700 uint_t, LE_32(dosp[i].numsect));
1702 1701 if (isDosDrive(dosp[i].systid)) {
1703 1702 extndDrives[numDrives++] = i;
1704 1703 } else if (isDosExtended(dosp[i].systid)) {
1705 1704 if (diskblk != lastseek) {
1706 1705 /*
1707 1706 * Already found an extended
1708 1707 * partition in this table.
1709 1708 */
1710 1709 cmn_err(CE_NOTE,
1711 1710 "!pcfs: ignoring unexpected"
1712 1711 " additional extended"
1713 1712 " partition");
1714 1713 } else {
1715 1714 diskblk = xstartsect +
1716 1715 LE_32(dosp[i].relsect);
1717 1716 }
1718 1717 }
1719 1718 }
1720 1719 } while (ldrive > logicalDriveCount + numDrives);
1721 1720
1722 1721 ASSERT(numDrives <= FD_NUMPART);
1723 1722
1724 1723 if (ldrive <= logicalDriveCount + numDrives) {
1725 1724 /*
1726 1725 * The number of logical drives we've found thus
1727 1726 * far is enough to get us to the one we were
1728 1727 * searching for.
1729 1728 */
1730 1729 driveIndex = logicalDriveCount + numDrives - ldrive;
1731 1730 mediasize =
1732 1731 LE_32(dosp[extndDrives[driveIndex]].numsect);
1733 1732 startsec =
1734 1733 LE_32(dosp[extndDrives[driveIndex]].relsect) +
1735 1734 lastseek;
1736 1735 if (startsec > (xstartsect + xnumsect)) {
1737 1736 cmn_err(CE_NOTE, "!pcfs: extended partition "
1738 1737 "values bad");
1739 1738 return (EINVAL);
1740 1739 }
1741 1740 goto found;
1742 1741 } else {
1743 1742 /*
1744 1743 * We ran out of extended dos partition
1745 1744 * drives. The only hope now is to go
1746 1745 * back to extra drives defined in the master
1747 1746 * fdisk table. But we overwrote that table
1748 1747 * already, so we must load it in again.
1749 1748 */
1750 1749 logicalDriveCount += numDrives;
1751 1750 brelse(*bp);
1752 1751 ASSERT(fsp->pcfs_dosstart == 0);
1753 1752 *bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
1754 1753 fsp->pcfs_secsize);
1755 1754 if ((*bp)->b_flags & B_ERROR) {
1756 1755 return (EIO);
1757 1756 }
1758 1757 COPY_PTBL((*bp)->b_un.b_addr, dosp);
1759 1758 }
1760 1759 }
1761 1760 /*
1762 1761 * Still haven't found the drive, is it an extra
1763 1762 * drive defined in the main FDISK table?
1764 1763 */
1765 1764 if (ldrive <= logicalDriveCount + numExtraDrives) {
1766 1765 driveIndex = logicalDriveCount + numExtraDrives - ldrive;
1767 1766 ASSERT(driveIndex < MIN(numExtraDrives, FD_NUMPART));
1768 1767 mediasize = LE_32(dosp[extraDrives[driveIndex]].numsect);
1769 1768 startsec = LE_32(dosp[extraDrives[driveIndex]].relsect);
1770 1769 goto found;
1771 1770 }
1772 1771 /*
1773 1772 * Still haven't found the drive, and there is
1774 1773 * nowhere else to look.
1775 1774 */
1776 1775 noLogicalDrive(ldrive);
1777 1776 return (EINVAL);
1778 1777
1779 1778 found:
1780 1779 /*
1781 1780 * We need this value in units of sectorsize, because PCFS' internal
1782 1781 * offset calculations go haywire for > 512Byte sectors unless all
1783 1782 * pcfs_.*start values are in units of sectors.
1784 1783 * So, assign before the capacity check (that's done in DEV_BSIZE)
1785 1784 */
1786 1785 fsp->pcfs_dosstart = startsec;
1787 1786
1788 1787 /*
1789 1788 * convert from device sectors to proper units:
1790 1789 * - starting sector: DEV_BSIZE (as argument to bread())
1791 1790 * - media size: Bytes
1792 1791 */
1793 1792 startsec = pc_dbdaddr(fsp, startsec);
1794 1793 mediasize *= fsp->pcfs_secsize;
1795 1794
1796 1795 /*
1797 1796 * some additional validation / warnings in case the partition table
1798 1797 * and the actual media capacity are not in accordance ...
1799 1798 */
1800 1799 if (fsp->pcfs_mediasize != 0) {
1801 1800 diskaddr_t startoff =
1802 1801 (diskaddr_t)startsec * (diskaddr_t)DEV_BSIZE;
1803 1802
1804 1803 if (startoff >= fsp->pcfs_mediasize ||
1805 1804 startoff + mediasize > fsp->pcfs_mediasize) {
1806 1805 cmn_err(CE_WARN,
1807 1806 "!pcfs: partition size (LBA start %u, %lld bytes, "
1808 1807 "device (%x.%x):%d) smaller than "
1809 1808 "mediasize (%lld bytes).\n"
1810 1809 "filesystem may be truncated, access errors "
1811 1810 "may result.\n",
1812 1811 (uint_t)startsec, (long long)mediasize,
1813 1812 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1814 1813 fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1815 1814 }
1816 1815 } else {
1817 1816 fsp->pcfs_mediasize = mediasize;
1818 1817 }
1819 1818
1820 1819 return (0);
1821 1820 }
1822 1821
1823 1822
1824 1823 static fattype_t
1825 1824 secondaryBPBChecks(struct pcfs *fsp, uchar_t *bpb, size_t secsize)
1826 1825 {
1827 1826 uint32_t ncl = fsp->pcfs_ncluster;
1828 1827
1829 1828 if (ncl <= 4096) {
1830 1829 if (bpb_get_FatSz16(bpb) == 0)
1831 1830 return (FAT_UNKNOWN);
1832 1831
1833 1832 if (bpb_get_FatSz16(bpb) * secsize < ncl * 2 &&
1834 1833 bpb_get_FatSz16(bpb) * secsize >= (3 * ncl / 2))
1835 1834 return (FAT12);
1836 1835 if (bcmp(bpb_FilSysType16(bpb), "FAT12", 5) == 0)
1837 1836 return (FAT12);
1838 1837 if (bcmp(bpb_FilSysType16(bpb), "FAT16", 5) == 0)
1839 1838 return (FAT16);
1840 1839
1841 1840 switch (bpb_get_Media(bpb)) {
1842 1841 case SS8SPT:
1843 1842 case DS8SPT:
1844 1843 case SS9SPT:
1845 1844 case DS9SPT:
1846 1845 case DS18SPT:
1847 1846 case DS9_15SPT:
1848 1847 /*
1849 1848 * Is this reliable - all floppies are FAT12 ?
1850 1849 */
1851 1850 return (FAT12);
1852 1851 case MD_FIXED:
1853 1852 /*
1854 1853 * Is this reliable - disks are always FAT16 ?
1855 1854 */
1856 1855 return (FAT16);
1857 1856 default:
1858 1857 break;
1859 1858 }
1860 1859 } else if (ncl <= 65536) {
1861 1860 if (bpb_get_FatSz16(bpb) == 0 && bpb_get_FatSz32(bpb) > 0)
1862 1861 return (FAT32);
1863 1862 if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
1864 1863 return (FAT32);
1865 1864 if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
1866 1865 return (FAT32);
1867 1866
1868 1867 if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
1869 1868 return (FAT16);
1870 1869 if (bpb_get_FatSz16(bpb) * secsize < ncl * 4)
1871 1870 return (FAT16);
1872 1871 }
1873 1872
1874 1873 /*
1875 1874 * We don't know
1876 1875 */
1877 1876 return (FAT_UNKNOWN);
1878 1877 }
1879 1878
1880 1879 /*
1881 1880 * Check to see if the BPB we found is correct.
1882 1881 *
1883 1882 * This looks far more complicated that it needs to be for pure structural
1884 1883 * validation. The reason for this is that parseBPB() is also used for
1885 1884 * debugging purposes (mdb dcmd) and we therefore want a bitmap of which
1886 1885 * BPB fields (do not) have 'known good' values, even if we (do not) reject
1887 1886 * the BPB when attempting to mount the filesystem.
1888 1887 *
1889 1888 * Real-world usage of FAT shows there are a lot of corner-case situations
1890 1889 * and, following the specification strictly, invalid filesystems out there.
1891 1890 * Known are situations such as:
1892 1891 * - FAT12/FAT16 filesystems with garbage in either totsec16/32
1893 1892 * instead of the zero in one of the fields mandated by the spec
1894 1893 * - filesystems that claim to be larger than the partition they're in
1895 1894 * - filesystems without valid media descriptor
1896 1895 * - FAT32 filesystems with RootEntCnt != 0
1897 1896 * - FAT32 filesystems with less than 65526 clusters
1898 1897 * - FAT32 filesystems without valid FSI sector
1899 1898 * - FAT32 filesystems with FAT size in fatsec16 instead of fatsec32
1900 1899 *
1901 1900 * Such filesystems are accessible by PCFS - if it'd know to start with that
1902 1901 * the filesystem should be treated as a specific FAT type. Before S10, it
1903 1902 * relied on the PC/fdisk partition type for the purpose and almost completely
1904 1903 * ignored the BPB; now it ignores the partition type for anything else but
1905 1904 * logical drive enumeration, which can result in rejection of (invalid)
1906 1905 * FAT32 - if the partition ID says FAT32, but the filesystem, for example
1907 1906 * has less than 65526 clusters.
1908 1907 *
1909 1908 * Without a "force this fs as FAT{12,16,32}" tunable or mount option, it's
1910 1909 * not possible to allow all such mostly-compliant filesystems in unless one
1911 1910 * accepts false positives (definitely invalid filesystems that cause problems
1912 1911 * later). This at least allows to pinpoint why the mount failed.
1913 1912 *
1914 1913 * Due to the use of FAT on removeable media, all relaxations of the rules
1915 1914 * here need to be carefully evaluated wrt. to potential effects on PCFS
1916 1915 * resilience. A faulty/"mis-crafted" filesystem must not cause a panic, so
1917 1916 * beware.
1918 1917 */
1919 1918 static int
1920 1919 parseBPB(struct pcfs *fsp, uchar_t *bpb, int *valid)
1921 1920 {
1922 1921 fattype_t type;
1923 1922
1924 1923 uint32_t ncl; /* number of clusters in file area */
1925 1924 uint32_t rec;
1926 1925 uint32_t reserved;
1927 1926 uint32_t fsisec, bkbootsec;
1928 1927 blkcnt_t totsec, totsec16, totsec32, datasec;
1929 1928 size_t fatsec, fatsec16, fatsec32, rdirsec;
1930 1929 size_t secsize;
1931 1930 len_t mediasize;
1932 1931 uint64_t validflags = 0;
1933 1932
1934 1933 if (VALID_BPBSIG(bpb_get_BPBSig(bpb)))
1935 1934 validflags |= BPB_BPBSIG_OK;
1936 1935
1937 1936 rec = bpb_get_RootEntCnt(bpb);
1938 1937 reserved = bpb_get_RsvdSecCnt(bpb);
1939 1938 fsisec = bpb_get_FSInfo32(bpb);
1940 1939 bkbootsec = bpb_get_BkBootSec32(bpb);
1941 1940 totsec16 = (blkcnt_t)bpb_get_TotSec16(bpb);
1942 1941 totsec32 = (blkcnt_t)bpb_get_TotSec32(bpb);
1943 1942 fatsec16 = bpb_get_FatSz16(bpb);
1944 1943 fatsec32 = bpb_get_FatSz32(bpb);
1945 1944
1946 1945 totsec = totsec16 ? totsec16 : totsec32;
1947 1946 fatsec = fatsec16 ? fatsec16 : fatsec32;
1948 1947
1949 1948 secsize = bpb_get_BytesPerSec(bpb);
1950 1949 if (!VALID_SECSIZE(secsize))
1951 1950 secsize = fsp->pcfs_secsize;
1952 1951 if (secsize != fsp->pcfs_secsize) {
1953 1952 PC_DPRINTF3(3, "!pcfs: parseBPB, device (%x.%x):%d:\n",
1954 1953 getmajor(fsp->pcfs_xdev),
1955 1954 getminor(fsp->pcfs_xdev), fsp->pcfs_ldrive);
1956 1955 PC_DPRINTF2(3, "!BPB secsize %d != "
1957 1956 "autodetected media block size %d\n",
1958 1957 (int)secsize, (int)fsp->pcfs_secsize);
1959 1958 if (fsp->pcfs_ldrive) {
1960 1959 /*
1961 1960 * We've already attempted to parse the partition
1962 1961 * table. If the block size used for that don't match
1963 1962 * the PCFS sector size, we're hosed one way or the
1964 1963 * other. Just try what happens.
1965 1964 */
1966 1965 secsize = fsp->pcfs_secsize;
1967 1966 PC_DPRINTF1(3,
1968 1967 "!pcfs: Using autodetected secsize %d\n",
1969 1968 (int)secsize);
1970 1969 } else {
1971 1970 /*
1972 1971 * This allows mounting lofi images of PCFS partitions
1973 1972 * with sectorsize != DEV_BSIZE. We can't parse the
1974 1973 * partition table on whole-disk images unless the
1975 1974 * (undocumented) "secsize=..." mount option is used,
1976 1975 * but at least this allows us to mount if we have
1977 1976 * an image of a partition.
1978 1977 */
1979 1978 PC_DPRINTF1(3,
1980 1979 "!pcfs: Using BPB secsize %d\n", (int)secsize);
1981 1980 }
1982 1981 }
1983 1982
1984 1983 if (fsp->pcfs_mediasize == 0) {
1985 1984 mediasize = (len_t)totsec * (len_t)secsize;
1986 1985 /*
1987 1986 * This is not an error because not all devices support the
1988 1987 * dkio(7i) mediasize queries, and/or not all devices are
1989 1988 * partitioned. If we have not been able to figure out the
1990 1989 * size of the underlaying medium, we have to trust the BPB.
1991 1990 */
1992 1991 PC_DPRINTF4(3, "!pcfs: parseBPB: mediasize autodetect failed "
1993 1992 "on device (%x.%x):%d, trusting BPB totsec (%lld Bytes)\n",
1994 1993 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
1995 1994 fsp->pcfs_ldrive, (long long)fsp->pcfs_mediasize);
1996 1995 } else if ((len_t)totsec * (len_t)secsize > fsp->pcfs_mediasize) {
1997 1996 cmn_err(CE_WARN,
1998 1997 "!pcfs: autodetected mediasize (%lld Bytes) smaller than "
1999 1998 "FAT BPB mediasize (%lld Bytes).\n"
2000 1999 "truncated filesystem on device (%x.%x):%d, access errors "
2001 2000 "possible.\n",
2002 2001 (long long)fsp->pcfs_mediasize,
2003 2002 (long long)(totsec * (blkcnt_t)secsize),
2004 2003 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2005 2004 fsp->pcfs_ldrive);
2006 2005 mediasize = fsp->pcfs_mediasize;
2007 2006 } else {
2008 2007 /*
2009 2008 * This is actually ok. A FAT needs not occupy the maximum
2010 2009 * space available in its partition, it can be shorter.
2011 2010 */
2012 2011 mediasize = (len_t)totsec * (len_t)secsize;
2013 2012 }
2014 2013
2015 2014 /*
2016 2015 * Since we let just about anything pass through this function,
2017 2016 * fence against divide-by-zero here.
2018 2017 */
2019 2018 if (secsize)
2020 2019 rdirsec = roundup(rec * 32, secsize) / secsize;
2021 2020 else
2022 2021 rdirsec = 0;
2023 2022
2024 2023 /*
2025 2024 * This assignment is necessary before pc_dbdaddr() can first be
2026 2025 * used. Must initialize the value here.
2027 2026 */
2028 2027 fsp->pcfs_secsize = secsize;
2029 2028 fsp->pcfs_sdshift = ddi_ffs(secsize / DEV_BSIZE) - 1;
2030 2029
2031 2030 fsp->pcfs_mediasize = mediasize;
2032 2031
2033 2032 fsp->pcfs_spcl = bpb_get_SecPerClus(bpb);
2034 2033 fsp->pcfs_numfat = bpb_get_NumFATs(bpb);
2035 2034 fsp->pcfs_mediadesc = bpb_get_Media(bpb);
2036 2035 fsp->pcfs_clsize = secsize * fsp->pcfs_spcl;
2037 2036 fsp->pcfs_rdirsec = rdirsec;
2038 2037
2039 2038 /*
2040 2039 * Remember: All PCFS offset calculations in sectors. Before I/O
2041 2040 * is done, convert to DEV_BSIZE units via pc_dbdaddr(). This is
2042 2041 * necessary so that media with > 512Byte sector sizes work correctly.
2043 2042 */
2044 2043 fsp->pcfs_fatstart = fsp->pcfs_dosstart + reserved;
2045 2044 fsp->pcfs_rdirstart = fsp->pcfs_fatstart + fsp->pcfs_numfat * fatsec;
2046 2045 fsp->pcfs_datastart = fsp->pcfs_rdirstart + rdirsec;
2047 2046 datasec = totsec -
2048 2047 (blkcnt_t)fatsec * fsp->pcfs_numfat -
2049 2048 (blkcnt_t)rdirsec -
2050 2049 (blkcnt_t)reserved;
2051 2050
2052 2051 DTRACE_PROBE4(fatgeometry,
2053 2052 blkcnt_t, totsec, size_t, fatsec,
2054 2053 size_t, rdirsec, blkcnt_t, datasec);
2055 2054
2056 2055 /*
2057 2056 * 'totsec' is taken directly from the BPB and guaranteed to fit
2058 2057 * into a 32bit unsigned integer. The calculation of 'datasec',
2059 2058 * on the other hand, could underflow for incorrect values in
2060 2059 * rdirsec/reserved/fatsec. Check for that.
2061 2060 * We also check that the BPB conforms to the FAT specification's
2062 2061 * requirement that either of the 16/32bit total sector counts
2063 2062 * must be zero.
2064 2063 */
2065 2064 if (totsec != 0 &&
2066 2065 (totsec16 == totsec32 || totsec16 == 0 || totsec32 == 0) &&
2067 2066 datasec < totsec && datasec <= UINT32_MAX)
2068 2067 validflags |= BPB_TOTSEC_OK;
2069 2068
2070 2069 if ((len_t)totsec * (len_t)secsize <= mediasize)
2071 2070 validflags |= BPB_MEDIASZ_OK;
2072 2071
2073 2072 if (VALID_SECSIZE(secsize))
2074 2073 validflags |= BPB_SECSIZE_OK;
2075 2074 if (VALID_SPCL(fsp->pcfs_spcl))
2076 2075 validflags |= BPB_SECPERCLUS_OK;
2077 2076 if (VALID_CLSIZE(fsp->pcfs_clsize))
2078 2077 validflags |= BPB_CLSIZE_OK;
2079 2078 if (VALID_NUMFATS(fsp->pcfs_numfat))
2080 2079 validflags |= BPB_NUMFAT_OK;
2081 2080 if (VALID_RSVDSEC(reserved) && reserved < totsec)
2082 2081 validflags |= BPB_RSVDSECCNT_OK;
2083 2082 if (VALID_MEDIA(fsp->pcfs_mediadesc))
2084 2083 validflags |= BPB_MEDIADESC_OK;
2085 2084 if (VALID_BOOTSIG(bpb_get_BootSig16(bpb)))
2086 2085 validflags |= BPB_BOOTSIG16_OK;
2087 2086 if (VALID_BOOTSIG(bpb_get_BootSig32(bpb)))
2088 2087 validflags |= BPB_BOOTSIG32_OK;
2089 2088 if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb)))
2090 2089 validflags |= BPB_FSTYPSTR16_OK;
2091 2090 if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb)))
2092 2091 validflags |= BPB_FSTYPSTR32_OK;
2093 2092 if (VALID_OEMNAME(bpb_OEMName(bpb)))
2094 2093 validflags |= BPB_OEMNAME_OK;
2095 2094 if (bkbootsec > 0 && bkbootsec <= reserved && fsisec != bkbootsec)
2096 2095 validflags |= BPB_BKBOOTSEC_OK;
2097 2096 if (fsisec > 0 && fsisec <= reserved)
2098 2097 validflags |= BPB_FSISEC_OK;
2099 2098 if (VALID_JMPBOOT(bpb_jmpBoot(bpb)))
2100 2099 validflags |= BPB_JMPBOOT_OK;
2101 2100 if (VALID_FSVER32(bpb_get_FSVer32(bpb)))
2102 2101 validflags |= BPB_FSVER_OK;
2103 2102 if (VALID_VOLLAB(bpb_VolLab16(bpb)))
2104 2103 validflags |= BPB_VOLLAB16_OK;
2105 2104 if (VALID_VOLLAB(bpb_VolLab32(bpb)))
2106 2105 validflags |= BPB_VOLLAB32_OK;
2107 2106 if (VALID_EXTFLAGS(bpb_get_ExtFlags32(bpb)))
2108 2107 validflags |= BPB_EXTFLAGS_OK;
2109 2108
2110 2109 /*
2111 2110 * Try to determine which FAT format to use.
2112 2111 *
2113 2112 * Calculate the number of clusters in order to determine
2114 2113 * the type of FAT we are looking at. This is the only
2115 2114 * recommended way of determining FAT type, though there
2116 2115 * are other hints in the data, this is the best way.
2117 2116 *
2118 2117 * Since we let just about "anything" pass through this function
2119 2118 * without early exits, fence against divide-by-zero here.
2120 2119 *
2121 2120 * datasec was already validated against UINT32_MAX so we know
2122 2121 * the result will not overflow the 32bit calculation.
2123 2122 */
2124 2123 if (fsp->pcfs_spcl)
2125 2124 ncl = (uint32_t)datasec / fsp->pcfs_spcl;
2126 2125 else
2127 2126 ncl = 0;
2128 2127
2129 2128 fsp->pcfs_ncluster = ncl;
2130 2129
2131 2130 /*
2132 2131 * From the Microsoft FAT specification:
2133 2132 * In the following example, when it says <, it does not mean <=.
2134 2133 * Note also that the numbers are correct. The first number for
2135 2134 * FAT12 is 4085; the second number for FAT16 is 65525. These numbers
2136 2135 * and the '<' signs are not wrong.
2137 2136 *
2138 2137 * We "specialdetect" the corner cases, and use at least one "extra"
2139 2138 * criterion to decide whether it's FAT16 or FAT32 if the cluster
2140 2139 * count is dangerously close to the boundaries.
2141 2140 */
2142 2141
2143 2142 if (ncl <= PCF_FIRSTCLUSTER) {
2144 2143 type = FAT_UNKNOWN;
2145 2144 } else if (ncl < 4085) {
2146 2145 type = FAT12;
2147 2146 } else if (ncl <= 4096) {
2148 2147 type = FAT_QUESTIONABLE;
2149 2148 } else if (ncl < 65525) {
2150 2149 type = FAT16;
2151 2150 } else if (ncl <= 65536) {
2152 2151 type = FAT_QUESTIONABLE;
2153 2152 } else if (ncl < PCF_LASTCLUSTER32) {
2154 2153 type = FAT32;
2155 2154 } else {
2156 2155 type = FAT_UNKNOWN;
2157 2156 }
2158 2157
2159 2158 DTRACE_PROBE4(parseBPB__initial,
2160 2159 struct pcfs *, fsp, unsigned char *, bpb,
2161 2160 int, validflags, fattype_t, type);
2162 2161
2163 2162 recheck:
2164 2163 fsp->pcfs_fatsec = fatsec;
2165 2164
2166 2165 /* Do some final sanity checks for each specific type of FAT */
2167 2166 switch (type) {
2168 2167 case FAT12:
2169 2168 if (rec != 0)
2170 2169 validflags |= BPB_ROOTENTCNT_OK;
2171 2170 if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2172 2171 bpb_get_TotSec16(bpb) == 0)
2173 2172 validflags |= BPB_TOTSEC16_OK;
2174 2173 if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2175 2174 bpb_get_TotSec32(bpb) == 0)
2176 2175 validflags |= BPB_TOTSEC32_OK;
2177 2176 if (bpb_get_FatSz16(bpb) == fatsec)
2178 2177 validflags |= BPB_FATSZ16_OK;
2179 2178 if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER)
2180 2179 * 3 / 2)
2181 2180 validflags |= BPB_FATSZ_OK;
2182 2181 if (ncl < 4085)
2183 2182 validflags |= BPB_NCLUSTERS_OK;
2184 2183
2185 2184 fsp->pcfs_lastclmark = (PCF_LASTCLUSTER & 0xfff);
2186 2185 fsp->pcfs_rootblksize =
2187 2186 fsp->pcfs_rdirsec * secsize;
2188 2187 fsp->pcfs_fsistart = 0;
2189 2188
2190 2189 if ((validflags & FAT12_VALIDMSK) != FAT12_VALIDMSK)
2191 2190 type = FAT_UNKNOWN;
2192 2191 break;
2193 2192 case FAT16:
2194 2193 if (rec != 0)
2195 2194 validflags |= BPB_ROOTENTCNT_OK;
2196 2195 if ((blkcnt_t)bpb_get_TotSec16(bpb) == totsec ||
2197 2196 bpb_get_TotSec16(bpb) == 0)
2198 2197 validflags |= BPB_TOTSEC16_OK;
2199 2198 if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec ||
2200 2199 bpb_get_TotSec32(bpb) == 0)
2201 2200 validflags |= BPB_TOTSEC32_OK;
2202 2201 if (bpb_get_FatSz16(bpb) == fatsec)
2203 2202 validflags |= BPB_FATSZ16_OK;
2204 2203 if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 2)
2205 2204 validflags |= BPB_FATSZ_OK;
2206 2205 if (ncl >= 4085 && ncl < 65525)
2207 2206 validflags |= BPB_NCLUSTERS_OK;
2208 2207
2209 2208 fsp->pcfs_lastclmark = PCF_LASTCLUSTER;
2210 2209 fsp->pcfs_rootblksize =
2211 2210 fsp->pcfs_rdirsec * secsize;
2212 2211 fsp->pcfs_fsistart = 0;
2213 2212
2214 2213 if ((validflags & FAT16_VALIDMSK) != FAT16_VALIDMSK)
2215 2214 type = FAT_UNKNOWN;
2216 2215 break;
2217 2216 case FAT32:
2218 2217 if (rec == 0)
2219 2218 validflags |= BPB_ROOTENTCNT_OK;
2220 2219 if (bpb_get_TotSec16(bpb) == 0)
2221 2220 validflags |= BPB_TOTSEC16_OK;
2222 2221 if ((blkcnt_t)bpb_get_TotSec32(bpb) == totsec)
2223 2222 validflags |= BPB_TOTSEC32_OK;
2224 2223 if (bpb_get_FatSz16(bpb) == 0)
2225 2224 validflags |= BPB_FATSZ16_OK;
2226 2225 if (bpb_get_FatSz32(bpb) == fatsec)
2227 2226 validflags |= BPB_FATSZ32_OK;
2228 2227 if (fatsec * secsize >= (ncl + PCF_FIRSTCLUSTER) * 4)
2229 2228 validflags |= BPB_FATSZ_OK;
2230 2229 if (ncl >= 65525 && ncl < PCF_LASTCLUSTER32)
2231 2230 validflags |= BPB_NCLUSTERS_OK;
2232 2231
2233 2232 fsp->pcfs_lastclmark = PCF_LASTCLUSTER32;
2234 2233 fsp->pcfs_rootblksize = fsp->pcfs_clsize;
2235 2234 fsp->pcfs_fsistart = fsp->pcfs_dosstart + fsisec;
2236 2235 if (validflags & BPB_FSISEC_OK)
2237 2236 fsp->pcfs_flags |= PCFS_FSINFO_OK;
2238 2237 fsp->pcfs_rootclnum = bpb_get_RootClus32(bpb);
2239 2238 if (pc_validcl(fsp, fsp->pcfs_rootclnum))
2240 2239 validflags |= BPB_ROOTCLUSTER_OK;
2241 2240
2242 2241 /*
2243 2242 * Current PCFS code only works if 'pcfs_rdirstart'
2244 2243 * contains the root cluster number on FAT32.
2245 2244 * That's a mis-use and would better be changed.
2246 2245 */
2247 2246 fsp->pcfs_rdirstart = (daddr_t)fsp->pcfs_rootclnum;
2248 2247
2249 2248 if ((validflags & FAT32_VALIDMSK) != FAT32_VALIDMSK)
2250 2249 type = FAT_UNKNOWN;
2251 2250 break;
2252 2251 case FAT_QUESTIONABLE:
2253 2252 type = secondaryBPBChecks(fsp, bpb, secsize);
2254 2253 goto recheck;
2255 2254 default:
2256 2255 ASSERT(type == FAT_UNKNOWN);
2257 2256 break;
2258 2257 }
2259 2258
2260 2259 ASSERT(type != FAT_QUESTIONABLE);
2261 2260
2262 2261 fsp->pcfs_fattype = type;
2263 2262
2264 2263 if (valid)
2265 2264 *valid = validflags;
2266 2265
2267 2266 DTRACE_PROBE4(parseBPB__final,
2268 2267 struct pcfs *, fsp, unsigned char *, bpb,
2269 2268 int, validflags, fattype_t, type);
2270 2269
2271 2270 if (type != FAT_UNKNOWN) {
2272 2271 ASSERT((secsize & (DEV_BSIZE - 1)) == 0);
2273 2272 ASSERT(ISP2(secsize / DEV_BSIZE));
2274 2273 return (1);
2275 2274 }
2276 2275
2277 2276 return (0);
2278 2277 }
2279 2278
2280 2279
2281 2280 /*
2282 2281 * Detect the device's native block size (sector size).
2283 2282 *
2284 2283 * Test whether the device is:
2285 2284 * - a floppy device from a known controller type via DKIOCINFO
2286 2285 * - a real floppy using the fd(7d) driver and capable of fdio(7I) ioctls
2287 2286 * - a USB floppy drive (identified by drive geometry)
2288 2287 *
2289 2288 * Detecting a floppy will make PCFS metadata updates on such media synchronous,
2290 2289 * to minimize risks due to slow I/O and user hotplugging / device ejection.
2291 2290 *
2292 2291 * This might be a bit wasteful on kernel stack space; if anyone's
2293 2292 * bothered by this, kmem_alloc/kmem_free the ioctl arguments...
2294 2293 */
2295 2294 static void
2296 2295 pcfs_device_getinfo(struct pcfs *fsp)
2297 2296 {
2298 2297 dev_t rdev = fsp->pcfs_xdev;
2299 2298 int error;
2300 2299 union {
2301 2300 struct dk_minfo mi;
2302 2301 struct dk_cinfo ci;
2303 2302 struct dk_geom gi;
2304 2303 struct fd_char fc;
2305 2304 } arg; /* save stackspace ... */
2306 2305 intptr_t argp = (intptr_t)&arg;
2307 2306 ldi_handle_t lh;
2308 2307 ldi_ident_t li;
2309 2308 int isfloppy, isremoveable, ishotpluggable;
2310 2309 cred_t *cr = CRED();
2311 2310
2312 2311 if (ldi_ident_from_dev(rdev, &li))
2313 2312 goto out;
2314 2313
2315 2314 error = ldi_open_by_dev(&rdev, OTYP_CHR, FREAD, cr, &lh, li);
2316 2315 ldi_ident_release(li);
2317 2316 if (error)
2318 2317 goto out;
2319 2318
2320 2319 /*
2321 2320 * Not sure if this could possibly happen. It'd be a bit like
2322 2321 * VOP_OPEN() changing the passed-in vnode ptr. We're just not
2323 2322 * expecting it, needs some thought if triggered ...
2324 2323 */
2325 2324 ASSERT(fsp->pcfs_xdev == rdev);
2326 2325
2327 2326 /*
2328 2327 * Check for removeable/hotpluggable media.
2329 2328 */
2330 2329 if (ldi_ioctl(lh, DKIOCREMOVABLE,
2331 2330 (intptr_t)&isremoveable, FKIOCTL, cr, NULL)) {
2332 2331 isremoveable = 0;
2333 2332 }
2334 2333 if (ldi_ioctl(lh, DKIOCHOTPLUGGABLE,
2335 2334 (intptr_t)&ishotpluggable, FKIOCTL, cr, NULL)) {
2336 2335 ishotpluggable = 0;
2337 2336 }
2338 2337
2339 2338 /*
2340 2339 * Make sure we don't use "half-initialized" values if the ioctls fail.
2341 2340 */
2342 2341 if (ldi_ioctl(lh, DKIOCGMEDIAINFO, argp, FKIOCTL, cr, NULL)) {
2343 2342 bzero(&arg, sizeof (arg));
2344 2343 fsp->pcfs_mediasize = 0;
2345 2344 } else {
2346 2345 fsp->pcfs_mediasize =
2347 2346 (len_t)arg.mi.dki_lbsize *
2348 2347 (len_t)arg.mi.dki_capacity;
2349 2348 }
2350 2349
2351 2350 if (VALID_SECSIZE(arg.mi.dki_lbsize)) {
2352 2351 if (fsp->pcfs_secsize == 0) {
2353 2352 fsp->pcfs_secsize = arg.mi.dki_lbsize;
2354 2353 fsp->pcfs_sdshift =
2355 2354 ddi_ffs(arg.mi.dki_lbsize / DEV_BSIZE) - 1;
2356 2355 } else {
2357 2356 PC_DPRINTF4(1, "!pcfs: autodetected media block size "
2358 2357 "%d, device (%x.%x), different from user-provided "
2359 2358 "%d. User override - ignoring autodetect result.\n",
2360 2359 arg.mi.dki_lbsize,
2361 2360 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2362 2361 fsp->pcfs_secsize);
2363 2362 }
2364 2363 } else if (arg.mi.dki_lbsize) {
2365 2364 PC_DPRINTF3(1, "!pcfs: autodetected media block size "
2366 2365 "%d, device (%x.%x), invalid (not 512, 1024, 2048, 4096). "
2367 2366 "Ignoring autodetect result.\n",
2368 2367 arg.mi.dki_lbsize,
2369 2368 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev));
2370 2369 }
2371 2370
2372 2371 /*
2373 2372 * We treat the following media types as a floppy by default.
2374 2373 */
2375 2374 isfloppy =
2376 2375 (arg.mi.dki_media_type == DK_FLOPPY ||
2377 2376 arg.mi.dki_media_type == DK_ZIP ||
2378 2377 arg.mi.dki_media_type == DK_JAZ);
2379 2378
2380 2379 /*
2381 2380 * if this device understands fdio(7I) requests it's
2382 2381 * obviously a floppy drive.
2383 2382 */
2384 2383 if (!isfloppy &&
2385 2384 !ldi_ioctl(lh, FDIOGCHAR, argp, FKIOCTL, cr, NULL))
2386 2385 isfloppy = 1;
2387 2386
2388 2387 /*
2389 2388 * some devices we like to treat as floppies, but they don't
2390 2389 * understand fdio(7I) requests.
2391 2390 */
2392 2391 if (!isfloppy &&
2393 2392 !ldi_ioctl(lh, DKIOCINFO, argp, FKIOCTL, cr, NULL) &&
2394 2393 (arg.ci.dki_ctype == DKC_WDC2880 ||
2395 2394 arg.ci.dki_ctype == DKC_NCRFLOPPY ||
2396 2395 arg.ci.dki_ctype == DKC_SMSFLOPPY ||
2397 2396 arg.ci.dki_ctype == DKC_INTEL82077))
2398 2397 isfloppy = 1;
2399 2398
2400 2399 /*
2401 2400 * This is the "final fallback" test - media with
2402 2401 * 2 heads and 80 cylinders are assumed to be floppies.
2403 2402 * This is normally true for USB floppy drives ...
2404 2403 */
2405 2404 if (!isfloppy &&
2406 2405 !ldi_ioctl(lh, DKIOCGGEOM, argp, FKIOCTL, cr, NULL) &&
2407 2406 (arg.gi.dkg_ncyl == 80 && arg.gi.dkg_nhead == 2))
2408 2407 isfloppy = 1;
2409 2408
2410 2409 /*
2411 2410 * This is similar to the "old" PCFS code that sets this flag
2412 2411 * just based on the media descriptor being 0xf8 (MD_FIXED).
2413 2412 * Should be re-worked. We really need some specialcasing for
2414 2413 * removeable media.
2415 2414 */
2416 2415 if (!isfloppy) {
2417 2416 fsp->pcfs_flags |= PCFS_NOCHK;
2418 2417 }
2419 2418
2420 2419 /*
2421 2420 * We automatically disable access time updates if the medium is
2422 2421 * removeable and/or hotpluggable, and the admin did not explicitly
2423 2422 * request access time updates (via the "atime" mount option).
2424 2423 * The majority of flash-based media should fit this category.
2425 2424 * Minimizing write access extends the lifetime of your memory stick !
2426 2425 */
2427 2426 if (!vfs_optionisset(fsp->pcfs_vfs, MNTOPT_ATIME, NULL) &&
2428 2427 (isremoveable || ishotpluggable | isfloppy)) {
2429 2428 fsp->pcfs_flags |= PCFS_NOATIME;
2430 2429 }
2431 2430
2432 2431 (void) ldi_close(lh, FREAD, cr);
2433 2432 out:
2434 2433 if (fsp->pcfs_secsize == 0) {
2435 2434 PC_DPRINTF3(1, "!pcfs: media block size autodetection "
2436 2435 "device (%x.%x) failed, no user-provided fallback. "
2437 2436 "Using %d bytes.\n",
2438 2437 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2439 2438 DEV_BSIZE);
2440 2439 fsp->pcfs_secsize = DEV_BSIZE;
2441 2440 fsp->pcfs_sdshift = 0;
2442 2441 }
2443 2442 ASSERT(fsp->pcfs_secsize % DEV_BSIZE == 0);
2444 2443 ASSERT(VALID_SECSIZE(fsp->pcfs_secsize));
2445 2444 }
2446 2445
2447 2446 /*
2448 2447 * Get the FAT type for the DOS medium.
2449 2448 *
2450 2449 * -------------------------
2451 2450 * According to Microsoft:
2452 2451 * The FAT type one of FAT12, FAT16, or FAT32 is determined by the
2453 2452 * count of clusters on the volume and nothing else.
2454 2453 * -------------------------
2455 2454 *
2456 2455 */
2457 2456 static int
2458 2457 pc_getfattype(struct pcfs *fsp)
2459 2458 {
2460 2459 int error = 0;
2461 2460 buf_t *bp = NULL;
2462 2461 struct vnode *devvp = fsp->pcfs_devvp;
2463 2462 dev_t dev = devvp->v_rdev;
2464 2463
2465 2464 /*
2466 2465 * Detect the native block size of the medium, and attempt to
2467 2466 * detect whether the medium is removeable.
2468 2467 * We do treat removable media (floppies, USB and FireWire disks)
2469 2468 * differently wrt. to the frequency and synchronicity of FAT updates.
2470 2469 * We need to know the media block size in order to be able to
2471 2470 * parse the partition table.
2472 2471 */
2473 2472 pcfs_device_getinfo(fsp);
2474 2473
2475 2474 /*
2476 2475 * Unpartitioned media (floppies and some removeable devices)
2477 2476 * don't have a partition table, the FAT BPB is at disk block 0.
2478 2477 * Start out by reading block 0.
2479 2478 */
2480 2479 fsp->pcfs_dosstart = 0;
2481 2480 bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart), fsp->pcfs_secsize);
2482 2481
2483 2482 if (error = geterror(bp))
2484 2483 goto out;
2485 2484
2486 2485 /*
2487 2486 * If a logical drive number is requested, parse the partition table
2488 2487 * and attempt to locate it. Otherwise, proceed immediately to the
2489 2488 * BPB check. findTheDrive(), if successful, returns the disk block
2490 2489 * number where the requested partition starts in "startsec".
2491 2490 */
2492 2491 if (fsp->pcfs_ldrive != 0) {
2493 2492 PC_DPRINTF3(5, "!pcfs: pc_getfattype: using FDISK table on "
2494 2493 "device (%x,%x):%d to find BPB\n",
2495 2494 getmajor(dev), getminor(dev), fsp->pcfs_ldrive);
2496 2495
2497 2496 if (error = findTheDrive(fsp, &bp))
2498 2497 goto out;
2499 2498
2500 2499 ASSERT(fsp->pcfs_dosstart != 0);
2501 2500
2502 2501 brelse(bp);
2503 2502 bp = bread(dev, pc_dbdaddr(fsp, fsp->pcfs_dosstart),
2504 2503 fsp->pcfs_secsize);
2505 2504 if (error = geterror(bp))
2506 2505 goto out;
2507 2506 }
2508 2507
2509 2508 /*
2510 2509 * Validate the BPB and fill in the instance structure.
2511 2510 */
2512 2511 if (!parseBPB(fsp, (uchar_t *)bp->b_un.b_addr, NULL)) {
2513 2512 PC_DPRINTF4(1, "!pcfs: pc_getfattype: No FAT BPB on "
2514 2513 "device (%x.%x):%d, disk LBA %u\n",
2515 2514 getmajor(dev), getminor(dev), fsp->pcfs_ldrive,
2516 2515 (uint_t)pc_dbdaddr(fsp, fsp->pcfs_dosstart));
2517 2516 error = EINVAL;
2518 2517 goto out;
2519 2518 }
2520 2519
2521 2520 ASSERT(fsp->pcfs_fattype != FAT_UNKNOWN);
2522 2521
2523 2522 out:
2524 2523 /*
2525 2524 * Release the buffer used
2526 2525 */
2527 2526 if (bp != NULL)
2528 2527 brelse(bp);
2529 2528 return (error);
2530 2529 }
2531 2530
2532 2531
2533 2532 /*
2534 2533 * Get the file allocation table.
2535 2534 * If there is an old FAT, invalidate it.
2536 2535 */
2537 2536 int
2538 2537 pc_getfat(struct pcfs *fsp)
2539 2538 {
2540 2539 struct buf *bp = NULL;
2541 2540 uchar_t *fatp = NULL;
2542 2541 uchar_t *fat_changemap = NULL;
2543 2542 int error;
2544 2543 int fat_changemapsize;
2545 2544 int flags = 0;
2546 2545 int nfat;
2547 2546 int altfat_mustmatch = 0;
2548 2547 int fatsize = fsp->pcfs_fatsec * fsp->pcfs_secsize;
2549 2548
2550 2549 if (fsp->pcfs_fatp) {
2551 2550 /*
2552 2551 * There is a FAT in core.
2553 2552 * If there are open file pcnodes or we have modified it or
2554 2553 * it hasn't timed out yet use the in core FAT.
2555 2554 * Otherwise invalidate it and get a new one
2556 2555 */
2557 2556 #ifdef notdef
2558 2557 if (fsp->pcfs_frefs ||
2559 2558 (fsp->pcfs_flags & PCFS_FATMOD) ||
2560 2559 (gethrestime_sec() < fsp->pcfs_fattime)) {
2561 2560 return (0);
2562 2561 } else {
2563 2562 mutex_enter(&pcfslock);
2564 2563 pc_invalfat(fsp);
2565 2564 mutex_exit(&pcfslock);
2566 2565 }
2567 2566 #endif /* notdef */
2568 2567 return (0);
2569 2568 }
2570 2569
2571 2570 /*
2572 2571 * Get FAT and check it for validity
2573 2572 */
2574 2573 fatp = kmem_alloc(fatsize, KM_SLEEP);
2575 2574 error = pc_readfat(fsp, fatp);
2576 2575 if (error) {
2577 2576 flags = B_ERROR;
2578 2577 goto out;
2579 2578 }
2580 2579 fat_changemapsize = (fatsize / fsp->pcfs_clsize) + 1;
2581 2580 fat_changemap = kmem_zalloc(fat_changemapsize, KM_SLEEP);
2582 2581 fsp->pcfs_fatp = fatp;
2583 2582 fsp->pcfs_fat_changemapsize = fat_changemapsize;
2584 2583 fsp->pcfs_fat_changemap = fat_changemap;
2585 2584
2586 2585 /*
2587 2586 * The only definite signature check is that the
2588 2587 * media descriptor byte should match the first byte
2589 2588 * of the FAT block.
2590 2589 */
2591 2590 if (fatp[0] != fsp->pcfs_mediadesc) {
2592 2591 cmn_err(CE_NOTE, "!pcfs: FAT signature mismatch, "
2593 2592 "media descriptor %x, FAT[0] lowbyte %x\n",
2594 2593 (uint32_t)fsp->pcfs_mediadesc, (uint32_t)fatp[0]);
2595 2594 cmn_err(CE_NOTE, "!pcfs: Enforcing alternate FAT validation\n");
2596 2595 altfat_mustmatch = 1;
2597 2596 }
2598 2597
2599 2598 /*
2600 2599 * Get alternate FATs and check for consistency
2601 2600 * This is an inlined version of pc_readfat().
2602 2601 * Since we're only comparing FAT and alternate FAT,
2603 2602 * there's no reason to let pc_readfat() copy data out
2604 2603 * of the buf. Instead, compare in-situ, one cluster
2605 2604 * at a time.
2606 2605 */
2607 2606 for (nfat = 1; nfat < fsp->pcfs_numfat; nfat++) {
2608 2607 size_t startsec;
2609 2608 size_t off;
2610 2609
2611 2610 startsec = pc_dbdaddr(fsp,
2612 2611 fsp->pcfs_fatstart + nfat * fsp->pcfs_fatsec);
2613 2612
2614 2613 for (off = 0; off < fatsize; off += fsp->pcfs_clsize) {
2615 2614 daddr_t fatblk = startsec + pc_dbdaddr(fsp,
2616 2615 pc_cltodb(fsp, pc_lblkno(fsp, off)));
2617 2616
2618 2617 bp = bread(fsp->pcfs_xdev, fatblk,
2619 2618 MIN(fsp->pcfs_clsize, fatsize - off));
2620 2619 if (bp->b_flags & (B_ERROR | B_STALE)) {
2621 2620 cmn_err(CE_NOTE,
2622 2621 "!pcfs: alternate FAT #%d (start LBA %p)"
2623 2622 " read error at offset %ld on device"
2624 2623 " (%x.%x):%d",
2625 2624 nfat, (void *)(uintptr_t)startsec, off,
2626 2625 getmajor(fsp->pcfs_xdev),
2627 2626 getminor(fsp->pcfs_xdev),
2628 2627 fsp->pcfs_ldrive);
2629 2628 flags = B_ERROR;
2630 2629 error = EIO;
2631 2630 goto out;
2632 2631 }
2633 2632 bp->b_flags |= B_STALE | B_AGE;
2634 2633 if (bcmp(bp->b_un.b_addr, fatp + off,
2635 2634 MIN(fsp->pcfs_clsize, fatsize - off))) {
2636 2635 cmn_err(CE_NOTE,
2637 2636 "!pcfs: alternate FAT #%d (start LBA %p)"
2638 2637 " corrupted at offset %ld on device"
2639 2638 " (%x.%x):%d",
2640 2639 nfat, (void *)(uintptr_t)startsec, off,
2641 2640 getmajor(fsp->pcfs_xdev),
2642 2641 getminor(fsp->pcfs_xdev),
2643 2642 fsp->pcfs_ldrive);
2644 2643 if (altfat_mustmatch) {
2645 2644 flags = B_ERROR;
2646 2645 error = EIO;
2647 2646 goto out;
2648 2647 }
2649 2648 }
2650 2649 brelse(bp);
2651 2650 bp = NULL; /* prevent double release */
2652 2651 }
2653 2652 }
2654 2653
2655 2654 fsp->pcfs_fattime = gethrestime_sec() + PCFS_DISKTIMEOUT;
2656 2655 fsp->pcfs_fatjustread = 1;
2657 2656
2658 2657 /*
2659 2658 * Retrieve FAT32 fsinfo sector.
2660 2659 * A failure to read this is not fatal to accessing the volume.
2661 2660 * It simply means operations that count or search free blocks
2662 2661 * will have to do a full FAT walk, vs. a possibly quicker lookup
2663 2662 * of the summary information.
2664 2663 * Hence, we log a message but return success overall after this point.
2665 2664 */
2666 2665 if (IS_FAT32(fsp) && (fsp->pcfs_flags & PCFS_FSINFO_OK)) {
2667 2666 struct fat_od_fsi *fsinfo_disk;
2668 2667
2669 2668 bp = bread(fsp->pcfs_xdev,
2670 2669 pc_dbdaddr(fsp, fsp->pcfs_fsistart), fsp->pcfs_secsize);
2671 2670 fsinfo_disk = (struct fat_od_fsi *)bp->b_un.b_addr;
2672 2671 if (bp->b_flags & (B_ERROR | B_STALE) ||
2673 2672 !FSISIG_OK(fsinfo_disk)) {
2674 2673 cmn_err(CE_NOTE,
2675 2674 "!pcfs: error reading fat32 fsinfo from "
2676 2675 "device (%x.%x):%d, block %lld",
2677 2676 getmajor(fsp->pcfs_xdev), getminor(fsp->pcfs_xdev),
2678 2677 fsp->pcfs_ldrive,
2679 2678 (long long)pc_dbdaddr(fsp, fsp->pcfs_fsistart));
2680 2679 fsp->pcfs_flags &= ~PCFS_FSINFO_OK;
2681 2680 fsp->pcfs_fsinfo.fs_free_clusters = FSINFO_UNKNOWN;
2682 2681 fsp->pcfs_fsinfo.fs_next_free = FSINFO_UNKNOWN;
2683 2682 } else {
2684 2683 bp->b_flags |= B_STALE | B_AGE;
2685 2684 fsinfo_disk = (fat_od_fsi_t *)(bp->b_un.b_addr);
2686 2685 fsp->pcfs_fsinfo.fs_free_clusters =
2687 2686 LE_32(fsinfo_disk->fsi_incore.fs_free_clusters);
2688 2687 fsp->pcfs_fsinfo.fs_next_free =
2689 2688 LE_32(fsinfo_disk->fsi_incore.fs_next_free);
2690 2689 }
2691 2690 brelse(bp);
2692 2691 bp = NULL;
2693 2692 }
2694 2693
2695 2694 if (pc_validcl(fsp, (pc_cluster32_t)fsp->pcfs_fsinfo.fs_next_free))
2696 2695 fsp->pcfs_nxfrecls = fsp->pcfs_fsinfo.fs_next_free;
2697 2696 else
2698 2697 fsp->pcfs_nxfrecls = PCF_FIRSTCLUSTER;
2699 2698
2700 2699 return (0);
2701 2700
2702 2701 out:
2703 2702 cmn_err(CE_NOTE, "!pcfs: illegal disk format");
2704 2703 if (bp)
2705 2704 brelse(bp);
2706 2705 if (fatp)
2707 2706 kmem_free(fatp, fatsize);
2708 2707 if (fat_changemap)
2709 2708 kmem_free(fat_changemap, fat_changemapsize);
2710 2709
2711 2710 if (flags) {
2712 2711 pc_mark_irrecov(fsp);
2713 2712 }
2714 2713 return (error);
2715 2714 }
↓ open down ↓ |
2462 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX