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