1 /* $NetBSD: ext2fs_vfsops.c,v 1.87 2005/07/23 12:18:41 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.87 2005/07/23 12:18:41 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 .gop_markupdate = ufs_gop_markupdate, 151 }; 152 153 /* 154 * XXX Same structure as FFS inodes? Should we share a common pool? 155 */ 156 POOL_INIT(ext2fs_inode_pool, sizeof(struct inode), 0, 0, 0, "ext2fsinopl", 157 &pool_allocator_nointr); 158 POOL_INIT(ext2fs_dinode_pool, sizeof(struct ext2fs_dinode), 0, 0, 0, 159 "ext2dinopl", &pool_allocator_nointr); 160 161 extern u_long ext2gennumber; 162 163 void 164 ext2fs_init() 165 { 166 #ifdef _LKM 167 pool_init(&ext2fs_inode_pool, sizeof(struct inode), 0, 0, 0, 168 "ext2fsinopl", &pool_allocator_nointr); 169 pool_init(&ext2fs_dinode_pool, sizeof(struct ext2fs_dinode), 0, 0, 0, 170 "ext2dinopl", &pool_allocator_nointr); 171 #endif 172 ufs_init(); 173 } 174 175 void 176 ext2fs_reinit() 177 { 178 ufs_reinit(); 179 } 180 181 void 182 ext2fs_done() 183 { 184 ufs_done(); 185 #ifdef _LKM 186 pool_destroy(&ext2fs_inode_pool); 187 pool_destroy(&ext2fs_dinode_pool); 188 #endif 189 } 190 191 /* 192 * Called by main() when ext2fs is going to be mounted as root. 193 * 194 * Name is updated by mount(8) after booting. 195 */ 196 #define ROOTNAME "root_device" 197 198 int 199 ext2fs_mountroot() 200 { 201 extern struct vnode *rootvp; 202 struct m_ext2fs *fs; 203 struct mount *mp; 204 struct proc *p = curproc; /* XXX */ 205 struct ufsmount *ump; 206 int error; 207 208 if (root_device->dv_class != DV_DISK) 209 return (ENODEV); 210 211 if ((error = vfs_rootmountalloc(MOUNT_EXT2FS, "root_device", &mp))) { 212 vrele(rootvp); 213 return (error); 214 } 215 216 if ((error = ext2fs_mountfs(rootvp, mp, p)) != 0) { 217 mp->mnt_op->vfs_refcount--; 218 vfs_unbusy(mp); 219 free(mp, M_MOUNT); 220 return (error); 221 } 222 simple_lock(&mountlist_slock); 223 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); 224 simple_unlock(&mountlist_slock); 225 ump = VFSTOUFS(mp); 226 fs = ump->um_e2fs; 227 memset(fs->e2fs_fsmnt, 0, sizeof(fs->e2fs_fsmnt)); 228 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs_fsmnt, 229 sizeof(fs->e2fs_fsmnt) - 1, 0); 230 if (fs->e2fs.e2fs_rev > E2FS_REV0) { 231 memset(fs->e2fs.e2fs_fsmnt, 0, sizeof(fs->e2fs.e2fs_fsmnt)); 232 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs.e2fs_fsmnt, 233 sizeof(fs->e2fs.e2fs_fsmnt) - 1, 0); 234 } 235 (void)ext2fs_statvfs(mp, &mp->mnt_stat, p); 236 vfs_unbusy(mp); 237 setrootfstime((time_t)fs->e2fs.e2fs_wtime); 238 return (0); 239 } 240 241 /* 242 * VFS Operations. 243 * 244 * mount system call 245 */ 246 int 247 ext2fs_mount(mp, path, data, ndp, p) 248 struct mount *mp; 249 const char *path; 250 void * data; 251 struct nameidata *ndp; 252 struct proc *p; 253 { 254 struct vnode *devvp; 255 struct ufs_args args; 256 struct ufsmount *ump = NULL; 257 struct m_ext2fs *fs; 258 size_t size; 259 int error, flags, update; 260 mode_t accessmode; 261 262 if (mp->mnt_flag & MNT_GETARGS) { 263 ump = VFSTOUFS(mp); 264 if (ump == NULL) 265 return EIO; 266 args.fspec = NULL; 267 vfs_showexport(mp, &args.export, &ump->um_export); 268 return copyout(&args, data, sizeof(args)); 269 } 270 error = copyin(data, &args, sizeof (struct ufs_args)); 271 if (error) 272 return (error); 273 274 update = mp->mnt_flag & MNT_UPDATE; 275 276 /* Check arguments */ 277 if (args.fspec != NULL) { 278 /* 279 * Look up the name and verify that it's sane. 280 */ 281 NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p); 282 if ((error = namei(ndp)) != 0) 283 return (error); 284 devvp = ndp->ni_vp; 285 286 if (!update) { 287 /* 288 * Be sure this is a valid block device 289 */ 290 if (devvp->v_type != VBLK) 291 error = ENOTBLK; 292 else if (bdevsw_lookup(devvp->v_rdev) == NULL) 293 error = ENXIO; 294 } else { 295 /* 296 * Be sure we're still naming the same device 297 * used for our initial mount 298 */ 299 ump = VFSTOUFS(mp); 300 if (devvp != ump->um_devvp) 301 error = EINVAL; 302 } 303 } else { 304 if (!update) { 305 /* New mounts must have a filename for the device */ 306 return (EINVAL); 307 } else { 308 ump = VFSTOUFS(mp); 309 devvp = ump->um_devvp; 310 vref(devvp); 311 } 312 } 313 314 /* 315 * If mount by non-root, then verify that user has necessary 316 * permissions on the device. 317 */ 318 if (error == 0 && p->p_ucred->cr_uid != 0) { 319 accessmode = VREAD; 320 if (update ? 321 (mp->mnt_iflag & IMNT_WANTRDWR) != 0 : 322 (mp->mnt_flag & MNT_RDONLY) == 0) 323 accessmode |= VWRITE; 324 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 325 error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p); 326 VOP_UNLOCK(devvp, 0); 327 } 328 329 if (error) { 330 vrele(devvp); 331 return (error); 332 } 333 334 if (!update) { 335 int xflags; 336 337 /* 338 * Disallow multiple mounts of the same device. 339 * Disallow mounting of a device that is currently in use 340 * (except for root, which might share swap device for 341 * miniroot). 342 */ 343 error = vfs_mountedon(devvp); 344 if (error) 345 goto fail; 346 if (vcount(devvp) > 1 && devvp != rootvp) { 347 error = EBUSY; 348 goto fail; 349 } 350 if (mp->mnt_flag & MNT_RDONLY) 351 xflags = FREAD; 352 else 353 xflags = FREAD|FWRITE; 354 error = VOP_OPEN(devvp, xflags, FSCRED, p); 355 if (error) 356 goto fail; 357 error = ext2fs_mountfs(devvp, mp, p); 358 if (error) { 359 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 360 (void)VOP_CLOSE(devvp, xflags, NOCRED, p); 361 VOP_UNLOCK(devvp, 0); 362 goto fail; 363 } 364 365 ump = VFSTOUFS(mp); 366 fs = ump->um_e2fs; 367 } else { 368 /* 369 * Update the mount. 370 */ 371 372 /* 373 * The initial mount got a reference on this 374 * device, so drop the one obtained via 375 * namei(), above. 376 */ 377 vrele(devvp); 378 379 ump = VFSTOUFS(mp); 380 fs = ump->um_e2fs; 381 if (fs->e2fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { 382 /* 383 * Changing from r/w to r/o 384 */ 385 flags = WRITECLOSE; 386 if (mp->mnt_flag & MNT_FORCE) 387 flags |= FORCECLOSE; 388 error = ext2fs_flushfiles(mp, flags, p); 389 if (error == 0 && 390 ext2fs_cgupdate(ump, MNT_WAIT) == 0 && 391 (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) { 392 fs->e2fs.e2fs_state = E2FS_ISCLEAN; 393 (void) ext2fs_sbupdate(ump, MNT_WAIT); 394 } 395 if (error) 396 return (error); 397 fs->e2fs_ronly = 1; 398 } 399 400 if (mp->mnt_flag & MNT_RELOAD) { 401 error = ext2fs_reload(mp, ndp->ni_cnd.cn_cred, p); 402 if (error) 403 return (error); 404 } 405 406 if (fs->e2fs_ronly && (mp->mnt_iflag & IMNT_WANTRDWR)) { 407 /* 408 * Changing from read-only to read/write 409 */ 410 fs->e2fs_ronly = 0; 411 if (fs->e2fs.e2fs_state == E2FS_ISCLEAN) 412 fs->e2fs.e2fs_state = 0; 413 else 414 fs->e2fs.e2fs_state = E2FS_ERRORS; 415 fs->e2fs_fmod = 1; 416 } 417 if (args.fspec == 0) { 418 /* 419 * Process export requests. 420 */ 421 return (vfs_export(mp, &ump->um_export, &args.export)); 422 } 423 } 424 425 error = set_statvfs_info(path, UIO_USERSPACE, args.fspec, 426 UIO_USERSPACE, mp, p); 427 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs_fsmnt, 428 sizeof(fs->e2fs_fsmnt) - 1, &size); 429 memset(fs->e2fs_fsmnt + size, 0, sizeof(fs->e2fs_fsmnt) - size); 430 if (fs->e2fs.e2fs_rev > E2FS_REV0) { 431 (void) copystr(mp->mnt_stat.f_mntonname, fs->e2fs.e2fs_fsmnt, 432 sizeof(fs->e2fs.e2fs_fsmnt) - 1, &size); 433 memset(fs->e2fs.e2fs_fsmnt, 0, 434 sizeof(fs->e2fs.e2fs_fsmnt) - size); 435 } 436 if (fs->e2fs_fmod != 0) { /* XXX */ 437 fs->e2fs_fmod = 0; 438 if (fs->e2fs.e2fs_state == 0) 439 fs->e2fs.e2fs_wtime = time.tv_sec; 440 else 441 printf("%s: file system not clean; please fsck(8)\n", 442 mp->mnt_stat.f_mntfromname); 443 (void) ext2fs_cgupdate(ump, MNT_WAIT); 444 } 445 return (error); 446 447 fail: 448 vrele(devvp); 449 return (error); 450 } 451 452 /* 453 * Reload all incore data for a filesystem (used after running fsck on 454 * the root filesystem and finding things to fix). The filesystem must 455 * be mounted read-only. 456 * 457 * Things to do to update the mount: 458 * 1) invalidate all cached meta-data. 459 * 2) re-read superblock from disk. 460 * 3) re-read summary information from disk. 461 * 4) invalidate all inactive vnodes. 462 * 5) invalidate all cached file data. 463 * 6) re-read inode data for all active vnodes. 464 */ 465 int 466 ext2fs_reload(mountp, cred, p) 467 struct mount *mountp; 468 struct ucred *cred; 469 struct proc *p; 470 { 471 struct vnode *vp, *nvp, *devvp; 472 struct inode *ip; 473 struct buf *bp; 474 struct m_ext2fs *fs; 475 struct ext2fs *newfs; 476 struct partinfo dpart; 477 int i, size, error; 478 caddr_t cp; 479 480 if ((mountp->mnt_flag & MNT_RDONLY) == 0) 481 return (EINVAL); 482 /* 483 * Step 1: invalidate all cached meta-data. 484 */ 485 devvp = VFSTOUFS(mountp)->um_devvp; 486 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 487 error = vinvalbuf(devvp, 0, cred, p, 0, 0); 488 VOP_UNLOCK(devvp, 0); 489 if (error) 490 panic("ext2fs_reload: dirty1"); 491 /* 492 * Step 2: re-read superblock from disk. 493 */ 494 if (VOP_IOCTL(devvp, DIOCGPART, &dpart, FREAD, NOCRED, p) != 0) 495 size = DEV_BSIZE; 496 else 497 size = dpart.disklab->d_secsize; 498 error = bread(devvp, (daddr_t)(SBOFF / size), SBSIZE, NOCRED, &bp); 499 if (error) { 500 brelse(bp); 501 return (error); 502 } 503 newfs = (struct ext2fs *)bp->b_data; 504 error = ext2fs_checksb(newfs, (mountp->mnt_flag & MNT_RDONLY) != 0); 505 if (error) { 506 brelse(bp); 507 return (error); 508 } 509 510 fs = VFSTOUFS(mountp)->um_e2fs; 511 /* 512 * copy in new superblock, and compute in-memory values 513 */ 514 e2fs_sbload(newfs, &fs->e2fs); 515 fs->e2fs_ncg = 516 howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock, 517 fs->e2fs.e2fs_bpg); 518 /* XXX assume hw bsize = 512 */ 519 fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1; 520 fs->e2fs_bsize = 1024 << fs->e2fs.e2fs_log_bsize; 521 fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize; 522 fs->e2fs_qbmask = fs->e2fs_bsize - 1; 523 fs->e2fs_bmask = ~fs->e2fs_qbmask; 524 fs->e2fs_ngdb = howmany(fs->e2fs_ncg, 525 fs->e2fs_bsize / sizeof(struct ext2_gd)); 526 fs->e2fs_ipb = fs->e2fs_bsize / EXT2_DINODE_SIZE; 527 fs->e2fs_itpg = fs->e2fs.e2fs_ipg/fs->e2fs_ipb; 528 529 /* 530 * Step 3: re-read summary information from disk. 531 */ 532 533 for (i=0; i < fs->e2fs_ngdb; i++) { 534 error = bread(devvp , 535 fsbtodb(fs, ((fs->e2fs_bsize>1024)? 0 : 1) + i + 1), 536 fs->e2fs_bsize, NOCRED, &bp); 537 if (error) { 538 brelse(bp); 539 return (error); 540 } 541 e2fs_cgload((struct ext2_gd*)bp->b_data, 542 &fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)], 543 fs->e2fs_bsize); 544 brelse(bp); 545 } 546 547 loop: 548 simple_lock(&mntvnode_slock); 549 for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { 550 if (vp->v_mount != mountp) { 551 simple_unlock(&mntvnode_slock); 552 goto loop; 553 } 554 nvp = vp->v_mntvnodes.le_next; 555 /* 556 * Step 4: invalidate all inactive vnodes. 557 */ 558 if (vrecycle(vp, &mntvnode_slock, p)) 559 goto loop; 560 /* 561 * Step 5: invalidate all cached file data. 562 */ 563 simple_lock(&vp->v_interlock); 564 simple_unlock(&mntvnode_slock); 565 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) 566 goto loop; 567 if (vinvalbuf(vp, 0, cred, p, 0, 0)) 568 panic("ext2fs_reload: dirty2"); 569 /* 570 * Step 6: re-read inode data for all active vnodes. 571 */ 572 ip = VTOI(vp); 573 error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 574 (int)fs->e2fs_bsize, NOCRED, &bp); 575 if (error) { 576 vput(vp); 577 return (error); 578 } 579 cp = (caddr_t)bp->b_data + 580 (ino_to_fsbo(fs, ip->i_number) * EXT2_DINODE_SIZE); 581 e2fs_iload((struct ext2fs_dinode *)cp, ip->i_din.e2fs_din); 582 brelse(bp); 583 vput(vp); 584 simple_lock(&mntvnode_slock); 585 } 586 simple_unlock(&mntvnode_slock); 587 return (0); 588 } 589 590 /* 591 * Common code for mount and mountroot 592 */ 593 int 594 ext2fs_mountfs(devvp, mp, p) 595 struct vnode *devvp; 596 struct mount *mp; 597 struct proc *p; 598 { 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 struct ucred *cred; 607 608 dev = devvp->v_rdev; 609 cred = p ? p->p_ucred : NOCRED; 610 611 /* Flush out any old buffers remaining from a previous use. */ 612 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); 613 error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0); 614 VOP_UNLOCK(devvp, 0); 615 if (error) 616 return (error); 617 618 ronly = (mp->mnt_flag & MNT_RDONLY) != 0; 619 if (VOP_IOCTL(devvp, DIOCGPART, &dpart, FREAD, cred, p) != 0) 620 size = DEV_BSIZE; 621 else 622 size = dpart.disklab->d_secsize; 623 624 bp = NULL; 625 ump = NULL; 626 627 #ifdef DEBUG_EXT2 628 printf("sb size: %d ino size %d\n", sizeof(struct ext2fs), 629 EXT2_DINODE_SIZE); 630 #endif 631 error = bread(devvp, (SBOFF / size), SBSIZE, cred, &bp); 632 if (error) 633 goto out; 634 fs = (struct ext2fs *)bp->b_data; 635 error = ext2fs_checksb(fs, ronly); 636 if (error) 637 goto out; 638 ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK); 639 memset(ump, 0, sizeof *ump); 640 ump->um_fstype = UFS1; 641 ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT, M_WAITOK); 642 memset(ump->um_e2fs, 0, sizeof(struct m_ext2fs)); 643 e2fs_sbload((struct ext2fs*)bp->b_data, &ump->um_e2fs->e2fs); 644 brelse(bp); 645 bp = NULL; 646 m_fs = ump->um_e2fs; 647 m_fs->e2fs_ronly = ronly; 648 if (ronly == 0) { 649 if (m_fs->e2fs.e2fs_state == E2FS_ISCLEAN) 650 m_fs->e2fs.e2fs_state = 0; 651 else 652 m_fs->e2fs.e2fs_state = E2FS_ERRORS; 653 m_fs->e2fs_fmod = 1; 654 } 655 656 /* compute dynamic sb infos */ 657 m_fs->e2fs_ncg = 658 howmany(m_fs->e2fs.e2fs_bcount - m_fs->e2fs.e2fs_first_dblock, 659 m_fs->e2fs.e2fs_bpg); 660 /* XXX assume hw bsize = 512 */ 661 m_fs->e2fs_fsbtodb = m_fs->e2fs.e2fs_log_bsize + 1; 662 m_fs->e2fs_bsize = 1024 << m_fs->e2fs.e2fs_log_bsize; 663 m_fs->e2fs_bshift = LOG_MINBSIZE + m_fs->e2fs.e2fs_log_bsize; 664 m_fs->e2fs_qbmask = m_fs->e2fs_bsize - 1; 665 m_fs->e2fs_bmask = ~m_fs->e2fs_qbmask; 666 m_fs->e2fs_ngdb = howmany(m_fs->e2fs_ncg, 667 m_fs->e2fs_bsize / sizeof(struct ext2_gd)); 668 m_fs->e2fs_ipb = m_fs->e2fs_bsize / EXT2_DINODE_SIZE; 669 m_fs->e2fs_itpg = m_fs->e2fs.e2fs_ipg/m_fs->e2fs_ipb; 670 671 m_fs->e2fs_gd = malloc(m_fs->e2fs_ngdb * m_fs->e2fs_bsize, 672 M_UFSMNT, M_WAITOK); 673 for (i=0; i < m_fs->e2fs_ngdb; i++) { 674 error = bread(devvp , 675 fsbtodb(m_fs, ((m_fs->e2fs_bsize>1024)? 0 : 1) + i + 1), 676 m_fs->e2fs_bsize, NOCRED, &bp); 677 if (error) { 678 free(m_fs->e2fs_gd, M_UFSMNT); 679 goto out; 680 } 681 e2fs_cgload((struct ext2_gd*)bp->b_data, 682 &m_fs->e2fs_gd[ 683 i * m_fs->e2fs_bsize / sizeof(struct ext2_gd)], 684 m_fs->e2fs_bsize); 685 brelse(bp); 686 bp = NULL; 687 } 688 689 mp->mnt_data = ump; 690 mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev; 691 mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_EXT2FS); 692 mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; 693 mp->mnt_stat.f_namemax = MAXNAMLEN; 694 mp->mnt_flag |= MNT_LOCAL; 695 mp->mnt_dev_bshift = DEV_BSHIFT; /* XXX */ 696 mp->mnt_fs_bshift = m_fs->e2fs_bshift; 697 mp->mnt_iflag |= IMNT_DTYPE; 698 ump->um_flags = 0; 699 ump->um_mountp = mp; 700 ump->um_dev = dev; 701 ump->um_devvp = devvp; 702 ump->um_nindir = NINDIR(m_fs); 703 ump->um_lognindir = ffs(NINDIR(m_fs)) - 1; 704 ump->um_bptrtodb = m_fs->e2fs_fsbtodb; 705 ump->um_seqinc = 1; /* no frags */ 706 ump->um_maxsymlinklen = EXT2_MAXSYMLINKLEN; 707 ump->um_dirblksiz = m_fs->e2fs_bsize; 708 ump->um_maxfilesize = ((u_int64_t)0x80000000 * m_fs->e2fs_bsize - 1); 709 devvp->v_specmountpoint = mp; 710 return (0); 711 712 out: 713 if (bp) 714 brelse(bp); 715 if (ump) { 716 free(ump->um_e2fs, M_UFSMNT); 717 free(ump, M_UFSMNT); 718 mp->mnt_data = NULL; 719 } 720 return (error); 721 } 722 723 /* 724 * unmount system call 725 */ 726 int 727 ext2fs_unmount(mp, mntflags, p) 728 struct mount *mp; 729 int mntflags; 730 struct proc *p; 731 { 732 struct ufsmount *ump; 733 struct m_ext2fs *fs; 734 int error, flags; 735 736 flags = 0; 737 if (mntflags & MNT_FORCE) 738 flags |= FORCECLOSE; 739 if ((error = ext2fs_flushfiles(mp, flags, p)) != 0) 740 return (error); 741 ump = VFSTOUFS(mp); 742 fs = ump->um_e2fs; 743 if (fs->e2fs_ronly == 0 && 744 ext2fs_cgupdate(ump, MNT_WAIT) == 0 && 745 (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) { 746 fs->e2fs.e2fs_state = E2FS_ISCLEAN; 747 (void) ext2fs_sbupdate(ump, MNT_WAIT); 748 } 749 if (ump->um_devvp->v_type != VBAD) 750 ump->um_devvp->v_specmountpoint = NULL; 751 vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY); 752 error = VOP_CLOSE(ump->um_devvp, fs->e2fs_ronly ? FREAD : FREAD|FWRITE, 753 NOCRED, p); 754 vput(ump->um_devvp); 755 free(fs->e2fs_gd, M_UFSMNT); 756 free(fs, M_UFSMNT); 757 free(ump, M_UFSMNT); 758 mp->mnt_data = NULL; 759 mp->mnt_flag &= ~MNT_LOCAL; 760 return (error); 761 } 762 763 /* 764 * Flush out all the files in a filesystem. 765 */ 766 int 767 ext2fs_flushfiles(mp, flags, p) 768 struct mount *mp; 769 int flags; 770 struct proc *p; 771 { 772 extern int doforce; 773 int error; 774 775 if (!doforce) 776 flags &= ~FORCECLOSE; 777 error = vflush(mp, NULLVP, flags); 778 return (error); 779 } 780 781 /* 782 * Get file system statistics. 783 */ 784 int 785 ext2fs_statvfs(mp, sbp, p) 786 struct mount *mp; 787 struct statvfs *sbp; 788 struct proc *p; 789 { 790 struct ufsmount *ump; 791 struct m_ext2fs *fs; 792 u_int32_t overhead, overhead_per_group; 793 int i, ngroups; 794 795 ump = VFSTOUFS(mp); 796 fs = ump->um_e2fs; 797 if (fs->e2fs.e2fs_magic != E2FS_MAGIC) 798 panic("ext2fs_statvfs"); 799 800 /* 801 * Compute the overhead (FS structures) 802 */ 803 overhead_per_group = 1 /* block bitmap */ + 804 1 /* inode bitmap */ + 805 fs->e2fs_itpg; 806 overhead = fs->e2fs.e2fs_first_dblock + 807 fs->e2fs_ncg * overhead_per_group; 808 if (fs->e2fs.e2fs_rev > E2FS_REV0 && 809 fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSESUPER) { 810 for (i = 0, ngroups = 0; i < fs->e2fs_ncg; i++) { 811 if (cg_has_sb(i)) 812 ngroups++; 813 } 814 } else { 815 ngroups = fs->e2fs_ncg; 816 } 817 overhead += ngroups * (1 + fs->e2fs_ngdb); 818 819 sbp->f_bsize = fs->e2fs_bsize; 820 sbp->f_frsize = 1024 << 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(mp, waitfor, cred, p) 846 struct mount *mp; 847 int waitfor; 848 struct ucred *cred; 849 struct proc *p; 850 { 851 struct vnode *vp, *nvp; 852 struct inode *ip; 853 struct ufsmount *ump = VFSTOUFS(mp); 854 struct m_ext2fs *fs; 855 int error, allerror = 0; 856 857 fs = ump->um_e2fs; 858 if (fs->e2fs_fmod != 0 && fs->e2fs_ronly != 0) { /* XXX */ 859 printf("fs = %s\n", fs->e2fs_fsmnt); 860 panic("update: rofs mod"); 861 } 862 /* 863 * Write back each (modified) inode. 864 */ 865 simple_lock(&mntvnode_slock); 866 loop: 867 for (vp = LIST_FIRST(&mp->mnt_vnodelist); vp != NULL; 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 = LIST_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 vp->v_uobj.uo_npages == 0)) 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 = VOP_UPDATE(vp, NULL, NULL, 0); 896 else 897 error = VOP_FSYNC(vp, cred, 898 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0, p); 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, p)) != 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.tv_sec; 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(mp, ino, vpp) 935 struct mount *mp; 936 ino_t ino; 937 struct vnode **vpp; 938 { 939 struct m_ext2fs *fs; 940 struct inode *ip; 941 struct ufsmount *ump; 942 struct buf *bp; 943 struct vnode *vp; 944 dev_t dev; 945 int error; 946 caddr_t cp; 947 948 ump = VFSTOUFS(mp); 949 dev = ump->um_dev; 950 951 if ((*vpp = ufs_ihashget(dev, ino, LK_EXCLUSIVE)) != NULL) 952 return (0); 953 954 /* Allocate a new vnode/inode. */ 955 if ((error = getnewvnode(VT_EXT2FS, mp, ext2fs_vnodeop_p, &vp)) != 0) { 956 *vpp = NULL; 957 return (error); 958 } 959 960 do { 961 if ((*vpp = ufs_ihashget(dev, ino, LK_EXCLUSIVE)) != NULL) { 962 ungetnewvnode(vp); 963 return (0); 964 } 965 } while (lockmgr(&ufs_hashlock, LK_EXCLUSIVE|LK_SLEEPFAIL, 0)); 966 967 vp->v_flag |= VLOCKSWORK; 968 969 ip = pool_get(&ext2fs_inode_pool, PR_WAITOK); 970 memset(ip, 0, sizeof(struct inode)); 971 vp->v_data = ip; 972 ip->i_vnode = vp; 973 ip->i_ump = ump; 974 ip->i_e2fs = fs = ump->um_e2fs; 975 ip->i_dev = dev; 976 ip->i_number = ino; 977 ip->i_e2fs_last_lblk = 0; 978 ip->i_e2fs_last_blk = 0; 979 980 /* 981 * Put it onto its hash chain and lock it so that other requests for 982 * this inode will block if they arrive while we are sleeping waiting 983 * for old data structures to be purged or for the contents of the 984 * disk portion of this inode to be read. 985 */ 986 987 ufs_ihashins(ip); 988 lockmgr(&ufs_hashlock, LK_RELEASE, 0); 989 990 /* Read in the disk contents for the inode, copy into the inode. */ 991 error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)), 992 (int)fs->e2fs_bsize, NOCRED, &bp); 993 if (error) { 994 995 /* 996 * The inode does not contain anything useful, so it would 997 * be misleading to leave it on its hash chain. With mode 998 * still zero, it will be unlinked and returned to the free 999 * list by vput(). 1000 */ 1001 1002 vput(vp); 1003 brelse(bp); 1004 *vpp = NULL; 1005 return (error); 1006 } 1007 cp = (caddr_t)bp->b_data + 1008 (ino_to_fsbo(fs, ino) * EXT2_DINODE_SIZE); 1009 ip->i_din.e2fs_din = pool_get(&ext2fs_dinode_pool, PR_WAITOK); 1010 e2fs_iload((struct ext2fs_dinode *)cp, ip->i_din.e2fs_din); 1011 brelse(bp); 1012 1013 /* If the inode was deleted, reset all fields */ 1014 if (ip->i_e2fs_dtime != 0) { 1015 ip->i_e2fs_mode = ip->i_e2fs_nblock = 0; 1016 (void)ext2fs_setsize(ip, 0); 1017 memset(ip->i_e2fs_blocks, 0, sizeof(ip->i_e2fs_blocks)); 1018 } 1019 1020 /* 1021 * Initialize the vnode from the inode, check for aliases. 1022 * Note that the underlying vnode may have changed. 1023 */ 1024 1025 error = ext2fs_vinit(mp, ext2fs_specop_p, ext2fs_fifoop_p, &vp); 1026 if (error) { 1027 vput(vp); 1028 *vpp = NULL; 1029 return (error); 1030 } 1031 /* 1032 * Finish inode initialization now that aliasing has been resolved. 1033 */ 1034 1035 genfs_node_init(vp, &ext2fs_genfsops); 1036 ip->i_devvp = ump->um_devvp; 1037 VREF(ip->i_devvp); 1038 1039 /* 1040 * Set up a generation number for this inode if it does not 1041 * already have one. This should only happen on old filesystems. 1042 */ 1043 1044 if (ip->i_e2fs_gen == 0) { 1045 if (++ext2gennumber < (u_long)time.tv_sec) 1046 ext2gennumber = time.tv_sec; 1047 ip->i_e2fs_gen = ext2gennumber; 1048 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) 1049 ip->i_flag |= IN_MODIFIED; 1050 } 1051 vp->v_size = ext2fs_size(ip); 1052 *vpp = vp; 1053 return (0); 1054 } 1055 1056 /* 1057 * File handle to vnode 1058 * 1059 * Have to be really careful about stale file handles: 1060 * - check that the inode number is valid 1061 * - call ext2fs_vget() to get the locked inode 1062 * - check for an unallocated inode (i_mode == 0) 1063 */ 1064 int 1065 ext2fs_fhtovp(mp, fhp, vpp) 1066 struct mount *mp; 1067 struct fid *fhp; 1068 struct vnode **vpp; 1069 { 1070 struct inode *ip; 1071 struct vnode *nvp; 1072 int error; 1073 struct ufid *ufhp; 1074 struct m_ext2fs *fs; 1075 1076 ufhp = (struct ufid *)fhp; 1077 fs = VFSTOUFS(mp)->um_e2fs; 1078 if ((ufhp->ufid_ino < EXT2_FIRSTINO && ufhp->ufid_ino != EXT2_ROOTINO) || 1079 ufhp->ufid_ino >= fs->e2fs_ncg * fs->e2fs.e2fs_ipg) 1080 return (ESTALE); 1081 1082 if ((error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) != 0) { 1083 *vpp = NULLVP; 1084 return (error); 1085 } 1086 ip = VTOI(nvp); 1087 if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0 || 1088 ip->i_e2fs_gen != ufhp->ufid_gen) { 1089 vput(nvp); 1090 *vpp = NULLVP; 1091 return (ESTALE); 1092 } 1093 *vpp = nvp; 1094 return (0); 1095 } 1096 1097 /* 1098 * Vnode pointer to File handle 1099 */ 1100 /* ARGSUSED */ 1101 int 1102 ext2fs_vptofh(vp, fhp) 1103 struct vnode *vp; 1104 struct fid *fhp; 1105 { 1106 struct inode *ip; 1107 struct ufid *ufhp; 1108 1109 ip = VTOI(vp); 1110 ufhp = (struct ufid *)fhp; 1111 ufhp->ufid_len = sizeof(struct ufid); 1112 ufhp->ufid_ino = ip->i_number; 1113 ufhp->ufid_gen = ip->i_e2fs_gen; 1114 return (0); 1115 } 1116 1117 SYSCTL_SETUP(sysctl_vfs_ext2fs_setup, "sysctl vfs.ext2fs subtree setup") 1118 { 1119 1120 sysctl_createv(clog, 0, NULL, NULL, 1121 CTLFLAG_PERMANENT, 1122 CTLTYPE_NODE, "vfs", NULL, 1123 NULL, 0, NULL, 0, 1124 CTL_VFS, CTL_EOL); 1125 sysctl_createv(clog, 0, NULL, NULL, 1126 CTLFLAG_PERMANENT, 1127 CTLTYPE_NODE, "ext2fs", 1128 SYSCTL_DESCR("Linux EXT2FS file system"), 1129 NULL, 0, NULL, 0, 1130 CTL_VFS, 17, CTL_EOL); 1131 /* 1132 * XXX the "17" above could be dynamic, thereby eliminating 1133 * one more instance of the "number to vfs" mapping problem, 1134 * but "17" is the order as taken from sys/mount.h 1135 */ 1136 } 1137 1138 /* 1139 * Write a superblock and associated information back to disk. 1140 */ 1141 int 1142 ext2fs_sbupdate(mp, waitfor) 1143 struct ufsmount *mp; 1144 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(mp, waitfor) 1161 struct ufsmount *mp; 1162 int waitfor; 1163 { 1164 struct m_ext2fs *fs = mp->um_e2fs; 1165 struct buf *bp; 1166 int i, error = 0, allerror = 0; 1167 1168 allerror = ext2fs_sbupdate(mp, waitfor); 1169 for (i = 0; i < fs->e2fs_ngdb; i++) { 1170 bp = getblk(mp->um_devvp, fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1), 1171 fs->e2fs_bsize, 0, 0); 1172 e2fs_cgsave(&fs->e2fs_gd[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(fs, ronly) 1187 struct ext2fs *fs; 1188 int ronly; 1189 { 1190 if (fs2h16(fs->e2fs_magic) != E2FS_MAGIC) { 1191 return (EINVAL); /* XXX needs translation */ 1192 } 1193 if (fs2h32(fs->e2fs_rev) > E2FS_REV1) { 1194 #ifdef DIAGNOSTIC 1195 printf("Ext2 fs: unsupported revision number: %x\n", 1196 fs2h32(fs->e2fs_rev)); 1197 #endif 1198 return (EINVAL); /* XXX needs translation */ 1199 } 1200 if (fs2h32(fs->e2fs_log_bsize) > 2) { /* block size = 1024|2048|4096 */ 1201 #ifdef DIAGNOSTIC 1202 printf("Ext2 fs: bad block size: %d (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