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