1 /* $NetBSD: ext2fs_vfsops.c,v 1.125 2007/12/08 19:29:53 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94 32 * Modified for ext2fs by Manuel Bouyer. 33 */ 34 35 /* 36 * Copyright (c) 1997 Manuel Bouyer. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. All advertising materials mentioning features or use of this software 47 * must display the following acknowledgement: 48 * This product includes software developed by Manuel Bouyer. 49 * 4. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 53 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 54 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 55 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 56 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 57 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 58 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 59 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 60 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 61 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 * 63 * @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94 64 * Modified for ext2fs by Manuel Bouyer. 65 */ 66 67 #include <sys/cdefs.h> 68 __KERNEL_RCSID(0, "$NetBSD: ext2fs_vfsops.c,v 1.125 2007/12/08 19:29:53 pooka Exp $"); 69 70 #if defined(_KERNEL_OPT) 71 #include "opt_compat_netbsd.h" 72 #endif 73 74 #include <sys/param.h> 75 #include <sys/systm.h> 76 #include <sys/sysctl.h> 77 #include <sys/namei.h> 78 #include <sys/proc.h> 79 #include <sys/kernel.h> 80 #include <sys/vnode.h> 81 #include <sys/socket.h> 82 #include <sys/mount.h> 83 #include <sys/buf.h> 84 #include <sys/device.h> 85 #include <sys/mbuf.h> 86 #include <sys/file.h> 87 #include <sys/disklabel.h> 88 #include <sys/ioctl.h> 89 #include <sys/errno.h> 90 #include <sys/malloc.h> 91 #include <sys/pool.h> 92 #include <sys/lock.h> 93 #include <sys/conf.h> 94 #include <sys/kauth.h> 95 96 #include <miscfs/specfs/specdev.h> 97 98 #include <ufs/ufs/quota.h> 99 #include <ufs/ufs/ufsmount.h> 100 #include <ufs/ufs/inode.h> 101 #include <ufs/ufs/dir.h> 102 #include <ufs/ufs/ufs_extern.h> 103 104 #include <ufs/ext2fs/ext2fs.h> 105 #include <ufs/ext2fs/ext2fs_dir.h> 106 #include <ufs/ext2fs/ext2fs_extern.h> 107 108 extern kmutex_t ufs_hashlock; 109 110 int ext2fs_sbupdate(struct ufsmount *, int); 111 static int ext2fs_checksb(struct ext2fs *, int); 112 113 extern const struct vnodeopv_desc ext2fs_vnodeop_opv_desc; 114 extern const struct vnodeopv_desc ext2fs_specop_opv_desc; 115 extern const struct vnodeopv_desc ext2fs_fifoop_opv_desc; 116 117 const struct vnodeopv_desc * const ext2fs_vnodeopv_descs[] = { 118 &ext2fs_vnodeop_opv_desc, 119 &ext2fs_specop_opv_desc, 120 &ext2fs_fifoop_opv_desc, 121 NULL, 122 }; 123 124 struct vfsops ext2fs_vfsops = { 125 MOUNT_EXT2FS, 126 sizeof (struct ufs_args), 127 ext2fs_mount, 128 ufs_start, 129 ext2fs_unmount, 130 ufs_root, 131 ufs_quotactl, 132 ext2fs_statvfs, 133 ext2fs_sync, 134 ext2fs_vget, 135 ext2fs_fhtovp, 136 ext2fs_vptofh, 137 ext2fs_init, 138 ext2fs_reinit, 139 ext2fs_done, 140 ext2fs_mountroot, 141 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp, 142 vfs_stdextattrctl, 143 (void *)eopnotsupp, /* vfs_suspendctl */ 144 ext2fs_vnodeopv_descs, 145 0, 146 { NULL, NULL }, 147 }; 148 VFS_ATTACH(ext2fs_vfsops); 149 150 static const struct genfs_ops ext2fs_genfsops = { 151 .gop_size = genfs_size, 152 .gop_alloc = ext2fs_gop_alloc, 153 .gop_write = genfs_gop_write, 154 .gop_markupdate = ufs_gop_markupdate, 155 }; 156 157 static const struct ufs_ops ext2fs_ufsops = { 158 .uo_itimes = ext2fs_itimes, 159 .uo_update = ext2fs_update, 160 }; 161 162 /* 163 * XXX Same structure as FFS inodes? Should we share a common pool? 164 */ 165 struct pool ext2fs_inode_pool; 166 struct pool ext2fs_dinode_pool; 167 168 extern u_long ext2gennumber; 169 170 void 171 ext2fs_init(void) 172 { 173 174 pool_init(&ext2fs_inode_pool, sizeof(struct inode), 0, 0, 0, 175 "ext2fsinopl", &pool_allocator_nointr, IPL_NONE); 176 pool_init(&ext2fs_dinode_pool, sizeof(struct ext2fs_dinode), 0, 0, 0, 177 "ext2dinopl", &pool_allocator_nointr, IPL_NONE); 178 ufs_init(); 179 } 180 181 void 182 ext2fs_reinit(void) 183 { 184 ufs_reinit(); 185 } 186 187 void 188 ext2fs_done(void) 189 { 190 191 ufs_done(); 192 pool_destroy(&ext2fs_inode_pool); 193 pool_destroy(&ext2fs_dinode_pool); 194 } 195 196 /* 197 * Called by main() when ext2fs is going to be mounted as root. 198 * 199 * Name is updated by mount(8) after booting. 200 */ 201 #define ROOTNAME "root_device" 202 203 int 204 ext2fs_mountroot(void) 205 { 206 extern struct vnode *rootvp; 207 struct m_ext2fs *fs; 208 struct mount *mp; 209 struct ufsmount *ump; 210 int error; 211 212 if (device_class(root_device) != DV_DISK) 213 return (ENODEV); 214 215 if ((error = vfs_rootmountalloc(MOUNT_EXT2FS, "root_device", &mp))) { 216 vrele(rootvp); 217 return (error); 218 } 219 220 if ((error = ext2fs_mountfs(rootvp, mp)) != 0) { 221 mp->mnt_op->vfs_refcount--; 222 vfs_unbusy(mp); 223 vfs_destroy(mp); 224 return (error); 225 } 226 mutex_enter(&mountlist_lock); 227 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 228 mutex_exit(&mountlist_lock); 229 ump = VFSTOUFS(mp); 230 fs = ump->um_e2fs; 231 memset(fs->e2fs_fsmnt, 0, sizeof(fs->e2fs_fsmnt)); 232 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs_fsmnt, 233 sizeof(fs->e2fs_fsmnt) - 1, 0); 234 if (fs->e2fs.e2fs_rev > E2FS_REV0) { 235 memset(fs->e2fs.e2fs_fsmnt, 0, sizeof(fs->e2fs.e2fs_fsmnt)); 236 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs.e2fs_fsmnt, 237 sizeof(fs->e2fs.e2fs_fsmnt) - 1, 0); 238 } 239 (void)ext2fs_statvfs(mp, &mp->mnt_stat); 240 vfs_unbusy(mp); 241 setrootfstime((time_t)fs->e2fs.e2fs_wtime); 242 return (0); 243 } 244 245 /* 246 * VFS Operations. 247 * 248 * mount system call 249 */ 250 int 251 ext2fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 252 { 253 struct lwp *l = curlwp; 254 struct nameidata nd; 255 struct vnode *devvp; 256 struct ufs_args *args = data; 257 struct ufsmount *ump = NULL; 258 struct m_ext2fs *fs; 259 size_t size; 260 int error = 0, flags, update; 261 mode_t accessmode; 262 263 if (*data_len < sizeof *args) 264 return EINVAL; 265 266 if (mp->mnt_flag & MNT_GETARGS) { 267 ump = VFSTOUFS(mp); 268 if (ump == NULL) 269 return EIO; 270 memset(args, 0, sizeof *args); 271 args->fspec = NULL; 272 *data_len = sizeof *args; 273 return 0; 274 } 275 276 update = mp->mnt_flag & MNT_UPDATE; 277 278 /* Check arguments */ 279 if (args->fspec != NULL) { 280 /* 281 * Look up the name and verify that it's sane. 282 */ 283 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, args->fspec); 284 if ((error = namei(&nd)) != 0) 285 return (error); 286 devvp = nd.ni_vp; 287 288 if (!update) { 289 /* 290 * Be sure this is a valid block device 291 */ 292 if (devvp->v_type != VBLK) 293 error = ENOTBLK; 294 else if (bdevsw_lookup(devvp->v_rdev) == NULL) 295 error = ENXIO; 296 } else { 297 /* 298 * Be sure we're still naming the same device 299 * used for our initial mount 300 */ 301 ump = VFSTOUFS(mp); 302 if (devvp != ump->um_devvp) 303 error = EINVAL; 304 } 305 } else { 306 if (!update) { 307 /* New mounts must have a filename for the device */ 308 return (EINVAL); 309 } else { 310 ump = VFSTOUFS(mp); 311 devvp = ump->um_devvp; 312 vref(devvp); 313 } 314 } 315 316 /* 317 * If mount by non-root, then verify that user has necessary 318 * permissions on the device. 319 */ 320 if (error == 0 && kauth_authorize_generic(l->l_cred, 321 KAUTH_GENERIC_ISSUSER, NULL) != 0) { 322 accessmode = VREAD; 323 if (update ? 324 (mp->mnt_iflag & IMNT_WANTRDWR) != 0 : 325 (mp->mnt_flag & MNT_RDONLY) == 0) 326 accessmode |= VWRITE; 327 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 328 error = VOP_ACCESS(devvp, accessmode, l->l_cred); 329 VOP_UNLOCK(devvp, 0); 330 } 331 332 if (error) { 333 vrele(devvp); 334 return (error); 335 } 336 337 if (!update) { 338 int xflags; 339 340 /* 341 * Disallow multiple mounts of the same device. 342 * Disallow mounting of a device that is currently in use 343 * (except for root, which might share swap device for 344 * miniroot). 345 */ 346 error = vfs_mountedon(devvp); 347 if (error) 348 goto fail; 349 if (vcount(devvp) > 1 && devvp != rootvp) { 350 error = EBUSY; 351 goto fail; 352 } 353 if (mp->mnt_flag & MNT_RDONLY) 354 xflags = FREAD; 355 else 356 xflags = FREAD|FWRITE; 357 error = VOP_OPEN(devvp, xflags, FSCRED); 358 if (error) 359 goto fail; 360 error = ext2fs_mountfs(devvp, mp); 361 if (error) { 362 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 363 (void)VOP_CLOSE(devvp, xflags, NOCRED); 364 VOP_UNLOCK(devvp, 0); 365 goto fail; 366 } 367 368 ump = VFSTOUFS(mp); 369 fs = ump->um_e2fs; 370 } else { 371 /* 372 * Update the mount. 373 */ 374 375 /* 376 * The initial mount got a reference on this 377 * device, so drop the one obtained via 378 * namei(), above. 379 */ 380 vrele(devvp); 381 382 ump = VFSTOUFS(mp); 383 fs = ump->um_e2fs; 384 if (fs->e2fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { 385 /* 386 * Changing from r/w to r/o 387 */ 388 flags = WRITECLOSE; 389 if (mp->mnt_flag & MNT_FORCE) 390 flags |= FORCECLOSE; 391 error = ext2fs_flushfiles(mp, flags); 392 if (error == 0 && 393 ext2fs_cgupdate(ump, MNT_WAIT) == 0 && 394 (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) { 395 fs->e2fs.e2fs_state = E2FS_ISCLEAN; 396 (void) ext2fs_sbupdate(ump, MNT_WAIT); 397 } 398 if (error) 399 return (error); 400 fs->e2fs_ronly = 1; 401 } 402 403 if (mp->mnt_flag & MNT_RELOAD) { 404 error = ext2fs_reload(mp, l->l_cred); 405 if (error) 406 return (error); 407 } 408 409 if (fs->e2fs_ronly && (mp->mnt_iflag & IMNT_WANTRDWR)) { 410 /* 411 * Changing from read-only to read/write 412 */ 413 fs->e2fs_ronly = 0; 414 if (fs->e2fs.e2fs_state == E2FS_ISCLEAN) 415 fs->e2fs.e2fs_state = 0; 416 else 417 fs->e2fs.e2fs_state = E2FS_ERRORS; 418 fs->e2fs_fmod = 1; 419 } 420 if (args->fspec == NULL) 421 return EINVAL; 422 } 423 424 error = set_statvfs_info(path, UIO_USERSPACE, args->fspec, 425 UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l); 426 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs_fsmnt, 427 sizeof(fs->e2fs_fsmnt) - 1, &size); 428 memset(fs->e2fs_fsmnt + size, 0, sizeof(fs->e2fs_fsmnt) - size); 429 if (fs->e2fs.e2fs_rev > E2FS_REV0) { 430 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs.e2fs_fsmnt, 431 sizeof(fs->e2fs.e2fs_fsmnt) - 1, &size); 432 memset(fs->e2fs.e2fs_fsmnt, 0, 433 sizeof(fs->e2fs.e2fs_fsmnt) - size); 434 } 435 if (fs->e2fs_fmod != 0) { /* XXX */ 436 fs->e2fs_fmod = 0; 437 if (fs->e2fs.e2fs_state == 0) 438 fs->e2fs.e2fs_wtime = time_second; 439 else 440 printf("%s: file system not clean; please fsck(8)\n", 441 mp->mnt_stat.f_mntfromname); 442 (void) ext2fs_cgupdate(ump, MNT_WAIT); 443 } 444 return (error); 445 446 fail: 447 vrele(devvp); 448 return (error); 449 } 450 451 /* 452 * Reload all incore data for a filesystem (used after running fsck on 453 * the root filesystem and finding things to fix). The filesystem must 454 * be mounted read-only. 455 * 456 * Things to do to update the mount: 457 * 1) invalidate all cached meta-data. 458 * 2) re-read superblock from disk. 459 * 3) re-read summary information from disk. 460 * 4) invalidate all inactive vnodes. 461 * 5) invalidate all cached file data. 462 * 6) re-read inode data for all active vnodes. 463 */ 464 int 465 ext2fs_reload(struct mount *mountp, kauth_cred_t cred) 466 { 467 struct lwp *l = curlwp; 468 struct vnode *vp, *nvp, *devvp; 469 struct inode *ip; 470 struct buf *bp; 471 struct m_ext2fs *fs; 472 struct ext2fs *newfs; 473 struct partinfo dpart; 474 int i, size, error; 475 void *cp; 476 477 if ((mountp->mnt_flag & MNT_RDONLY) == 0) 478 return (EINVAL); 479 /* 480 * Step 1: invalidate all cached meta-data. 481 */ 482 devvp = VFSTOUFS(mountp)->um_devvp; 483 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 484 error = vinvalbuf(devvp, 0, cred, l, 0, 0); 485 VOP_UNLOCK(devvp, 0); 486 if (error) 487 panic("ext2fs_reload: dirty1"); 488 /* 489 * Step 2: re-read superblock from disk. 490 */ 491 if (VOP_IOCTL(devvp, DIOCGPART, &dpart, FREAD, NOCRED) != 0) 492 size = DEV_BSIZE; 493 else 494 size = dpart.disklab->d_secsize; 495 error = bread(devvp, (daddr_t)(SBOFF / size), SBSIZE, NOCRED, &bp); 496 if (error) { 497 brelse(bp, 0); 498 return (error); 499 } 500 newfs = (struct ext2fs *)bp->b_data; 501 error = ext2fs_checksb(newfs, (mountp->mnt_flag & MNT_RDONLY) != 0); 502 if (error) { 503 brelse(bp, 0); 504 return (error); 505 } 506 507 fs = VFSTOUFS(mountp)->um_e2fs; 508 /* 509 * copy in new superblock, and compute in-memory values 510 */ 511 e2fs_sbload(newfs, &fs->e2fs); 512 fs->e2fs_ncg = 513 howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock, 514 fs->e2fs.e2fs_bpg); 515 /* XXX assume hw bsize = 512 */ 516 fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1; 517 fs->e2fs_bsize = MINBSIZE << fs->e2fs.e2fs_log_bsize; 518 fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize; 519 fs->e2fs_qbmask = fs->e2fs_bsize - 1; 520 fs->e2fs_bmask = ~fs->e2fs_qbmask; 521 fs->e2fs_ngdb = 522 howmany(fs->e2fs_ncg, fs->e2fs_bsize / sizeof(struct ext2_gd)); 523 fs->e2fs_ipb = fs->e2fs_bsize / EXT2_DINODE_SIZE; 524 fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb; 525 526 /* 527 * Step 3: re-read summary information from disk. 528 */ 529 530 for (i = 0; i < fs->e2fs_ngdb; i++) { 531 error = bread(devvp , 532 fsbtodb(fs, fs->e2fs.e2fs_first_dblock + 533 1 /* superblock */ + i), 534 fs->e2fs_bsize, NOCRED, &bp); 535 if (error) { 536 brelse(bp, 0); 537 return (error); 538 } 539 e2fs_cgload((struct ext2_gd *)bp->b_data, 540 &fs->e2fs_gd[i * fs->e2fs_bsize / sizeof(struct ext2_gd)], 541 fs->e2fs_bsize); 542 brelse(bp, 0); 543 } 544 545 loop: 546 /* 547 * NOTE: not using the TAILQ_FOREACH here since in this loop vgone() 548 * and vclean() can be called indirectly 549 */ 550 simple_lock(&mntvnode_slock); 551 for (vp = TAILQ_FIRST(&mountp->mnt_vnodelist); vp; vp = nvp) { 552 if (vp->v_mount != mountp) { 553 simple_unlock(&mntvnode_slock); 554 goto loop; 555 } 556 /* 557 * Step 4: invalidate all inactive vnodes. 558 */ 559 if (vrecycle(vp, &mntvnode_slock, l)) 560 goto loop; 561 /* 562 * Step 5: invalidate all cached file data. 563 */ 564 simple_lock(&vp->v_interlock); 565 nvp = TAILQ_NEXT(vp, v_mntvnodes); 566 simple_unlock(&mntvnode_slock); 567 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) 568 goto loop; 569 if (vinvalbuf(vp, 0, cred, l, 0, 0)) 570 panic("ext2fs_reload: dirty2"); 571 /* 572 * Step 6: re-read inode data for all active vnodes. 573 */ 574 ip = VTOI(vp); 575 error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 576 (int)fs->e2fs_bsize, NOCRED, &bp); 577 if (error) { 578 vput(vp); 579 return (error); 580 } 581 cp = (char *)bp->b_data + 582 (ino_to_fsbo(fs, ip->i_number) * EXT2_DINODE_SIZE); 583 e2fs_iload((struct ext2fs_dinode *)cp, ip->i_din.e2fs_din); 584 brelse(bp, 0); 585 vput(vp); 586 simple_lock(&mntvnode_slock); 587 } 588 simple_unlock(&mntvnode_slock); 589 return (0); 590 } 591 592 /* 593 * Common code for mount and mountroot 594 */ 595 int 596 ext2fs_mountfs(struct vnode *devvp, struct mount *mp) 597 { 598 struct lwp *l = curlwp; 599 struct ufsmount *ump; 600 struct buf *bp; 601 struct ext2fs *fs; 602 struct m_ext2fs *m_fs; 603 dev_t dev; 604 struct partinfo dpart; 605 int error, i, size, ronly; 606 kauth_cred_t cred; 607 struct proc *p; 608 609 dev = devvp->v_rdev; 610 p = l ? l->l_proc : NULL; 611 cred = l ? l->l_cred : NOCRED; 612 613 /* Flush out any old buffers remaining from a previous use. */ 614 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 615 error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0); 616 VOP_UNLOCK(devvp, 0); 617 if (error) 618 return (error); 619 620 ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 621 if (VOP_IOCTL(devvp, DIOCGPART, &dpart, FREAD, cred) != 0) 622 size = DEV_BSIZE; 623 else 624 size = dpart.disklab->d_secsize; 625 626 bp = NULL; 627 ump = NULL; 628 629 #ifdef DEBUG_EXT2 630 printf("sb size: %d ino size %d\n", sizeof(struct ext2fs), 631 EXT2_DINODE_SIZE); 632 #endif 633 error = bread(devvp, (SBOFF / size), SBSIZE, cred, &bp); 634 if (error) 635 goto out; 636 fs = (struct ext2fs *)bp->b_data; 637 error = ext2fs_checksb(fs, ronly); 638 if (error) 639 goto out; 640 ump = malloc(sizeof(*ump), M_UFSMNT, M_WAITOK); 641 memset(ump, 0, sizeof(*ump)); 642 ump->um_fstype = UFS1; 643 ump->um_ops = &ext2fs_ufsops; 644 ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT, M_WAITOK); 645 memset(ump->um_e2fs, 0, sizeof(struct m_ext2fs)); 646 e2fs_sbload((struct ext2fs *)bp->b_data, &ump->um_e2fs->e2fs); 647 brelse(bp, 0); 648 bp = NULL; 649 m_fs = ump->um_e2fs; 650 m_fs->e2fs_ronly = ronly; 651 if (ronly == 0) { 652 if (m_fs->e2fs.e2fs_state == E2FS_ISCLEAN) 653 m_fs->e2fs.e2fs_state = 0; 654 else 655 m_fs->e2fs.e2fs_state = E2FS_ERRORS; 656 m_fs->e2fs_fmod = 1; 657 } 658 659 /* compute dynamic sb infos */ 660 m_fs->e2fs_ncg = 661 howmany(m_fs->e2fs.e2fs_bcount - m_fs->e2fs.e2fs_first_dblock, 662 m_fs->e2fs.e2fs_bpg); 663 /* XXX assume hw bsize = 512 */ 664 m_fs->e2fs_fsbtodb = m_fs->e2fs.e2fs_log_bsize + 1; 665 m_fs->e2fs_bsize = MINBSIZE << m_fs->e2fs.e2fs_log_bsize; 666 m_fs->e2fs_bshift = LOG_MINBSIZE + m_fs->e2fs.e2fs_log_bsize; 667 m_fs->e2fs_qbmask = m_fs->e2fs_bsize - 1; 668 m_fs->e2fs_bmask = ~m_fs->e2fs_qbmask; 669 m_fs->e2fs_ngdb = 670 howmany(m_fs->e2fs_ncg, m_fs->e2fs_bsize / sizeof(struct ext2_gd)); 671 m_fs->e2fs_ipb = m_fs->e2fs_bsize / EXT2_DINODE_SIZE; 672 m_fs->e2fs_itpg = m_fs->e2fs.e2fs_ipg / m_fs->e2fs_ipb; 673 674 m_fs->e2fs_gd = malloc(m_fs->e2fs_ngdb * m_fs->e2fs_bsize, 675 M_UFSMNT, M_WAITOK); 676 for (i = 0; i < m_fs->e2fs_ngdb; i++) { 677 error = bread(devvp , 678 fsbtodb(m_fs, m_fs->e2fs.e2fs_first_dblock + 679 1 /* superblock */ + i), 680 m_fs->e2fs_bsize, NOCRED, &bp); 681 if (error) { 682 free(m_fs->e2fs_gd, M_UFSMNT); 683 goto out; 684 } 685 e2fs_cgload((struct ext2_gd *)bp->b_data, 686 &m_fs->e2fs_gd[ 687 i * m_fs->e2fs_bsize / sizeof(struct ext2_gd)], 688 m_fs->e2fs_bsize); 689 brelse(bp, 0); 690 bp = NULL; 691 } 692 693 mp->mnt_data = ump; 694 mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev; 695 mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_EXT2FS); 696 mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; 697 mp->mnt_stat.f_namemax = EXT2FS_MAXNAMLEN; 698 mp->mnt_flag |= MNT_LOCAL; 699 mp->mnt_dev_bshift = DEV_BSHIFT; /* XXX */ 700 mp->mnt_fs_bshift = m_fs->e2fs_bshift; 701 mp->mnt_iflag |= IMNT_DTYPE; 702 ump->um_flags = 0; 703 ump->um_mountp = mp; 704 ump->um_dev = dev; 705 ump->um_devvp = devvp; 706 ump->um_nindir = NINDIR(m_fs); 707 ump->um_lognindir = ffs(NINDIR(m_fs)) - 1; 708 ump->um_bptrtodb = m_fs->e2fs_fsbtodb; 709 ump->um_seqinc = 1; /* no frags */ 710 ump->um_maxsymlinklen = EXT2_MAXSYMLINKLEN; 711 ump->um_dirblksiz = m_fs->e2fs_bsize; 712 ump->um_maxfilesize = ((uint64_t)0x80000000 * m_fs->e2fs_bsize - 1); 713 devvp->v_specmountpoint = mp; 714 return (0); 715 716 out: 717 KASSERT(bp != NULL); 718 brelse(bp, 0); 719 if (ump) { 720 free(ump->um_e2fs, M_UFSMNT); 721 free(ump, M_UFSMNT); 722 mp->mnt_data = NULL; 723 } 724 return (error); 725 } 726 727 /* 728 * unmount system call 729 */ 730 int 731 ext2fs_unmount(struct mount *mp, int mntflags) 732 { 733 struct ufsmount *ump; 734 struct m_ext2fs *fs; 735 int error, flags; 736 737 flags = 0; 738 if (mntflags & MNT_FORCE) 739 flags |= FORCECLOSE; 740 if ((error = ext2fs_flushfiles(mp, flags)) != 0) 741 return (error); 742 ump = VFSTOUFS(mp); 743 fs = ump->um_e2fs; 744 if (fs->e2fs_ronly == 0 && 745 ext2fs_cgupdate(ump, MNT_WAIT) == 0 && 746 (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) { 747 fs->e2fs.e2fs_state = E2FS_ISCLEAN; 748 (void) ext2fs_sbupdate(ump, MNT_WAIT); 749 } 750 if (ump->um_devvp->v_type != VBAD) 751 ump->um_devvp->v_specmountpoint = NULL; 752 vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); 753 error = VOP_CLOSE(ump->um_devvp, fs->e2fs_ronly ? FREAD : FREAD|FWRITE, 754 NOCRED); 755 vput(ump->um_devvp); 756 free(fs->e2fs_gd, M_UFSMNT); 757 free(fs, M_UFSMNT); 758 free(ump, M_UFSMNT); 759 mp->mnt_data = NULL; 760 mp->mnt_flag &= ~MNT_LOCAL; 761 return (error); 762 } 763 764 /* 765 * Flush out all the files in a filesystem. 766 */ 767 int 768 ext2fs_flushfiles(struct mount *mp, int flags) 769 { 770 extern int doforce; 771 int error; 772 773 if (!doforce) 774 flags &= ~FORCECLOSE; 775 error = vflush(mp, NULLVP, flags); 776 return (error); 777 } 778 779 /* 780 * Get file system statistics. 781 */ 782 int 783 ext2fs_statvfs(struct mount *mp, struct statvfs *sbp) 784 { 785 struct ufsmount *ump; 786 struct m_ext2fs *fs; 787 uint32_t overhead, overhead_per_group, ngdb; 788 int i, ngroups; 789 790 ump = VFSTOUFS(mp); 791 fs = ump->um_e2fs; 792 if (fs->e2fs.e2fs_magic != E2FS_MAGIC) 793 panic("ext2fs_statvfs"); 794 795 /* 796 * Compute the overhead (FS structures) 797 */ 798 overhead_per_group = 799 1 /* block bitmap */ + 800 1 /* inode bitmap */ + 801 fs->e2fs_itpg; 802 overhead = fs->e2fs.e2fs_first_dblock + 803 fs->e2fs_ncg * overhead_per_group; 804 if (fs->e2fs.e2fs_rev > E2FS_REV0 && 805 fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSESUPER) { 806 for (i = 0, ngroups = 0; i < fs->e2fs_ncg; i++) { 807 if (cg_has_sb(i)) 808 ngroups++; 809 } 810 } else { 811 ngroups = fs->e2fs_ncg; 812 } 813 ngdb = fs->e2fs_ngdb; 814 if (fs->e2fs.e2fs_rev > E2FS_REV0 && 815 fs->e2fs.e2fs_features_compat & EXT2F_COMPAT_RESIZE) 816 ngdb += fs->e2fs.e2fs_reserved_ngdb; 817 overhead += ngroups * (1 /* superblock */ + ngdb); 818 819 sbp->f_bsize = fs->e2fs_bsize; 820 sbp->f_frsize = MINBSIZE << fs->e2fs.e2fs_fsize; 821 sbp->f_iosize = fs->e2fs_bsize; 822 sbp->f_blocks = fs->e2fs.e2fs_bcount - overhead; 823 sbp->f_bfree = fs->e2fs.e2fs_fbcount; 824 sbp->f_bresvd = fs->e2fs.e2fs_rbcount; 825 if (sbp->f_bfree > sbp->f_bresvd) 826 sbp->f_bavail = sbp->f_bfree - sbp->f_bresvd; 827 else 828 sbp->f_bavail = 0; 829 sbp->f_files = fs->e2fs.e2fs_icount; 830 sbp->f_ffree = fs->e2fs.e2fs_ficount; 831 sbp->f_favail = fs->e2fs.e2fs_ficount; 832 sbp->f_fresvd = 0; 833 copy_statvfs_info(sbp, mp); 834 return (0); 835 } 836 837 /* 838 * Go through the disk queues to initiate sandbagged IO; 839 * go through the inodes to write those that have been modified; 840 * initiate the writing of the super block if it has been modified. 841 * 842 * Note: we are always called with the filesystem marked `MPBUSY'. 843 */ 844 int 845 ext2fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 846 { 847 struct vnode *vp, *nvp; 848 struct inode *ip; 849 struct ufsmount *ump = VFSTOUFS(mp); 850 struct m_ext2fs *fs; 851 int error, allerror = 0; 852 853 fs = ump->um_e2fs; 854 if (fs->e2fs_fmod != 0 && fs->e2fs_ronly != 0) { /* XXX */ 855 printf("fs = %s\n", fs->e2fs_fsmnt); 856 panic("update: rofs mod"); 857 } 858 /* 859 * Write back each (modified) inode. 860 */ 861 simple_lock(&mntvnode_slock); 862 loop: 863 /* 864 * NOTE: not using the TAILQ_FOREACH here since in this loop vgone() 865 * and vclean() can be called indirectly 866 */ 867 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) { 868 /* 869 * If the vnode that we are about to sync is no longer 870 * associated with this mount point, start over. 871 */ 872 if (vp->v_mount != mp) 873 goto loop; 874 simple_lock(&vp->v_interlock); 875 nvp = TAILQ_NEXT(vp, v_mntvnodes); 876 ip = VTOI(vp); 877 if (vp->v_type == VNON || 878 ((ip->i_flag & 879 (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) == 0 && 880 LIST_EMPTY(&vp->v_dirtyblkhd) && 881 UVM_OBJ_IS_CLEAN(&vp->v_uobj))) 882 { 883 simple_unlock(&vp->v_interlock); 884 continue; 885 } 886 simple_unlock(&mntvnode_slock); 887 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK); 888 if (error) { 889 simple_lock(&mntvnode_slock); 890 if (error == ENOENT) 891 goto loop; 892 continue; 893 } 894 if (vp->v_type == VREG && waitfor == MNT_LAZY) 895 error = ext2fs_update(vp, NULL, NULL, 0); 896 else 897 error = VOP_FSYNC(vp, cred, 898 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0); 899 if (error) 900 allerror = error; 901 vput(vp); 902 simple_lock(&mntvnode_slock); 903 } 904 simple_unlock(&mntvnode_slock); 905 /* 906 * Force stale file system control information to be flushed. 907 */ 908 if (waitfor != MNT_LAZY) { 909 vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); 910 if ((error = VOP_FSYNC(ump->um_devvp, cred, 911 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0)) != 0) 912 allerror = error; 913 VOP_UNLOCK(ump->um_devvp, 0); 914 } 915 /* 916 * Write back modified superblock. 917 */ 918 if (fs->e2fs_fmod != 0) { 919 fs->e2fs_fmod = 0; 920 fs->e2fs.e2fs_wtime = time_second; 921 if ((error = ext2fs_cgupdate(ump, waitfor))) 922 allerror = error; 923 } 924 return (allerror); 925 } 926 927 /* 928 * Look up a EXT2FS dinode number to find its incore vnode, otherwise read it 929 * in from disk. If it is in core, wait for the lock bit to clear, then 930 * return the inode locked. Detection and handling of mount points must be 931 * done by the calling routine. 932 */ 933 int 934 ext2fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) 935 { 936 struct m_ext2fs *fs; 937 struct inode *ip; 938 struct ufsmount *ump; 939 struct buf *bp; 940 struct vnode *vp; 941 dev_t dev; 942 int error; 943 void *cp; 944 945 ump = VFSTOUFS(mp); 946 dev = ump->um_dev; 947 retry: 948 if ((*vpp = ufs_ihashget(dev, ino, LK_EXCLUSIVE)) != NULL) 949 return (0); 950 951 /* Allocate a new vnode/inode. */ 952 if ((error = getnewvnode(VT_EXT2FS, mp, ext2fs_vnodeop_p, &vp)) != 0) { 953 *vpp = NULL; 954 return (error); 955 } 956 ip = pool_get(&ext2fs_inode_pool, PR_WAITOK); 957 958 mutex_enter(&ufs_hashlock); 959 if ((*vpp = ufs_ihashget(dev, ino, 0)) != NULL) { 960 mutex_exit(&ufs_hashlock); 961 ungetnewvnode(vp); 962 pool_put(&ext2fs_inode_pool, ip); 963 goto retry; 964 } 965 966 vp->v_vflag |= VV_LOCKSWORK; 967 968 memset(ip, 0, sizeof(struct inode)); 969 vp->v_data = ip; 970 ip->i_vnode = vp; 971 ip->i_ump = ump; 972 ip->i_e2fs = fs = ump->um_e2fs; 973 ip->i_dev = dev; 974 ip->i_number = ino; 975 ip->i_e2fs_last_lblk = 0; 976 ip->i_e2fs_last_blk = 0; 977 978 /* 979 * Put it onto its hash chain and lock it so that other requests for 980 * this inode will block if they arrive while we are sleeping waiting 981 * for old data structures to be purged or for the contents of the 982 * disk portion of this inode to be read. 983 */ 984 985 ufs_ihashins(ip); 986 mutex_exit(&ufs_hashlock); 987 988 /* Read in the disk contents for the inode, copy into the inode. */ 989 error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), 990 (int)fs->e2fs_bsize, NOCRED, &bp); 991 if (error) { 992 993 /* 994 * The inode does not contain anything useful, so it would 995 * be misleading to leave it on its hash chain. With mode 996 * still zero, it will be unlinked and returned to the free 997 * list by vput(). 998 */ 999 1000 vput(vp); 1001 brelse(bp, 0); 1002 *vpp = NULL; 1003 return (error); 1004 } 1005 cp = (char *)bp->b_data + (ino_to_fsbo(fs, ino) * EXT2_DINODE_SIZE); 1006 ip->i_din.e2fs_din = pool_get(&ext2fs_dinode_pool, PR_WAITOK); 1007 e2fs_iload((struct ext2fs_dinode *)cp, ip->i_din.e2fs_din); 1008 brelse(bp, 0); 1009 1010 /* If the inode was deleted, reset all fields */ 1011 if (ip->i_e2fs_dtime != 0) { 1012 ip->i_e2fs_mode = ip->i_e2fs_nblock = 0; 1013 (void)ext2fs_setsize(ip, 0); 1014 memset(ip->i_e2fs_blocks, 0, sizeof(ip->i_e2fs_blocks)); 1015 } 1016 1017 /* 1018 * Initialize the vnode from the inode, check for aliases. 1019 * Note that the underlying vnode may have changed. 1020 */ 1021 1022 error = ext2fs_vinit(mp, ext2fs_specop_p, ext2fs_fifoop_p, &vp); 1023 if (error) { 1024 vput(vp); 1025 *vpp = NULL; 1026 return (error); 1027 } 1028 /* 1029 * Finish inode initialization now that aliasing has been resolved. 1030 */ 1031 1032 genfs_node_init(vp, &ext2fs_genfsops); 1033 ip->i_devvp = ump->um_devvp; 1034 VREF(ip->i_devvp); 1035 1036 /* 1037 * Set up a generation number for this inode if it does not 1038 * already have one. This should only happen on old filesystems. 1039 */ 1040 1041 if (ip->i_e2fs_gen == 0) { 1042 if (++ext2gennumber < (u_long)time_second) 1043 ext2gennumber = time_second; 1044 ip->i_e2fs_gen = ext2gennumber; 1045 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) 1046 ip->i_flag |= IN_MODIFIED; 1047 } 1048 uvm_vnp_setsize(vp, ext2fs_size(ip)); 1049 *vpp = vp; 1050 return (0); 1051 } 1052 1053 /* 1054 * File handle to vnode 1055 * 1056 * Have to be really careful about stale file handles: 1057 * - check that the inode number is valid 1058 * - call ext2fs_vget() to get the locked inode 1059 * - check for an unallocated inode (i_mode == 0) 1060 */ 1061 int 1062 ext2fs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) 1063 { 1064 struct inode *ip; 1065 struct vnode *nvp; 1066 int error; 1067 struct ufid ufh; 1068 struct m_ext2fs *fs; 1069 1070 if (fhp->fid_len != sizeof(struct ufid)) 1071 return EINVAL; 1072 1073 memcpy(&ufh, fhp, sizeof(struct ufid)); 1074 fs = VFSTOUFS(mp)->um_e2fs; 1075 if ((ufh.ufid_ino < EXT2_FIRSTINO && ufh.ufid_ino != EXT2_ROOTINO) || 1076 ufh.ufid_ino >= fs->e2fs_ncg * fs->e2fs.e2fs_ipg) 1077 return (ESTALE); 1078 1079 if ((error = VFS_VGET(mp, ufh.ufid_ino, &nvp)) != 0) { 1080 *vpp = NULLVP; 1081 return (error); 1082 } 1083 ip = VTOI(nvp); 1084 if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0 || 1085 ip->i_e2fs_gen != ufh.ufid_gen) { 1086 vput(nvp); 1087 *vpp = NULLVP; 1088 return (ESTALE); 1089 } 1090 *vpp = nvp; 1091 return (0); 1092 } 1093 1094 /* 1095 * Vnode pointer to File handle 1096 */ 1097 /* ARGSUSED */ 1098 int 1099 ext2fs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) 1100 { 1101 struct inode *ip; 1102 struct ufid ufh; 1103 1104 if (*fh_size < sizeof(struct ufid)) { 1105 *fh_size = sizeof(struct ufid); 1106 return E2BIG; 1107 } 1108 *fh_size = sizeof(struct ufid); 1109 1110 ip = VTOI(vp); 1111 memset(&ufh, 0, sizeof(ufh)); 1112 ufh.ufid_len = sizeof(struct ufid); 1113 ufh.ufid_ino = ip->i_number; 1114 ufh.ufid_gen = ip->i_e2fs_gen; 1115 memcpy(fhp, &ufh, sizeof(ufh)); 1116 return (0); 1117 } 1118 1119 SYSCTL_SETUP(sysctl_vfs_ext2fs_setup, "sysctl vfs.ext2fs subtree setup") 1120 { 1121 1122 sysctl_createv(clog, 0, NULL, NULL, 1123 CTLFLAG_PERMANENT, 1124 CTLTYPE_NODE, "vfs", NULL, 1125 NULL, 0, NULL, 0, 1126 CTL_VFS, CTL_EOL); 1127 sysctl_createv(clog, 0, NULL, NULL, 1128 CTLFLAG_PERMANENT, 1129 CTLTYPE_NODE, "ext2fs", 1130 SYSCTL_DESCR("Linux EXT2FS file system"), 1131 NULL, 0, NULL, 0, 1132 CTL_VFS, 17, CTL_EOL); 1133 /* 1134 * XXX the "17" above could be dynamic, thereby eliminating 1135 * one more instance of the "number to vfs" mapping problem, 1136 * but "17" is the order as taken from sys/mount.h 1137 */ 1138 } 1139 1140 /* 1141 * Write a superblock and associated information back to disk. 1142 */ 1143 int 1144 ext2fs_sbupdate(struct ufsmount *mp, int waitfor) 1145 { 1146 struct m_ext2fs *fs = mp->um_e2fs; 1147 struct buf *bp; 1148 int error = 0; 1149 1150 bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0); 1151 e2fs_sbsave(&fs->e2fs, (struct ext2fs*)bp->b_data); 1152 if (waitfor == MNT_WAIT) 1153 error = bwrite(bp); 1154 else 1155 bawrite(bp); 1156 return (error); 1157 } 1158 1159 int 1160 ext2fs_cgupdate(struct ufsmount *mp, int waitfor) 1161 { 1162 struct m_ext2fs *fs = mp->um_e2fs; 1163 struct buf *bp; 1164 int i, error = 0, allerror = 0; 1165 1166 allerror = ext2fs_sbupdate(mp, waitfor); 1167 for (i = 0; i < fs->e2fs_ngdb; i++) { 1168 bp = getblk(mp->um_devvp, fsbtodb(fs, 1169 fs->e2fs.e2fs_first_dblock + 1170 1 /* superblock */ + i), fs->e2fs_bsize, 0, 0); 1171 e2fs_cgsave(&fs->e2fs_gd[ 1172 i * fs->e2fs_bsize / sizeof(struct ext2_gd)], 1173 (struct ext2_gd *)bp->b_data, fs->e2fs_bsize); 1174 if (waitfor == MNT_WAIT) 1175 error = bwrite(bp); 1176 else 1177 bawrite(bp); 1178 } 1179 1180 if (!allerror && error) 1181 allerror = error; 1182 return (allerror); 1183 } 1184 1185 static int 1186 ext2fs_checksb(struct ext2fs *fs, int ronly) 1187 { 1188 1189 if (fs2h16(fs->e2fs_magic) != E2FS_MAGIC) { 1190 return (EINVAL); /* XXX needs translation */ 1191 } 1192 if (fs2h32(fs->e2fs_rev) > E2FS_REV1) { 1193 #ifdef DIAGNOSTIC 1194 printf("Ext2 fs: unsupported revision number: %x\n", 1195 fs2h32(fs->e2fs_rev)); 1196 #endif 1197 return (EINVAL); /* XXX needs translation */ 1198 } 1199 if (fs2h32(fs->e2fs_log_bsize) > 2) { /* block size = 1024|2048|4096 */ 1200 #ifdef DIAGNOSTIC 1201 printf("Ext2 fs: bad block size: %d " 1202 "(expected <= 2 for ext2 fs)\n", 1203 fs2h32(fs->e2fs_log_bsize)); 1204 #endif 1205 return (EINVAL); /* XXX needs translation */ 1206 } 1207 if (fs2h32(fs->e2fs_rev) > E2FS_REV0) { 1208 if (fs2h32(fs->e2fs_first_ino) != EXT2_FIRSTINO || 1209 fs2h16(fs->e2fs_inode_size) != EXT2_DINODE_SIZE) { 1210 printf("Ext2 fs: unsupported inode size\n"); 1211 return (EINVAL); /* XXX needs translation */ 1212 } 1213 if (fs2h32(fs->e2fs_features_incompat) & 1214 ~EXT2F_INCOMPAT_SUPP) { 1215 printf("Ext2 fs: unsupported optional feature\n"); 1216 return (EINVAL); /* XXX needs translation */ 1217 } 1218 if (!ronly && fs2h32(fs->e2fs_features_rocompat) & 1219 ~EXT2F_ROCOMPAT_SUPP) { 1220 return (EROFS); /* XXX needs translation */ 1221 } 1222 } 1223 return (0); 1224 } 1225