Line data Source code
1 : /* $OpenBSD: ext2fs_vfsops.c,v 1.109 2018/05/29 14:29:52 visa Exp $ */
2 : /* $NetBSD: ext2fs_vfsops.c,v 1.1 1997/06/11 09:34:07 bouyer Exp $ */
3 :
4 : /*
5 : * Copyright (c) 1997 Manuel Bouyer.
6 : * Copyright (c) 1989, 1991, 1993, 1994
7 : * The Regents of the University of California. All rights reserved.
8 : *
9 : * Redistribution and use in source and binary forms, with or without
10 : * modification, are permitted provided that the following conditions
11 : * are met:
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : * 3. Neither the name of the University nor the names of its contributors
18 : * may be used to endorse or promote products derived from this software
19 : * without specific prior written permission.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 : * SUCH DAMAGE.
32 : *
33 : * @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94
34 : * Modified for ext2fs by Manuel Bouyer.
35 : */
36 :
37 : #include <sys/param.h>
38 : #include <sys/systm.h>
39 : #include <sys/namei.h>
40 : #include <sys/proc.h>
41 : #include <sys/kernel.h>
42 : #include <sys/vnode.h>
43 : #include <sys/socket.h>
44 : #include <sys/mount.h>
45 : #include <sys/buf.h>
46 : #include <sys/disk.h>
47 : #include <sys/mbuf.h>
48 : #include <sys/fcntl.h>
49 : #include <sys/disklabel.h>
50 : #include <sys/ioctl.h>
51 : #include <sys/errno.h>
52 : #include <sys/malloc.h>
53 : #include <sys/pool.h>
54 : #include <sys/lock.h>
55 : #include <sys/dkio.h>
56 : #include <sys/specdev.h>
57 :
58 : #include <ufs/ufs/quota.h>
59 : #include <ufs/ufs/ufsmount.h>
60 : #include <ufs/ufs/inode.h>
61 : #include <ufs/ufs/dir.h>
62 : #include <ufs/ufs/ufs_extern.h>
63 :
64 : #include <ufs/ext2fs/ext2fs.h>
65 : #include <ufs/ext2fs/ext2fs_extern.h>
66 :
67 : extern struct lock ufs_hashlock;
68 :
69 : int ext2fs_sbupdate(struct ufsmount *, int);
70 : static int e2fs_sbcheck(struct ext2fs *, int);
71 :
72 : const struct vfsops ext2fs_vfsops = {
73 : ext2fs_mount,
74 : ufs_start,
75 : ext2fs_unmount,
76 : ufs_root,
77 : ufs_quotactl,
78 : ext2fs_statfs,
79 : ext2fs_sync,
80 : ext2fs_vget,
81 : ext2fs_fhtovp,
82 : ext2fs_vptofh,
83 : ext2fs_init,
84 : ext2fs_sysctl,
85 : ufs_check_export
86 : };
87 :
88 : struct pool ext2fs_inode_pool;
89 : struct pool ext2fs_dinode_pool;
90 :
91 : extern u_long ext2gennumber;
92 :
93 : int
94 0 : ext2fs_init(struct vfsconf *vfsp)
95 : {
96 0 : pool_init(&ext2fs_inode_pool, sizeof(struct inode), 0,
97 : IPL_NONE, PR_WAITOK, "ext2inopl", NULL);
98 0 : pool_init(&ext2fs_dinode_pool, sizeof(struct ext2fs_dinode), 0,
99 : IPL_NONE, PR_WAITOK, "ext2dinopl", NULL);
100 :
101 0 : return (ufs_init(vfsp));
102 : }
103 :
104 : /*
105 : * Called by main() when ext2fs is going to be mounted as root.
106 : *
107 : * Name is updated by mount(8) after booting.
108 : */
109 : #define ROOTNAME "root_device"
110 :
111 : int
112 0 : ext2fs_mountroot(void)
113 : {
114 : struct m_ext2fs *fs;
115 0 : struct mount *mp;
116 0 : struct proc *p = curproc; /* XXX */
117 : struct ufsmount *ump;
118 : int error;
119 :
120 : /*
121 : * Get vnodes for swapdev and rootdev.
122 : */
123 0 : if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
124 0 : panic("ext2fs_mountroot: can't setup bdevvp's");
125 :
126 0 : if ((error = vfs_rootmountalloc("ext2fs", "root_device", &mp)) != 0) {
127 0 : vrele(rootvp);
128 0 : return (error);
129 : }
130 :
131 0 : if ((error = ext2fs_mountfs(rootvp, mp, p)) != 0) {
132 0 : mp->mnt_vfc->vfc_refcount--;
133 0 : vfs_unbusy(mp);
134 0 : free(mp, M_MOUNT, sizeof *mp);
135 0 : vrele(rootvp);
136 0 : return (error);
137 : }
138 :
139 0 : TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
140 0 : ump = VFSTOUFS(mp);
141 0 : fs = ump->um_e2fs;
142 0 : memset(fs->e2fs_fsmnt, 0, sizeof(fs->e2fs_fsmnt));
143 0 : strlcpy(fs->e2fs_fsmnt, mp->mnt_stat.f_mntonname, sizeof(fs->e2fs_fsmnt));
144 0 : if (fs->e2fs.e2fs_rev > E2FS_REV0) {
145 0 : memset(fs->e2fs.e2fs_fsmnt, 0, sizeof(fs->e2fs.e2fs_fsmnt));
146 0 : strlcpy(fs->e2fs.e2fs_fsmnt, mp->mnt_stat.f_mntonname,
147 : sizeof(fs->e2fs.e2fs_fsmnt));
148 0 : }
149 0 : (void)ext2fs_statfs(mp, &mp->mnt_stat, p);
150 0 : vfs_unbusy(mp);
151 0 : inittodr(fs->e2fs.e2fs_wtime);
152 0 : return (0);
153 0 : }
154 :
155 : /*
156 : * VFS Operations.
157 : *
158 : * mount system call
159 : */
160 : int
161 0 : ext2fs_mount(struct mount *mp, const char *path, void *data,
162 : struct nameidata *ndp, struct proc *p)
163 : {
164 : struct vnode *devvp;
165 0 : struct ufs_args *args = data;
166 : struct ufsmount *ump = NULL;
167 : struct m_ext2fs *fs;
168 0 : char fname[MNAMELEN];
169 0 : char fspec[MNAMELEN];
170 : int error, flags;
171 :
172 : /*
173 : * If updating, check whether changing from read-only to
174 : * read/write; if there is no device name, that's all we do.
175 : */
176 0 : if (mp->mnt_flag & MNT_UPDATE) {
177 0 : ump = VFSTOUFS(mp);
178 0 : fs = ump->um_e2fs;
179 0 : if (fs->e2fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
180 : flags = WRITECLOSE;
181 0 : if (mp->mnt_flag & MNT_FORCE)
182 0 : flags |= FORCECLOSE;
183 0 : error = ext2fs_flushfiles(mp, flags, p);
184 0 : if (error == 0 &&
185 0 : ext2fs_cgupdate(ump, MNT_WAIT) == 0 &&
186 0 : (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) {
187 0 : fs->e2fs.e2fs_state = E2FS_ISCLEAN;
188 0 : (void)ext2fs_sbupdate(ump, MNT_WAIT);
189 0 : }
190 0 : if (error)
191 0 : return (error);
192 0 : fs->e2fs_ronly = 1;
193 0 : }
194 0 : if (mp->mnt_flag & MNT_RELOAD) {
195 0 : error = ext2fs_reload(mp, ndp->ni_cnd.cn_cred, p);
196 0 : if (error)
197 0 : return (error);
198 : }
199 0 : if (fs->e2fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) {
200 0 : fs->e2fs_ronly = 0;
201 0 : if (fs->e2fs.e2fs_state == E2FS_ISCLEAN)
202 0 : fs->e2fs.e2fs_state = 0;
203 : else
204 0 : fs->e2fs.e2fs_state = E2FS_ERRORS;
205 0 : fs->e2fs_fmod = 1;
206 0 : }
207 0 : if (args && args->fspec == NULL) {
208 : /*
209 : * Process export requests.
210 : */
211 0 : return (vfs_export(mp, &ump->um_export,
212 0 : &args->export_info));
213 : }
214 0 : if (args == NULL)
215 : goto success;
216 : }
217 : /*
218 : * Not an update, or updating the name: look up the name
219 : * and verify that it refers to a sensible block device.
220 : */
221 0 : error = copyinstr(args->fspec, fspec, sizeof(fspec), NULL);
222 0 : if (error)
223 : goto error;
224 :
225 0 : if (disk_map(fspec, fname, MNAMELEN, DM_OPENBLCK) == -1)
226 0 : memcpy(fname, fspec, sizeof(fname));
227 :
228 0 : NDINIT(ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, fname, p);
229 0 : if ((error = namei(ndp)) != 0)
230 : goto error;
231 0 : devvp = ndp->ni_vp;
232 :
233 0 : if (devvp->v_type != VBLK) {
234 : error = ENOTBLK;
235 0 : goto error_devvp;
236 : }
237 0 : if (major(devvp->v_rdev) >= nblkdev) {
238 : error = ENXIO;
239 0 : goto error_devvp;
240 : }
241 0 : if ((mp->mnt_flag & MNT_UPDATE) == 0)
242 0 : error = ext2fs_mountfs(devvp, mp, p);
243 : else {
244 0 : if (devvp != ump->um_devvp)
245 0 : error = EINVAL; /* XXX needs translation */
246 : else
247 0 : vrele(devvp);
248 : }
249 0 : if (error)
250 : goto error_devvp;
251 0 : ump = VFSTOUFS(mp);
252 0 : fs = ump->um_e2fs;
253 :
254 0 : memset(fs->e2fs_fsmnt, 0, sizeof(fs->e2fs_fsmnt));
255 0 : strlcpy(fs->e2fs_fsmnt, path, sizeof(fs->e2fs_fsmnt));
256 0 : if (fs->e2fs.e2fs_rev > E2FS_REV0) {
257 0 : memset(fs->e2fs.e2fs_fsmnt, 0, sizeof(fs->e2fs.e2fs_fsmnt));
258 0 : strlcpy(fs->e2fs.e2fs_fsmnt, mp->mnt_stat.f_mntonname,
259 : sizeof(fs->e2fs.e2fs_fsmnt));
260 0 : }
261 0 : memcpy(mp->mnt_stat.f_mntonname, fs->e2fs_fsmnt, MNAMELEN);
262 0 : memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN);
263 0 : strlcpy(mp->mnt_stat.f_mntfromname, fname, MNAMELEN);
264 0 : memset(mp->mnt_stat.f_mntfromspec, 0, MNAMELEN);
265 0 : strlcpy(mp->mnt_stat.f_mntfromspec, fspec, MNAMELEN);
266 0 : memcpy(&mp->mnt_stat.mount_info.ufs_args, args, sizeof(*args));
267 :
268 0 : if (fs->e2fs_fmod != 0) { /* XXX */
269 0 : fs->e2fs_fmod = 0;
270 0 : if (fs->e2fs.e2fs_state == 0)
271 0 : fs->e2fs.e2fs_wtime = time_second;
272 : else
273 0 : printf("%s: file system not clean; please fsck(8)\n",
274 : mp->mnt_stat.f_mntfromname);
275 0 : ext2fs_cgupdate(ump, MNT_WAIT);
276 0 : }
277 :
278 : goto success;
279 :
280 : error_devvp:
281 : /* Error with devvp held. */
282 0 : vrele(devvp);
283 :
284 : error:
285 : /* Error with no state to backout. */
286 :
287 : success:
288 0 : return (error);
289 0 : }
290 :
291 : int ext2fs_reload_vnode(struct vnode *, void *args);
292 :
293 : struct ext2fs_reload_args {
294 : struct m_ext2fs *fs;
295 : struct proc *p;
296 : struct ucred *cred;
297 : struct vnode *devvp;
298 : };
299 :
300 : int
301 0 : ext2fs_reload_vnode(struct vnode *vp, void *args)
302 : {
303 0 : struct ext2fs_reload_args *era = args;
304 0 : struct buf *bp;
305 : struct inode *ip;
306 : int error;
307 : caddr_t cp;
308 :
309 : /*
310 : * Step 4: invalidate all inactive vnodes.
311 : */
312 0 : if (vp->v_usecount == 0) {
313 0 : vgonel(vp, era->p);
314 0 : return (0);
315 : }
316 :
317 : /*
318 : * Step 5: invalidate all cached file data.
319 : */
320 0 : if (vget(vp, LK_EXCLUSIVE))
321 0 : return (0);
322 :
323 0 : if (vinvalbuf(vp, 0, era->cred, era->p, 0, 0))
324 0 : panic("ext2fs_reload: dirty2");
325 : /*
326 : * Step 6: re-read inode data for all active vnodes.
327 : */
328 0 : ip = VTOI(vp);
329 0 : error = bread(era->devvp,
330 0 : fsbtodb(era->fs, ino_to_fsba(era->fs, ip->i_number)),
331 0 : (int)era->fs->e2fs_bsize, &bp);
332 0 : if (error) {
333 0 : vput(vp);
334 0 : return (error);
335 : }
336 0 : cp = (caddr_t)bp->b_data +
337 0 : (ino_to_fsbo(era->fs, ip->i_number) * EXT2_DINODE_SIZE(era->fs));
338 0 : e2fs_iload(era->fs, (struct ext2fs_dinode *)cp, ip->i_e2din);
339 0 : brelse(bp);
340 0 : vput(vp);
341 0 : return (0);
342 0 : }
343 :
344 : static off_t
345 0 : ext2fs_maxfilesize(struct m_ext2fs *fs)
346 : {
347 0 : bool huge = fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_HUGE_FILE;
348 0 : off_t b = fs->e2fs_bsize / 4;
349 : off_t physically, logically;
350 :
351 0 : physically = dbtob(huge ? ((1ULL << 48) - 1) : UINT_MAX);
352 0 : logically = (12ULL + b + b*b + b*b*b) * fs->e2fs_bsize;
353 :
354 0 : return MIN(logically, physically);
355 : }
356 :
357 : static int
358 0 : e2fs_sbfill(struct vnode *devvp, struct m_ext2fs *fs)
359 : {
360 0 : struct buf *bp = NULL;
361 : int i, error;
362 :
363 : /* XXX assume hardware block size == 512 */
364 0 : fs->e2fs_ncg = howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
365 : fs->e2fs.e2fs_bpg);
366 0 : fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
367 0 : fs->e2fs_bsize = 1024 << fs->e2fs.e2fs_log_bsize;
368 0 : fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
369 0 : fs->e2fs_fsize = 1024 << fs->e2fs.e2fs_log_fsize;
370 :
371 0 : fs->e2fs_qbmask = fs->e2fs_bsize - 1;
372 0 : fs->e2fs_bmask = ~fs->e2fs_qbmask;
373 :
374 0 : fs->e2fs_ipb = fs->e2fs_bsize / EXT2_DINODE_SIZE(fs);
375 0 : fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb;
376 :
377 : /* Re-read group descriptors from the disk. */
378 0 : fs->e2fs_ngdb = howmany(fs->e2fs_ncg,
379 : fs->e2fs_bsize / sizeof(struct ext2_gd));
380 0 : fs->e2fs_gd = mallocarray(fs->e2fs_ngdb, fs->e2fs_bsize,
381 : M_UFSMNT, M_WAITOK);
382 :
383 0 : for (i = 0; i < fs->e2fs_ngdb; ++i) {
384 0 : daddr_t dblk = ((fs->e2fs_bsize > 1024) ? 0 : 1) + i + 1;
385 0 : size_t gdesc = i * fs->e2fs_bsize / sizeof(struct ext2_gd);
386 : struct ext2_gd *gd;
387 :
388 0 : error = bread(devvp, fsbtodb(fs, dblk), fs->e2fs_bsize, &bp);
389 0 : if (error) {
390 0 : size_t gdescs_space = fs->e2fs_ngdb * fs->e2fs_bsize;
391 :
392 0 : free(fs->e2fs_gd, M_UFSMNT, gdescs_space);
393 0 : fs->e2fs_gd = NULL;
394 0 : brelse(bp);
395 : return (error);
396 : }
397 :
398 0 : gd = (struct ext2_gd *) bp->b_data;
399 0 : e2fs_cgload(gd, fs->e2fs_gd + gdesc, fs->e2fs_bsize);
400 0 : brelse(bp);
401 0 : bp = NULL;
402 0 : }
403 :
404 0 : if ((fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_LARGEFILE) == 0 ||
405 0 : (fs->e2fs.e2fs_rev == E2FS_REV0))
406 0 : fs->e2fs_maxfilesize = INT_MAX;
407 : else
408 0 : fs->e2fs_maxfilesize = ext2fs_maxfilesize(fs);
409 :
410 0 : if (fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_EXTENTS)
411 0 : fs->e2fs_maxfilesize *= 4;
412 :
413 0 : return (0);
414 0 : }
415 :
416 : /*
417 : * Reload all incore data for a filesystem (used after running fsck on
418 : * the root filesystem and finding things to fix). The filesystem must
419 : * be mounted read-only.
420 : *
421 : * Things to do to update the mount:
422 : * 1) invalidate all cached meta-data.
423 : * 2) re-read superblock from disk.
424 : * 3) re-read summary information from disk.
425 : * 4) invalidate all inactive vnodes.
426 : * 5) invalidate all cached file data.
427 : * 6) re-read inode data for all active vnodes.
428 : */
429 : int
430 0 : ext2fs_reload(struct mount *mountp, struct ucred *cred, struct proc *p)
431 : {
432 : struct vnode *devvp;
433 0 : struct buf *bp;
434 : struct m_ext2fs *fs;
435 : struct ext2fs *newfs;
436 : int error;
437 0 : struct ext2fs_reload_args era;
438 :
439 0 : if ((mountp->mnt_flag & MNT_RDONLY) == 0)
440 0 : return (EINVAL);
441 : /*
442 : * Step 1: invalidate all cached meta-data.
443 : */
444 0 : devvp = VFSTOUFS(mountp)->um_devvp;
445 0 : vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
446 0 : error = vinvalbuf(devvp, 0, cred, p, 0, 0);
447 0 : VOP_UNLOCK(devvp);
448 0 : if (error != 0)
449 0 : panic("ext2fs_reload: dirty1");
450 :
451 : /*
452 : * Step 2: re-read superblock from disk.
453 : */
454 0 : error = bread(devvp, (daddr_t)(SBOFF / DEV_BSIZE), SBSIZE, &bp);
455 0 : if (error) {
456 0 : brelse(bp);
457 0 : return (error);
458 : }
459 0 : newfs = (struct ext2fs *)bp->b_data;
460 0 : error = e2fs_sbcheck(newfs, (mountp->mnt_flag & MNT_RDONLY));
461 0 : if (error) {
462 0 : brelse(bp);
463 0 : return (error);
464 : }
465 :
466 0 : fs = VFSTOUFS(mountp)->um_e2fs;
467 : /*
468 : * Copy in the new superblock, compute in-memory values
469 : * and load group descriptors.
470 : */
471 0 : e2fs_sbload(newfs, &fs->e2fs);
472 0 : if ((error = e2fs_sbfill(devvp, fs)) != 0)
473 0 : return (error);
474 :
475 0 : era.p = p;
476 0 : era.cred = cred;
477 0 : era.fs = fs;
478 0 : era.devvp = devvp;
479 :
480 0 : error = vfs_mount_foreach_vnode(mountp, ext2fs_reload_vnode, &era);
481 :
482 0 : return (error);
483 0 : }
484 :
485 : /*
486 : * Common code for mount and mountroot
487 : */
488 : int
489 0 : ext2fs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p)
490 : {
491 : struct ufsmount *ump;
492 0 : struct buf *bp;
493 : struct ext2fs *fs;
494 : dev_t dev;
495 : int error, ronly;
496 : struct ucred *cred;
497 :
498 0 : dev = devvp->v_rdev;
499 0 : cred = p ? p->p_ucred : NOCRED;
500 : /*
501 : * Disallow multiple mounts of the same device.
502 : * Disallow mounting of a device that is currently in use
503 : * (except for root, which might share swap device for miniroot).
504 : * Flush out any old buffers remaining from a previous use.
505 : */
506 0 : if ((error = vfs_mountedon(devvp)) != 0)
507 0 : return (error);
508 0 : if (vcount(devvp) > 1 && devvp != rootvp)
509 0 : return (EBUSY);
510 0 : vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
511 0 : error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0);
512 0 : VOP_UNLOCK(devvp);
513 0 : if (error != 0)
514 0 : return (error);
515 :
516 0 : ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
517 0 : error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
518 0 : if (error)
519 0 : return (error);
520 :
521 0 : bp = NULL;
522 : ump = NULL;
523 :
524 : /*
525 : * Read the superblock from disk.
526 : */
527 0 : error = bread(devvp, (daddr_t)(SBOFF / DEV_BSIZE), SBSIZE, &bp);
528 0 : if (error)
529 : goto out;
530 0 : fs = (struct ext2fs *)bp->b_data;
531 0 : error = e2fs_sbcheck(fs, ronly);
532 0 : if (error)
533 : goto out;
534 :
535 0 : ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK | M_ZERO);
536 0 : ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT,
537 : M_WAITOK | M_ZERO);
538 :
539 : /*
540 : * Copy in the superblock, compute in-memory values
541 : * and load group descriptors.
542 : */
543 0 : e2fs_sbload(fs, &ump->um_e2fs->e2fs);
544 0 : if ((error = e2fs_sbfill(devvp, ump->um_e2fs)) != 0)
545 : goto out;
546 0 : brelse(bp);
547 0 : bp = NULL;
548 0 : fs = &ump->um_e2fs->e2fs;
549 0 : ump->um_e2fs->e2fs_ronly = ronly;
550 0 : ump->um_fstype = UM_EXT2FS;
551 :
552 0 : if (ronly == 0) {
553 0 : if (fs->e2fs_state == E2FS_ISCLEAN)
554 0 : fs->e2fs_state = 0;
555 : else
556 0 : fs->e2fs_state = E2FS_ERRORS;
557 0 : ump->um_e2fs->e2fs_fmod = 1;
558 0 : }
559 :
560 0 : mp->mnt_data = ump;
561 0 : mp->mnt_stat.f_fsid.val[0] = (long)dev;
562 0 : mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
563 0 : mp->mnt_stat.f_namemax = MAXNAMLEN;
564 0 : mp->mnt_flag |= MNT_LOCAL;
565 0 : ump->um_mountp = mp;
566 0 : ump->um_dev = dev;
567 0 : ump->um_devvp = devvp;
568 0 : ump->um_nindir = NINDIR(ump->um_e2fs);
569 0 : ump->um_bptrtodb = ump->um_e2fs->e2fs_fsbtodb;
570 0 : ump->um_seqinc = 1; /* no frags */
571 0 : ump->um_maxsymlinklen = EXT2_MAXSYMLINKLEN;
572 0 : devvp->v_specmountpoint = mp;
573 0 : return (0);
574 : out:
575 0 : if (devvp->v_specinfo)
576 0 : devvp->v_specmountpoint = NULL;
577 0 : if (bp)
578 0 : brelse(bp);
579 0 : vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
580 0 : (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
581 0 : VOP_UNLOCK(devvp);
582 0 : if (ump) {
583 0 : free(ump->um_e2fs, M_UFSMNT, sizeof *ump->um_e2fs);
584 0 : free(ump, M_UFSMNT, sizeof *ump);
585 0 : mp->mnt_data = NULL;
586 0 : }
587 0 : return (error);
588 0 : }
589 :
590 : /*
591 : * unmount system call
592 : */
593 : int
594 0 : ext2fs_unmount(struct mount *mp, int mntflags, struct proc *p)
595 : {
596 : struct ufsmount *ump;
597 : struct m_ext2fs *fs;
598 : int error, flags;
599 : size_t gdescs_space;
600 :
601 : flags = 0;
602 0 : if (mntflags & MNT_FORCE)
603 0 : flags |= FORCECLOSE;
604 0 : if ((error = ext2fs_flushfiles(mp, flags, p)) != 0)
605 0 : return (error);
606 0 : ump = VFSTOUFS(mp);
607 0 : fs = ump->um_e2fs;
608 0 : gdescs_space = fs->e2fs_ngdb * fs->e2fs_bsize;
609 :
610 0 : if (!fs->e2fs_ronly && ext2fs_cgupdate(ump, MNT_WAIT) == 0 &&
611 0 : (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) {
612 0 : fs->e2fs.e2fs_state = E2FS_ISCLEAN;
613 0 : (void) ext2fs_sbupdate(ump, MNT_WAIT);
614 0 : }
615 :
616 0 : if (ump->um_devvp->v_type != VBAD)
617 0 : ump->um_devvp->v_specmountpoint = NULL;
618 0 : vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
619 0 : (void)VOP_CLOSE(ump->um_devvp, fs->e2fs_ronly ? FREAD : FREAD|FWRITE,
620 : NOCRED, p);
621 0 : vput(ump->um_devvp);
622 0 : free(fs->e2fs_gd, M_UFSMNT, gdescs_space);
623 0 : free(fs, M_UFSMNT, sizeof *fs);
624 0 : free(ump, M_UFSMNT, sizeof *ump);
625 0 : mp->mnt_data = NULL;
626 0 : mp->mnt_flag &= ~MNT_LOCAL;
627 0 : return (0);
628 0 : }
629 :
630 : /*
631 : * Flush out all the files in a filesystem.
632 : */
633 : int
634 0 : ext2fs_flushfiles(struct mount *mp, int flags, struct proc *p)
635 : {
636 : struct ufsmount *ump;
637 : int error;
638 :
639 0 : ump = VFSTOUFS(mp);
640 : /*
641 : * Flush all the files.
642 : */
643 0 : if ((error = vflush(mp, NULL, flags)) != 0)
644 0 : return (error);
645 : /*
646 : * Flush filesystem metadata.
647 : */
648 0 : vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
649 0 : error = VOP_FSYNC(ump->um_devvp, p->p_ucred, MNT_WAIT, p);
650 0 : VOP_UNLOCK(ump->um_devvp);
651 0 : return (error);
652 0 : }
653 :
654 : /*
655 : * Get file system statistics.
656 : */
657 : int
658 0 : ext2fs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
659 : {
660 : struct ufsmount *ump;
661 : struct m_ext2fs *fs;
662 : u_int32_t overhead, overhead_per_group;
663 : int i, ngroups;
664 :
665 0 : ump = VFSTOUFS(mp);
666 0 : fs = ump->um_e2fs;
667 0 : if (fs->e2fs.e2fs_magic != E2FS_MAGIC)
668 0 : panic("ext2fs_statfs");
669 :
670 : /*
671 : * Compute the overhead (FS structures)
672 : */
673 0 : overhead_per_group = 1 /* block bitmap */ + 1 /* inode bitmap */ +
674 0 : fs->e2fs_itpg;
675 0 : overhead = fs->e2fs.e2fs_first_dblock +
676 0 : fs->e2fs_ncg * overhead_per_group;
677 0 : if (fs->e2fs.e2fs_rev > E2FS_REV0 &&
678 0 : fs->e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_SPARSESUPER) {
679 0 : for (i = 0, ngroups = 0; i < fs->e2fs_ncg; i++) {
680 0 : if (cg_has_sb(i))
681 0 : ngroups++;
682 : }
683 : } else {
684 0 : ngroups = fs->e2fs_ncg;
685 : }
686 0 : overhead += ngroups * (1 + fs->e2fs_ngdb);
687 :
688 0 : sbp->f_bsize = fs->e2fs_bsize;
689 0 : sbp->f_iosize = fs->e2fs_bsize;
690 0 : sbp->f_blocks = fs->e2fs.e2fs_bcount - overhead;
691 0 : sbp->f_bfree = fs->e2fs.e2fs_fbcount;
692 0 : sbp->f_bavail = sbp->f_bfree - fs->e2fs.e2fs_rbcount;
693 0 : sbp->f_files = fs->e2fs.e2fs_icount;
694 0 : sbp->f_favail = sbp->f_ffree = fs->e2fs.e2fs_ficount;
695 0 : copy_statfs_info(sbp, mp);
696 :
697 0 : return (0);
698 : }
699 :
700 : int ext2fs_sync_vnode(struct vnode *vp, void *);
701 :
702 : struct ext2fs_sync_args {
703 : int allerror;
704 : int waitfor;
705 : int nlink0;
706 : int inflight;
707 : struct proc *p;
708 : struct ucred *cred;
709 : };
710 :
711 : int
712 0 : ext2fs_sync_vnode(struct vnode *vp, void *args)
713 : {
714 0 : struct ext2fs_sync_args *esa = args;
715 : struct inode *ip;
716 : int error, nlink0 = 0;
717 :
718 0 : if (vp->v_type == VNON)
719 0 : return (0);
720 :
721 0 : if (vp->v_inflight)
722 0 : esa->inflight = MIN(esa->inflight+1, 65536);
723 :
724 0 : ip = VTOI(vp);
725 :
726 0 : if (ip->i_e2fs_nlink == 0)
727 0 : nlink0 = 1;
728 :
729 0 : if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
730 0 : LIST_EMPTY(&vp->v_dirtyblkhd)) {
731 : goto end;
732 : }
733 :
734 0 : if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT)) {
735 : nlink0 = 1; /* potentially */
736 0 : goto end;
737 : }
738 :
739 0 : if ((error = VOP_FSYNC(vp, esa->cred, esa->waitfor, esa->p)) != 0)
740 0 : esa->allerror = error;
741 0 : vput(vp);
742 : end:
743 0 : esa->nlink0 = MIN(esa->nlink0 + nlink0, 65536);
744 0 : return (0);
745 0 : }
746 : /*
747 : * Go through the disk queues to initiate sandbagged IO;
748 : * go through the inodes to write those that have been modified;
749 : * initiate the writing of the super block if it has been modified.
750 : *
751 : * Should always be called with the mount point locked.
752 : */
753 : int
754 0 : ext2fs_sync(struct mount *mp, int waitfor, int stall,
755 : struct ucred *cred, struct proc *p)
756 : {
757 0 : struct ufsmount *ump = VFSTOUFS(mp);
758 : struct m_ext2fs *fs;
759 : int error, allerror = 0, state, fmod;
760 0 : struct ext2fs_sync_args esa;
761 :
762 0 : fs = ump->um_e2fs;
763 0 : if (fs->e2fs_ronly != 0) { /* XXX */
764 0 : printf("fs = %s\n", fs->e2fs_fsmnt);
765 0 : panic("update: rofs mod");
766 : }
767 :
768 : /*
769 : * Write back each (modified) inode.
770 : */
771 0 : esa.p = p;
772 0 : esa.cred = cred;
773 0 : esa.allerror = 0;
774 0 : esa.waitfor = waitfor;
775 0 : esa.nlink0 = 0;
776 0 : esa.inflight = 0;
777 :
778 0 : vfs_mount_foreach_vnode(mp, ext2fs_sync_vnode, &esa);
779 0 : if (esa.allerror != 0)
780 0 : allerror = esa.allerror;
781 :
782 : /*
783 : * Force stale file system control information to be flushed.
784 : */
785 0 : if (waitfor != MNT_LAZY) {
786 0 : vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
787 0 : if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0)
788 0 : allerror = error;
789 0 : VOP_UNLOCK(ump->um_devvp);
790 0 : }
791 : /*
792 : * Write back modified superblock.
793 : */
794 0 : state = fs->e2fs.e2fs_state;
795 0 : fmod = fs->e2fs_fmod;
796 0 : if (stall && fs->e2fs_ronly == 0) {
797 0 : fs->e2fs_fmod = 1;
798 0 : if (allerror == 0 && esa.nlink0 == 0 && esa.inflight == 0) {
799 0 : if ((fs->e2fs.e2fs_state & E2FS_ERRORS) == 0)
800 0 : fs->e2fs.e2fs_state = E2FS_ISCLEAN;
801 : #if 0
802 : printf("%s force clean (dangling %d inflight %d)\n",
803 : mp->mnt_stat.f_mntonname, esa.nlink0, esa.inflight);
804 : #endif
805 : } else {
806 0 : fs->e2fs.e2fs_state = 0;
807 : #if 0
808 : printf("%s force dirty (dangling %d inflight %d)\n",
809 : mp->mnt_stat.f_mntonname, esa.nlink0, esa.inflight);
810 : #endif
811 : }
812 : }
813 0 : if (fs->e2fs_fmod != 0) {
814 0 : fs->e2fs_fmod = 0;
815 0 : fs->e2fs.e2fs_wtime = time_second;
816 0 : if ((error = ext2fs_cgupdate(ump, waitfor)))
817 0 : allerror = error;
818 : }
819 0 : fs->e2fs.e2fs_state = state;
820 0 : fs->e2fs_fmod = fmod;
821 0 : return (allerror);
822 0 : }
823 :
824 : /*
825 : * Look up a EXT2FS dinode number to find its incore vnode, otherwise read it
826 : * in from disk. If it is in core, wait for the lock bit to clear, then
827 : * return the inode locked. Detection and handling of mount points must be
828 : * done by the calling routine.
829 : */
830 : int
831 0 : ext2fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
832 : {
833 : struct m_ext2fs *fs;
834 : struct inode *ip;
835 : struct ext2fs_dinode *dp;
836 : struct ufsmount *ump;
837 0 : struct buf *bp;
838 0 : struct vnode *vp;
839 : dev_t dev;
840 : int error;
841 :
842 0 : if (ino > (ufsino_t)-1)
843 0 : panic("ext2fs_vget: alien ino_t %llu",
844 : (unsigned long long)ino);
845 :
846 0 : ump = VFSTOUFS(mp);
847 0 : dev = ump->um_dev;
848 :
849 : retry:
850 0 : if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
851 0 : return (0);
852 :
853 : /* Allocate a new vnode/inode. */
854 0 : if ((error = getnewvnode(VT_EXT2FS, mp, &ext2fs_vops, &vp)) != 0) {
855 0 : *vpp = NULL;
856 0 : return (error);
857 : }
858 :
859 0 : ip = pool_get(&ext2fs_inode_pool, PR_WAITOK|PR_ZERO);
860 0 : rrw_init_flags(&ip->i_lock, "inode", RWL_DUPOK | RWL_IS_VNODE);
861 0 : vp->v_data = ip;
862 0 : ip->i_vnode = vp;
863 0 : ip->i_ump = ump;
864 0 : ip->i_e2fs = fs = ump->um_e2fs;
865 0 : ip->i_dev = dev;
866 0 : ip->i_number = ino;
867 0 : ip->i_e2fs_last_lblk = 0;
868 0 : ip->i_e2fs_last_blk = 0;
869 :
870 : /*
871 : * Put it onto its hash chain and lock it so that other requests for
872 : * this inode will block if they arrive while we are sleeping waiting
873 : * for old data structures to be purged or for the contents of the
874 : * disk portion of this inode to be read.
875 : */
876 0 : error = ufs_ihashins(ip);
877 :
878 0 : if (error) {
879 0 : vrele(vp);
880 :
881 0 : if (error == EEXIST)
882 0 : goto retry;
883 :
884 0 : return (error);
885 : }
886 :
887 : /* Read in the disk contents for the inode, copy into the inode. */
888 0 : error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
889 0 : (int)fs->e2fs_bsize, &bp);
890 0 : if (error) {
891 : /*
892 : * The inode does not contain anything useful, so it would
893 : * be misleading to leave it on its hash chain. With mode
894 : * still zero, it will be unlinked and returned to the free
895 : * list by vput().
896 : */
897 0 : vput(vp);
898 0 : brelse(bp);
899 0 : *vpp = NULL;
900 0 : return (error);
901 : }
902 :
903 0 : dp = (struct ext2fs_dinode *) ((char *)bp->b_data
904 0 : + EXT2_DINODE_SIZE(fs) * ino_to_fsbo(fs, ino));
905 :
906 0 : ip->i_e2din = pool_get(&ext2fs_dinode_pool, PR_WAITOK);
907 0 : e2fs_iload(fs, dp, ip->i_e2din);
908 0 : brelse(bp);
909 :
910 0 : ip->i_effnlink = ip->i_e2fs_nlink;
911 :
912 : /*
913 : * The fields for storing the UID and GID of an ext2fs inode are
914 : * limited to 16 bits. To overcome this limitation, Linux decided to
915 : * scatter the highest bits of these values into a previously reserved
916 : * area on the disk inode. We deal with this situation by having two
917 : * 32-bit fields *out* of the disk inode to hold the complete values.
918 : * Now that we are reading in the inode, compute these fields.
919 : */
920 0 : ip->i_e2fs_uid = ip->i_e2fs_uid_low | (ip->i_e2fs_uid_high << 16);
921 0 : ip->i_e2fs_gid = ip->i_e2fs_gid_low | (ip->i_e2fs_gid_high << 16);
922 :
923 : /* If the inode was deleted, reset all fields */
924 0 : if (ip->i_e2fs_dtime != 0) {
925 0 : ip->i_e2fs_mode = ip->i_e2fs_nblock = 0;
926 0 : (void)ext2fs_setsize(ip, 0);
927 0 : }
928 :
929 : /*
930 : * Initialize the vnode from the inode, check for aliases.
931 : * Note that the underlying vnode may have changed.
932 : */
933 0 : error = ext2fs_vinit(mp, &vp);
934 0 : if (error) {
935 0 : vput(vp);
936 0 : *vpp = NULL;
937 0 : return (error);
938 : }
939 :
940 : /*
941 : * Finish inode initialization now that aliasing has been resolved.
942 : */
943 0 : vref(ip->i_devvp);
944 : /*
945 : * Set up a generation number for this inode if it does not
946 : * already have one. This should only happen on old filesystems.
947 : */
948 0 : if (ip->i_e2fs_gen == 0) {
949 0 : if (++ext2gennumber < (u_long)time_second)
950 0 : ext2gennumber = time_second;
951 0 : ip->i_e2fs_gen = ext2gennumber;
952 0 : if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
953 0 : ip->i_flag |= IN_MODIFIED;
954 : }
955 :
956 0 : *vpp = vp;
957 0 : return (0);
958 0 : }
959 :
960 : /*
961 : * File handle to vnode
962 : *
963 : * Have to be really careful about stale file handles:
964 : * - check that the inode number is valid
965 : * - call ext2fs_vget() to get the locked inode
966 : * - check for an unallocated inode (i_mode == 0)
967 : * - check that the given client host has export rights and return
968 : * those rights via. exflagsp and credanonp
969 : */
970 : int
971 0 : ext2fs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
972 : {
973 : struct inode *ip;
974 0 : struct vnode *nvp;
975 : int error;
976 : struct ufid *ufhp;
977 : struct m_ext2fs *fs;
978 :
979 0 : ufhp = (struct ufid *)fhp;
980 0 : fs = VFSTOUFS(mp)->um_e2fs;
981 0 : if ((ufhp->ufid_ino < EXT2_FIRSTINO && ufhp->ufid_ino != EXT2_ROOTINO) ||
982 0 : ufhp->ufid_ino > fs->e2fs_ncg * fs->e2fs.e2fs_ipg)
983 0 : return (ESTALE);
984 :
985 0 : if ((error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) != 0) {
986 0 : *vpp = NULLVP;
987 0 : return (error);
988 : }
989 0 : ip = VTOI(nvp);
990 0 : if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0 ||
991 0 : ip->i_e2fs_gen != ufhp->ufid_gen) {
992 0 : vput(nvp);
993 0 : *vpp = NULLVP;
994 0 : return (ESTALE);
995 : }
996 0 : *vpp = nvp;
997 0 : return (0);
998 0 : }
999 :
1000 : /*
1001 : * Vnode pointer to File handle
1002 : */
1003 : /* ARGSUSED */
1004 : int
1005 0 : ext2fs_vptofh(struct vnode *vp, struct fid *fhp)
1006 : {
1007 : struct inode *ip;
1008 : struct ufid *ufhp;
1009 :
1010 0 : ip = VTOI(vp);
1011 0 : ufhp = (struct ufid *)fhp;
1012 0 : ufhp->ufid_len = sizeof(struct ufid);
1013 0 : ufhp->ufid_ino = ip->i_number;
1014 0 : ufhp->ufid_gen = ip->i_e2fs_gen;
1015 0 : return (0);
1016 : }
1017 :
1018 : /*
1019 : * no sysctl for ext2fs
1020 : */
1021 :
1022 : int
1023 0 : ext2fs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
1024 : void *newp, size_t newlen, struct proc *p)
1025 : {
1026 0 : return (EOPNOTSUPP);
1027 : }
1028 :
1029 : /*
1030 : * Write a superblock and associated information back to disk.
1031 : */
1032 : int
1033 0 : ext2fs_sbupdate(struct ufsmount *mp, int waitfor)
1034 : {
1035 0 : struct m_ext2fs *fs = mp->um_e2fs;
1036 : struct buf *bp;
1037 : int error = 0;
1038 :
1039 0 : bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0);
1040 0 : e2fs_sbsave(&fs->e2fs, (struct ext2fs *) bp->b_data);
1041 0 : if (waitfor == MNT_WAIT)
1042 0 : error = bwrite(bp);
1043 : else
1044 0 : bawrite(bp);
1045 0 : fs->e2fs_fmod = 0;
1046 0 : return (error);
1047 : }
1048 :
1049 : int
1050 0 : ext2fs_cgupdate(struct ufsmount *mp, int waitfor)
1051 : {
1052 0 : struct m_ext2fs *fs = mp->um_e2fs;
1053 : struct buf *bp;
1054 : int i, error = 0, allerror = 0;
1055 :
1056 0 : allerror = ext2fs_sbupdate(mp, waitfor);
1057 0 : for (i = 0; i < fs->e2fs_ngdb; i++) {
1058 0 : bp = getblk(mp->um_devvp, fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1),
1059 : fs->e2fs_bsize, 0, 0);
1060 0 : e2fs_cgsave(&fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)], (struct ext2_gd*)bp->b_data, fs->e2fs_bsize);
1061 0 : if (waitfor == MNT_WAIT)
1062 0 : error = bwrite(bp);
1063 : else
1064 0 : bawrite(bp);
1065 : }
1066 :
1067 0 : if (!allerror && error)
1068 0 : allerror = error;
1069 0 : return (allerror);
1070 : }
1071 :
1072 : /* This is called before the superblock is copied. Watch out for endianity! */
1073 : static int
1074 0 : e2fs_sbcheck(struct ext2fs *fs, int ronly)
1075 : {
1076 : u_int32_t tmp;
1077 :
1078 0 : tmp = letoh16(fs->e2fs_magic);
1079 0 : if (tmp != E2FS_MAGIC) {
1080 0 : printf("ext2fs: wrong magic number 0x%x\n", tmp);
1081 0 : return (EIO); /* XXX needs translation */
1082 : }
1083 :
1084 0 : tmp = letoh32(fs->e2fs_log_bsize);
1085 0 : if (tmp > 2) {
1086 : /* skewed log(block size): 1024 -> 0 | 2048 -> 1 | 4096 -> 2 */
1087 0 : tmp += 10;
1088 0 : printf("ext2fs: wrong log2(block size) %d\n", tmp);
1089 0 : return (EIO); /* XXX needs translation */
1090 : }
1091 :
1092 0 : if (fs->e2fs_bpg == 0) {
1093 0 : printf("ext2fs: zero blocks per group\n");
1094 0 : return (EIO);
1095 : }
1096 :
1097 0 : tmp = letoh32(fs->e2fs_rev);
1098 0 : if (tmp > E2FS_REV1) {
1099 0 : printf("ext2fs: wrong revision number 0x%x\n", tmp);
1100 0 : return (EIO); /* XXX needs translation */
1101 : }
1102 0 : else if (tmp == E2FS_REV0)
1103 0 : return (0);
1104 :
1105 0 : tmp = letoh32(fs->e2fs_first_ino);
1106 0 : if (tmp != EXT2_FIRSTINO) {
1107 0 : printf("ext2fs: first inode at 0x%x\n", tmp);
1108 0 : return (EINVAL); /* XXX needs translation */
1109 : }
1110 :
1111 0 : tmp = letoh32(fs->e2fs_features_incompat);
1112 0 : if (tmp & ~(EXT2F_INCOMPAT_SUPP | EXT4F_RO_INCOMPAT_SUPP)) {
1113 0 : printf("ext2fs: unsupported incompat features 0x%x\n", tmp);
1114 0 : return (EINVAL); /* XXX needs translation */
1115 : }
1116 :
1117 0 : if (!ronly && (tmp & EXT4F_RO_INCOMPAT_SUPP)) {
1118 0 : printf("ext4fs: only read-only support right now\n");
1119 0 : return (EROFS); /* XXX needs translation */
1120 : }
1121 :
1122 0 : if (tmp & EXT2F_INCOMPAT_RECOVER) {
1123 0 : printf("ext2fs: your file system says it needs recovery\n");
1124 0 : if (!ronly)
1125 0 : return (EROFS); /* XXX needs translation */
1126 : }
1127 :
1128 0 : tmp = letoh32(fs->e2fs_features_rocompat);
1129 0 : if (!ronly && (tmp & ~EXT2F_ROCOMPAT_SUPP)) {
1130 0 : printf("ext2fs: unsupported R/O compat features 0x%x\n", tmp);
1131 0 : return (EROFS); /* XXX needs translation */
1132 : }
1133 :
1134 0 : return (0);
1135 0 : }
|