xref: /openbsd/sys/ufs/ext2fs/ext2fs_vnops.c (revision 73471bf0)
1 /*	$OpenBSD: ext2fs_vnops.c,v 1.89 2021/10/02 08:51:41 semarie Exp $	*/
2 /*	$NetBSD: ext2fs_vnops.c,v 1.1 1997/06/11 09:34:09 bouyer Exp $	*/
3 
4 /*
5  * Copyright (c) 1997 Manuel Bouyer.
6  * Copyright (c) 1982, 1986, 1989, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  * (c) UNIX System Laboratories, Inc.
9  * All or some portions of this file are derived from material licensed
10  * to the University of California by American Telephone and Telegraph
11  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
12  * the permission of UNIX System Laboratories, Inc.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)ufs_vnops.c	8.14 (Berkeley) 10/26/94
39  * Modified for ext2fs by Manuel Bouyer.
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/resourcevar.h>
45 #include <sys/kernel.h>
46 #include <sys/fcntl.h>
47 #include <sys/stat.h>
48 #include <sys/proc.h>
49 #include <sys/conf.h>
50 #include <sys/mount.h>
51 #include <sys/namei.h>
52 #include <sys/vnode.h>
53 #include <sys/lockf.h>
54 #include <sys/malloc.h>
55 #include <sys/pool.h>
56 #include <sys/signalvar.h>
57 #include <sys/specdev.h>
58 #include <sys/unistd.h>
59 
60 #include <miscfs/fifofs/fifo.h>
61 
62 #include <ufs/ufs/quota.h>
63 #include <ufs/ufs/inode.h>
64 #include <ufs/ufs/ufs_extern.h>
65 #include <ufs/ufs/ufsmount.h>
66 
67 #include <ufs/ext2fs/ext2fs.h>
68 #include <ufs/ext2fs/ext2fs_extern.h>
69 #include <ufs/ext2fs/ext2fs_dir.h>
70 
71 #include <uvm/uvm_extern.h>
72 
73 static int ext2fs_chmod(struct vnode *, mode_t, struct ucred *);
74 static int ext2fs_chown(struct vnode *, uid_t, gid_t, struct ucred *);
75 
76 /*
77  * Create a regular file
78  */
79 int
80 ext2fs_create(void *v)
81 {
82 	struct vop_create_args *ap = v;
83 
84 	return (ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type,
85 	    ap->a_vap->va_mode), ap->a_dvp, ap->a_vpp, ap->a_cnp));
86 }
87 
88 /*
89  * Mknod vnode call
90  */
91 /* ARGSUSED */
92 int
93 ext2fs_mknod(void *v)
94 {
95 	struct vop_mknod_args *ap = v;
96 	struct vattr *vap = ap->a_vap;
97 	struct vnode **vpp = ap->a_vpp;
98 	struct inode *ip;
99 	int error;
100 
101 	error = ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
102 	    ap->a_dvp, vpp, ap->a_cnp);
103 	if (error != 0)
104 		return (error);
105 	ip = VTOI(*vpp);
106 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
107 	if (vap->va_rdev != VNOVAL) {
108 		/*
109 		 * Want to be able to use this to make badblock
110 		 * inodes, so don't truncate the dev number.
111 		 */
112 		ip->i_e2din->e2di_rdev = htole32(vap->va_rdev);
113 	}
114 	/*
115 	 * Remove inode so that it will be reloaded by VFS_VGET and
116 	 * checked to see if it is an alias of an existing entry in
117 	 * the inode cache.
118 	 */
119 	vput(*vpp);
120 	(*vpp)->v_type = VNON;
121 	vgone(*vpp);
122 	*vpp = NULL;
123 	return (0);
124 }
125 
126 /*
127  * Open called.
128  *
129  * Just check the APPEND flag.
130  */
131 /* ARGSUSED */
132 int
133 ext2fs_open(void *v)
134 {
135 	struct vop_open_args *ap = v;
136 
137 	/*
138 	 * Files marked append-only must be opened for appending.
139 	 */
140 	if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
141 		(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
142 		return (EPERM);
143 	return (0);
144 }
145 
146 int
147 ext2fs_access(void *v)
148 {
149 	struct vop_access_args *ap = v;
150 	struct vnode *vp = ap->a_vp;
151 	struct inode *ip = VTOI(vp);
152 	mode_t mode = ap->a_mode;
153 
154 	/* If immutable bit set, nobody gets to write it. */
155 	if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
156 		return (EPERM);
157 
158 	return (vaccess(vp->v_type, ip->i_e2fs_mode, ip->i_e2fs_uid,
159 			ip->i_e2fs_gid, mode, ap->a_cred));
160 }
161 
162 /* ARGSUSED */
163 int
164 ext2fs_getattr(void *v)
165 {
166 	struct vop_getattr_args *ap = v;
167 	struct vnode *vp = ap->a_vp;
168 	struct inode *ip = VTOI(vp);
169 	struct vattr *vap = ap->a_vap;
170 
171 	EXT2FS_ITIMES(ip);
172 	/*
173 	 * Copy from inode table
174 	 */
175 	vap->va_fsid = ip->i_dev;
176 	vap->va_fileid = ip->i_number;
177 	vap->va_mode = ip->i_e2fs_mode & ALLPERMS;
178 	vap->va_nlink = ip->i_e2fs_nlink;
179 	vap->va_uid = ip->i_e2fs_uid;
180 	vap->va_gid = ip->i_e2fs_gid;
181 	vap->va_rdev = (dev_t) letoh32(ip->i_e2din->e2di_rdev);
182 	vap->va_size = ext2fs_size(ip);
183 	vap->va_atime.tv_sec = ip->i_e2fs_atime;
184 	vap->va_atime.tv_nsec = 0;
185 	vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
186 	vap->va_mtime.tv_nsec = 0;
187 	vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
188 	vap->va_ctime.tv_nsec = 0;
189 #ifdef EXT2FS_SYSTEM_FLAGS
190 	vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
191 	vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
192 #else
193 	vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
194 	vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
195 #endif
196 	vap->va_gen = ip->i_e2fs_gen;
197 	/* this doesn't belong here */
198 	if (vp->v_type == VBLK)
199 		vap->va_blocksize = BLKDEV_IOSIZE;
200 	else if (vp->v_type == VCHR)
201 		vap->va_blocksize = MAXBSIZE;
202 	else
203 		vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
204 	vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock);
205 	vap->va_type = vp->v_type;
206 	vap->va_filerev = ip->i_modrev;
207 	return (0);
208 }
209 
210 /*
211  * Set attribute vnode op. called from several syscalls
212  */
213 int
214 ext2fs_setattr(void *v)
215 {
216 	struct vop_setattr_args *ap = v;
217 	struct vattr *vap = ap->a_vap;
218 	struct vnode *vp = ap->a_vp;
219 	struct inode *ip = VTOI(vp);
220 	struct ucred *cred = ap->a_cred;
221 	int error;
222 
223 	/*
224 	 * Check for unsettable attributes.
225 	 */
226 	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
227 		(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
228 		(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
229 		((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
230 		return (EINVAL);
231 	}
232 	if (vap->va_flags != VNOVAL) {
233 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
234 			return (EROFS);
235 		if (cred->cr_uid != ip->i_e2fs_uid &&
236 			(error = suser_ucred(cred)))
237 			return (error);
238 #ifdef EXT2FS_SYSTEM_FLAGS
239 		if (cred->cr_uid == 0) {
240 			if ((ip->i_e2fs_flags &
241 			    (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0)
242 				return (EPERM);
243 			ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
244 			ip->i_e2fs_flags |=
245 			    (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
246 			    (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
247 		} else {
248 			return (EPERM);
249 		}
250 #else
251 		ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
252 		ip->i_e2fs_flags |=
253 		    (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
254 		    (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
255 #endif
256 		ip->i_flag |= IN_CHANGE;
257 		if (vap->va_flags & (IMMUTABLE | APPEND))
258 			return (0);
259 	}
260 	if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
261 		return (EPERM);
262 	/*
263 	 * Go through the fields and update iff not VNOVAL.
264 	 */
265 	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
266 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
267 			return (EROFS);
268 		error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred);
269 		if (error)
270 			return (error);
271 	}
272 	if (vap->va_size != VNOVAL) {
273 		/*
274 		 * Disallow write attempts on read-only file systems;
275 		 * unless the file is a socket, fifo, or a block or
276 		 * character device resident on the file system.
277 		 */
278 		switch (vp->v_type) {
279 		case VDIR:
280 			return (EISDIR);
281 		case VLNK:
282 		case VREG:
283 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
284 				return (EROFS);
285 		default:
286 			break;
287 		}
288 		error = ext2fs_truncate(ip, vap->va_size, 0, cred);
289 		if (error)
290 			return (error);
291 	}
292 	if ((vap->va_vaflags & VA_UTIMES_CHANGE) ||
293 	    vap->va_atime.tv_nsec != VNOVAL ||
294 	    vap->va_mtime.tv_nsec != VNOVAL) {
295 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
296 			return (EROFS);
297 		if (cred->cr_uid != ip->i_e2fs_uid &&
298 			(error = suser_ucred(cred)) &&
299 			((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
300 			(error = VOP_ACCESS(vp, VWRITE, cred, ap->a_p))))
301 			return (error);
302 		if (vap->va_mtime.tv_nsec != VNOVAL)
303 			ip->i_flag |= IN_CHANGE | IN_UPDATE;
304 		else if (vap->va_vaflags & VA_UTIMES_CHANGE)
305 			ip->i_flag |= IN_CHANGE;
306 		if (vap->va_atime.tv_nsec != VNOVAL) {
307 			if (!(vp->v_mount->mnt_flag & MNT_NOATIME) ||
308 			    (ip->i_flag & (IN_CHANGE | IN_UPDATE)))
309 				ip->i_flag |= IN_ACCESS;
310 		}
311 		EXT2FS_ITIMES(ip);
312 		if (vap->va_mtime.tv_nsec != VNOVAL)
313 			ip->i_e2fs_mtime = vap->va_mtime.tv_sec;
314 		if (vap->va_atime.tv_nsec != VNOVAL)
315 			ip->i_e2fs_atime = vap->va_atime.tv_sec;
316 		error = ext2fs_update(ip, 1);
317 		if (error)
318 			return (error);
319 	}
320 	error = 0;
321 	if (vap->va_mode != (mode_t)VNOVAL) {
322 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
323 			return (EROFS);
324 		error = ext2fs_chmod(vp, vap->va_mode, cred);
325 	}
326 	return (error);
327 }
328 
329 /*
330  * Change the mode on a file.
331  * Inode must be locked before calling.
332  */
333 static int
334 ext2fs_chmod(struct vnode *vp, mode_t mode, struct ucred *cred)
335 {
336 	struct inode *ip = VTOI(vp);
337 	int error;
338 
339 	if (cred->cr_uid != ip->i_e2fs_uid && (error = suser_ucred(cred)))
340 		return (error);
341 	if (cred->cr_uid) {
342 		if (vp->v_type != VDIR && (mode & S_ISTXT))
343 			return (EFTYPE);
344 		if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
345 			return (EPERM);
346 	}
347 	ip->i_e2fs_mode &= ~ALLPERMS;
348 	ip->i_e2fs_mode |= (mode & ALLPERMS);
349 	ip->i_flag |= IN_CHANGE;
350 	if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
351 		(void) uvm_vnp_uncache(vp);
352 	return (0);
353 }
354 
355 /*
356  * Perform chown operation on inode ip;
357  * inode must be locked prior to call.
358  */
359 static int
360 ext2fs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred)
361 {
362 	struct inode *ip = VTOI(vp);
363 	uid_t ouid;
364 	gid_t ogid;
365 	int error = 0;
366 
367 	if (uid == (uid_t)VNOVAL)
368 		uid = ip->i_e2fs_uid;
369 	if (gid == (gid_t)VNOVAL)
370 		gid = ip->i_e2fs_gid;
371 	/*
372 	 * If we don't own the file, are trying to change the owner
373 	 * of the file, or are not a member of the target group,
374 	 * the caller must be superuser or the call fails.
375 	 */
376 	if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
377 		(gid != ip->i_e2fs_gid && !groupmember(gid, cred))) &&
378 		(error = suser_ucred(cred)))
379 		return (error);
380 	ogid = ip->i_e2fs_gid;
381 	ouid = ip->i_e2fs_uid;
382 
383 	ip->i_e2fs_gid = gid;
384 	ip->i_e2fs_uid = uid;
385 	if (ouid != uid || ogid != gid)
386 		ip->i_flag |= IN_CHANGE;
387 	if (ouid != uid && cred->cr_uid != 0)
388 		ip->i_e2fs_mode &= ~ISUID;
389 	if (ogid != gid && cred->cr_uid != 0)
390 		ip->i_e2fs_mode &= ~ISGID;
391 	return (0);
392 }
393 
394 int
395 ext2fs_remove(void *v)
396 {
397 	struct vop_remove_args *ap = v;
398 	struct inode *ip;
399 	struct vnode *vp = ap->a_vp;
400 	struct vnode *dvp = ap->a_dvp;
401 	int error;
402 
403 	ip = VTOI(vp);
404 	if (vp->v_type == VDIR ||
405 		(ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
406 	    	(VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
407 		error = EPERM;
408 		goto out;
409 	}
410 	error = ext2fs_dirremove(dvp, ap->a_cnp);
411 	if (error == 0) {
412 		ip->i_e2fs_nlink--;
413 		ip->i_flag |= IN_CHANGE;
414 	}
415 out:
416 	if (dvp == vp)
417 		vrele(vp);
418 	else
419 		vput(vp);
420 	vput(dvp);
421 	return (error);
422 }
423 
424 /*
425  * link vnode call
426  */
427 int
428 ext2fs_link(void *v)
429 {
430 	struct vop_link_args *ap = v;
431 	struct vnode *dvp = ap->a_dvp;
432 	struct vnode *vp = ap->a_vp;
433 	struct componentname *cnp = ap->a_cnp;
434 	struct inode *ip;
435 	int error;
436 
437 #ifdef DIAGNOSTIC
438 	if ((cnp->cn_flags & HASBUF) == 0)
439 		panic("ext2fs_link: no name");
440 #endif
441 	if (vp->v_type == VDIR) {
442 		VOP_ABORTOP(dvp, cnp);
443 		error = EISDIR;
444 		goto out2;
445 	}
446 	if (dvp->v_mount != vp->v_mount) {
447 		VOP_ABORTOP(dvp, cnp);
448 		error = EXDEV;
449 		goto out2;
450 	}
451 	if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE))) {
452 		VOP_ABORTOP(dvp, cnp);
453 		goto out2;
454 	}
455 	ip = VTOI(vp);
456 	if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
457 		VOP_ABORTOP(dvp, cnp);
458 		error = EMLINK;
459 		goto out1;
460 	}
461 	if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
462 		VOP_ABORTOP(dvp, cnp);
463 		error = EPERM;
464 		goto out1;
465 	}
466 	ip->i_e2fs_nlink++;
467 	ip->i_flag |= IN_CHANGE;
468 	error = ext2fs_update(ip, 1);
469 	if (!error)
470 		error = ext2fs_direnter(ip, dvp, cnp);
471 	if (error) {
472 		ip->i_e2fs_nlink--;
473 		ip->i_flag |= IN_CHANGE;
474 	}
475 	pool_put(&namei_pool, cnp->cn_pnbuf);
476 out1:
477 	if (dvp != vp)
478 		VOP_UNLOCK(vp);
479 out2:
480 	vput(dvp);
481 	return (error);
482 }
483 
484 /*
485  * Rename system call.
486  * 	rename("foo", "bar");
487  * is essentially
488  *	unlink("bar");
489  *	link("foo", "bar");
490  *	unlink("foo");
491  * but ``atomically''.  Can't do full commit without saving state in the
492  * inode on disk which isn't feasible at this time.  Best we can do is
493  * always guarantee the target exists.
494  *
495  * Basic algorithm is:
496  *
497  * 1) Bump link count on source while we're linking it to the
498  *    target.  This also ensure the inode won't be deleted out
499  *    from underneath us while we work (it may be truncated by
500  *    a concurrent `trunc' or `open' for creation).
501  * 2) Link source to destination.  If destination already exists,
502  *    delete it first.
503  * 3) Unlink source reference to inode if still around. If a
504  *    directory was moved and the parent of the destination
505  *    is different from the source, patch the ".." entry in the
506  *    directory.
507  */
508 int
509 ext2fs_rename(void *v)
510 {
511 	struct vop_rename_args  *ap = v;
512 	struct vnode *tvp = ap->a_tvp;
513 	struct vnode *tdvp = ap->a_tdvp;
514 	struct vnode *fvp = ap->a_fvp;
515 	struct vnode *fdvp = ap->a_fdvp;
516 	struct componentname *tcnp = ap->a_tcnp;
517 	struct componentname *fcnp = ap->a_fcnp;
518 	struct inode *ip, *xp, *dp;
519 	struct ext2fs_dirtemplate dirbuf;
520 	/* struct timespec ts; */
521 	int doingdirectory = 0, oldparent = 0, newparent = 0;
522 	int error = 0;
523 	u_char namlen;
524 
525 #ifdef DIAGNOSTIC
526 	if ((tcnp->cn_flags & HASBUF) == 0 ||
527 	    (fcnp->cn_flags & HASBUF) == 0)
528 		panic("ext2fs_rename: no name");
529 #endif
530 	/*
531 	 * Check for cross-device rename.
532 	 */
533 	if ((fvp->v_mount != tdvp->v_mount) ||
534 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
535 		error = EXDEV;
536 abortit:
537 		VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
538 		if (tdvp == tvp)
539 			vrele(tdvp);
540 		else
541 			vput(tdvp);
542 		if (tvp)
543 			vput(tvp);
544 		VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
545 		vrele(fdvp);
546 		vrele(fvp);
547 		return (error);
548 	}
549 
550 	/*
551 	 * Check if just deleting a link name.
552 	 */
553 	if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
554 	    (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
555 		error = EPERM;
556 		goto abortit;
557 	}
558 	if (fvp == tvp) {
559 		if (fvp->v_type == VDIR) {
560 			error = EINVAL;
561 			goto abortit;
562 		}
563 
564 		/* Release destination completely. */
565 		VOP_ABORTOP(tdvp, tcnp);
566 		vput(tdvp);
567 		vput(tvp);
568 
569 		/* Delete source. */
570 		vrele(fdvp);
571 		vrele(fvp);
572 		fcnp->cn_flags &= ~MODMASK;
573 		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
574 		if ((fcnp->cn_flags & SAVESTART) == 0)
575 			panic("ext2fs_rename: lost from startdir");
576 		fcnp->cn_nameiop = DELETE;
577 		(void) vfs_relookup(fdvp, &fvp, fcnp);
578 		return (VOP_REMOVE(fdvp, fvp, fcnp));
579 	}
580 	if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
581 		goto abortit;
582 	dp = VTOI(fdvp);
583 	ip = VTOI(fvp);
584 	if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
585 		VOP_UNLOCK(fvp);
586 		error = EMLINK;
587 		goto abortit;
588 	}
589 	if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
590 		(dp->i_e2fs_flags & EXT2_APPEND)) {
591 		VOP_UNLOCK(fvp);
592 		error = EPERM;
593 		goto abortit;
594 	}
595 	if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
596 		error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
597 		if (!error && tvp)
598 			error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred,
599 			    tcnp->cn_proc);
600 		if (error) {
601 			VOP_UNLOCK(fvp);
602 			error = EACCES;
603 			goto abortit;
604 		}
605 		/*
606 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
607 		 */
608 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
609 		    dp == ip ||
610 			(fcnp->cn_flags&ISDOTDOT) ||
611 			(tcnp->cn_flags & ISDOTDOT) ||
612 		    (ip->i_flag & IN_RENAME)) {
613 			VOP_UNLOCK(fvp);
614 			error = EINVAL;
615 			goto abortit;
616 		}
617 		ip->i_flag |= IN_RENAME;
618 		oldparent = dp->i_number;
619 		doingdirectory++;
620 	}
621 	vrele(fdvp);
622 
623 	/*
624 	 * When the target exists, both the directory
625 	 * and target vnodes are returned locked.
626 	 */
627 	dp = VTOI(tdvp);
628 	xp = NULL;
629 	if (tvp)
630 		xp = VTOI(tvp);
631 
632 	/*
633 	 * 1) Bump link count while we're moving stuff
634 	 *    around.  If we crash somewhere before
635 	 *    completing our work, the link count
636 	 *    may be wrong, but correctable.
637 	 */
638 	ip->i_e2fs_nlink++;
639 	ip->i_flag |= IN_CHANGE;
640 	if ((error = ext2fs_update(ip, 1)) != 0) {
641 		VOP_UNLOCK(fvp);
642 		goto bad;
643 	}
644 
645 	/*
646 	 * If ".." must be changed (ie the directory gets a new
647 	 * parent) then the source directory must not be in the
648 	 * directory hierarchy above the target, as this would
649 	 * orphan everything below the source directory. Also
650 	 * the user must have write permission in the source so
651 	 * as to be able to change "..". We must repeat the call
652 	 * to namei, as the parent directory is unlocked by the
653 	 * call to checkpath().
654 	 */
655 	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
656 	VOP_UNLOCK(fvp);
657 	if (oldparent != dp->i_number)
658 		newparent = dp->i_number;
659 	if (doingdirectory && newparent) {
660 		if (error)	/* write access check above */
661 			goto bad;
662 		if (xp != NULL)
663 			vput(tvp);
664 		error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
665 		if (error != 0)
666 			goto out;
667 		if ((tcnp->cn_flags & SAVESTART) == 0)
668 			panic("ext2fs_rename: lost to startdir");
669 		if ((error = vfs_relookup(tdvp, &tvp, tcnp)) != 0)
670 			goto out;
671 		dp = VTOI(tdvp);
672 		xp = NULL;
673 		if (tvp)
674 			xp = VTOI(tvp);
675 	}
676 	/*
677 	 * 2) If target doesn't exist, link the target
678 	 *    to the source and unlink the source.
679 	 *    Otherwise, rewrite the target directory
680 	 *    entry to reference the source inode and
681 	 *    expunge the original entry's existence.
682 	 */
683 	if (xp == NULL) {
684 		if (dp->i_dev != ip->i_dev)
685 			panic("rename: EXDEV");
686 		/*
687 		 * Account for ".." in new directory.
688 		 * When source and destination have the same
689 		 * parent we don't fool with the link count.
690 		 */
691 		if (doingdirectory && newparent) {
692 			if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
693 				error = EMLINK;
694 				goto bad;
695 			}
696 			dp->i_e2fs_nlink++;
697 			dp->i_flag |= IN_CHANGE;
698 			if ((error = ext2fs_update(dp, 1)) != 0)
699 				goto bad;
700 		}
701 		error = ext2fs_direnter(ip, tdvp, tcnp);
702 		if (error != 0) {
703 			if (doingdirectory && newparent) {
704 				dp->i_e2fs_nlink--;
705 				dp->i_flag |= IN_CHANGE;
706 				(void)ext2fs_update(dp, 1);
707 			}
708 			goto bad;
709 		}
710 		vput(tdvp);
711 	} else {
712 		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
713 			panic("rename: EXDEV");
714 		/*
715 		 * Short circuit rename(foo, foo).
716 		 */
717 		if (xp->i_number == ip->i_number)
718 			panic("rename: same file");
719 		/*
720 		 * If the parent directory is "sticky", then the user must
721 		 * own the parent directory, or the destination of the rename,
722 		 * otherwise the destination may not be changed (except by
723 		 * root). This implements append-only directories.
724 		 */
725 		if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
726 		    tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
727 		    xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
728 			error = EPERM;
729 			goto bad;
730 		}
731 		/*
732 		 * Target must be empty if a directory and have no links
733 		 * to it. Also, ensure source and target are compatible
734 		 * (both directories, or both not directories).
735 		 */
736 		if ((xp->i_e2fs_mode & IFMT) == IFDIR) {
737 			if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
738 				xp->i_e2fs_nlink > 2) {
739 				error = ENOTEMPTY;
740 				goto bad;
741 			}
742 			if (!doingdirectory) {
743 				error = ENOTDIR;
744 				goto bad;
745 			}
746 			cache_purge(tdvp);
747 		} else if (doingdirectory) {
748 			error = EISDIR;
749 			goto bad;
750 		}
751 		error = ext2fs_dirrewrite(dp, ip, tcnp);
752 		if (error != 0)
753 			goto bad;
754 		/*
755 		 * If the target directory is in the same
756 		 * directory as the source directory,
757 		 * decrement the link count on the parent
758 		 * of the target directory.
759 		 */
760 		if (doingdirectory && !newparent) {
761 			dp->i_e2fs_nlink--;
762 			dp->i_flag |= IN_CHANGE;
763 		}
764 		vput(tdvp);
765 		/*
766 		 * Adjust the link count of the target to
767 		 * reflect the dirrewrite above.  If this is
768 		 * a directory it is empty and there are
769 		 * no links to it, so we can squash the inode and
770 		 * any space associated with it.  We disallowed
771 		 * renaming over top of a directory with links to
772 		 * it above, as the remaining link would point to
773 		 * a directory without "." or ".." entries.
774 		 */
775 		xp->i_e2fs_nlink--;
776 		if (doingdirectory) {
777 			if (--xp->i_e2fs_nlink != 0)
778 				panic("rename: linked directory");
779 			error = ext2fs_truncate(xp, (off_t)0, IO_SYNC,
780 			    tcnp->cn_cred);
781 		}
782 		xp->i_flag |= IN_CHANGE;
783 		vput(tvp);
784 		xp = NULL;
785 	}
786 
787 	/*
788 	 * 3) Unlink the source.
789 	 */
790 	fcnp->cn_flags &= ~MODMASK;
791 	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
792 	if ((fcnp->cn_flags & SAVESTART) == 0)
793 		panic("ext2fs_rename: lost from startdir");
794 	(void) vfs_relookup(fdvp, &fvp, fcnp);
795 	if (fvp != NULL) {
796 		xp = VTOI(fvp);
797 		dp = VTOI(fdvp);
798 	} else {
799 		/*
800 		 * From name has disappeared.
801 		 */
802 		if (doingdirectory)
803 			panic("ext2fs_rename: lost dir entry");
804 		vrele(ap->a_fvp);
805 		return (0);
806 	}
807 	/*
808 	 * Ensure that the directory entry still exists and has not
809 	 * changed while the new name has been entered. If the source is
810 	 * a file then the entry may have been unlinked or renamed. In
811 	 * either case there is no further work to be done. If the source
812 	 * is a directory then it cannot have been rmdir'ed; its link
813 	 * count of three would cause a rmdir to fail with ENOTEMPTY.
814 	 * The IRENAME flag ensures that it cannot be moved by another
815 	 * rename.
816 	 */
817 	if (xp != ip) {
818 		if (doingdirectory)
819 			panic("ext2fs_rename: lost dir entry");
820 	} else {
821 		/*
822 		 * If the source is a directory with a
823 		 * new parent, the link count of the old
824 		 * parent directory must be decremented
825 		 * and ".." set to point to the new parent.
826 		 */
827 		if (doingdirectory && newparent) {
828 			dp->i_e2fs_nlink--;
829 			dp->i_flag |= IN_CHANGE;
830 			error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
831 				sizeof (struct ext2fs_dirtemplate), (off_t)0,
832 				UIO_SYSSPACE, IO_NODELOCKED,
833 				tcnp->cn_cred, NULL, curproc);
834 			if (error == 0) {
835 					namlen = dirbuf.dotdot_namlen;
836 				if (namlen != 2 ||
837 				    dirbuf.dotdot_name[0] != '.' ||
838 				    dirbuf.dotdot_name[1] != '.') {
839 					ufs_dirbad(xp, (doff_t)12,
840 					    "ext2fs_rename: mangled dir");
841 				} else {
842 					dirbuf.dotdot_ino = htole32(newparent);
843 					(void) vn_rdwr(UIO_WRITE, fvp,
844 					    (caddr_t)&dirbuf,
845 					    sizeof (struct dirtemplate),
846 					    (off_t)0, UIO_SYSSPACE,
847 					    IO_NODELOCKED|IO_SYNC,
848 					    tcnp->cn_cred, NULL, curproc);
849 					cache_purge(fdvp);
850 				}
851 			}
852 		}
853 		error = ext2fs_dirremove(fdvp, fcnp);
854 		if (!error) {
855 			xp->i_e2fs_nlink--;
856 			xp->i_flag |= IN_CHANGE;
857 		}
858 		xp->i_flag &= ~IN_RENAME;
859 	}
860 	if (dp)
861 		vput(fdvp);
862 	if (xp)
863 		vput(fvp);
864 	vrele(ap->a_fvp);
865 	return (error);
866 
867 bad:
868 	if (xp)
869 		vput(ITOV(xp));
870 	vput(ITOV(dp));
871 out:
872 	if (doingdirectory)
873 		ip->i_flag &= ~IN_RENAME;
874 	if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
875 		ip->i_e2fs_nlink--;
876 		ip->i_flag |= IN_CHANGE;
877 		vput(fvp);
878 	} else
879 		vrele(fvp);
880 	return (error);
881 }
882 
883 /*
884  * Mkdir system call
885  */
886 int
887 ext2fs_mkdir(void *v)
888 {
889 	struct vop_mkdir_args *ap = v;
890 	struct vnode *dvp = ap->a_dvp;
891 	struct vattr *vap = ap->a_vap;
892 	struct componentname *cnp = ap->a_cnp;
893 	struct inode *ip, *dp;
894 	struct vnode *tvp;
895 	struct ext2fs_dirtemplate dirtemplate;
896 	mode_t dmode;
897 	int error;
898 
899 #ifdef DIAGNOSTIC
900 	if ((cnp->cn_flags & HASBUF) == 0)
901 		panic("ext2fs_mkdir: no name");
902 #endif
903 	dp = VTOI(dvp);
904 	if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
905 		error = EMLINK;
906 		goto out;
907 	}
908 	dmode = vap->va_mode & ACCESSPERMS;
909 	dmode |= IFDIR;
910 	/*
911 	 * Must simulate part of ext2fs_makeinode here to acquire the inode,
912 	 * but not have it entered in the parent directory. The entry is
913 	 * made later after writing "." and ".." entries.
914 	 */
915 	if ((error = ext2fs_inode_alloc(dp, dmode, cnp->cn_cred, &tvp)) != 0)
916 		goto out;
917 	ip = VTOI(tvp);
918 	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
919 	ip->i_e2fs_gid = dp->i_e2fs_gid;
920 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
921 	ip->i_e2fs_mode = dmode;
922 	tvp->v_type = VDIR;	/* Rest init'd in getnewvnode(). */
923 	ip->i_e2fs_nlink = 2;
924 	error = ext2fs_update(ip, 1);
925 
926 	/*
927 	 * Bump link count in parent directory
928 	 * to reflect work done below.  Should
929 	 * be done before reference is created
930 	 * so reparation is possible if we crash.
931 	 */
932 	dp->i_e2fs_nlink++;
933 	dp->i_flag |= IN_CHANGE;
934 	if ((error = ext2fs_update(dp, 1)) != 0)
935 		goto bad;
936 
937 	/* Initialize directory with "." and ".." from static template. */
938 	memset(&dirtemplate, 0, sizeof(dirtemplate));
939 	dirtemplate.dot_ino = htole32(ip->i_number);
940 	dirtemplate.dot_reclen = htole16(12);
941 	dirtemplate.dot_namlen = 1;
942 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
943 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
944 		dirtemplate.dot_type = EXT2_FT_DIR;
945 	}
946 	dirtemplate.dot_name[0] = '.';
947 	dirtemplate.dotdot_ino = htole32(dp->i_number);
948 	dirtemplate.dotdot_reclen = htole16(VTOI(dvp)->i_e2fs->e2fs_bsize - 12);
949 	dirtemplate.dotdot_namlen = 2;
950 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
951 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
952 		dirtemplate.dotdot_type = EXT2_FT_DIR;
953 	}
954 	dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.';
955 	error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
956 	    sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
957 	    IO_NODELOCKED|IO_SYNC, cnp->cn_cred, NULL, curproc);
958 	if (error) {
959 		dp->i_e2fs_nlink--;
960 		dp->i_flag |= IN_CHANGE;
961 		goto bad;
962 	}
963 	if (VTOI(dvp)->i_e2fs->e2fs_bsize >
964 							VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
965 		panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
966 	else {
967 		error = ext2fs_setsize(ip, VTOI(dvp)->i_e2fs->e2fs_bsize);
968 		if (error) {
969 			dp->i_e2fs_nlink--;
970 			dp->i_flag |= IN_CHANGE;
971 			goto bad;
972 		}
973 		ip->i_flag |= IN_CHANGE;
974 	}
975 
976 	/* Directory set up, now install its entry in the parent directory. */
977 	error = ext2fs_direnter(ip, dvp, cnp);
978 	if (error != 0) {
979 		dp->i_e2fs_nlink--;
980 		dp->i_flag |= IN_CHANGE;
981 	}
982 bad:
983 	/*
984 	 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
985 	 * for us because we set the link count to 0.
986 	 */
987 	if (error) {
988 		ip->i_e2fs_nlink = 0;
989 		ip->i_flag |= IN_CHANGE;
990 		vput(tvp);
991 	} else
992 		*ap->a_vpp = tvp;
993 out:
994 	pool_put(&namei_pool, cnp->cn_pnbuf);
995 	vput(dvp);
996 	return (error);
997 }
998 
999 /*
1000  * Rmdir system call.
1001  */
1002 int
1003 ext2fs_rmdir(void *v)
1004 {
1005 	struct vop_rmdir_args *ap = v;
1006 	struct vnode *vp = ap->a_vp;
1007 	struct vnode *dvp = ap->a_dvp;
1008 	struct componentname *cnp = ap->a_cnp;
1009 	struct inode *ip, *dp;
1010 	int error;
1011 
1012 	ip = VTOI(vp);
1013 	dp = VTOI(dvp);
1014 	/*
1015 	 * Verify the directory is empty (and valid).
1016 	 * (Rmdir ".." won't be valid since
1017 	 *  ".." will contain a reference to
1018 	 *  the current directory and thus be
1019 	 *  non-empty.)
1020 	 */
1021 	error = 0;
1022 	if (ip->i_e2fs_nlink != 2 ||
1023 	    !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
1024 		error = ENOTEMPTY;
1025 		goto out;
1026 	}
1027 	if ((dp->i_e2fs_flags & EXT2_APPEND) ||
1028 				 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
1029 		error = EPERM;
1030 		goto out;
1031 	}
1032 	/*
1033 	 * Delete reference to directory before purging
1034 	 * inode.  If we crash in between, the directory
1035 	 * will be reattached to lost+found,
1036 	 */
1037 	error = ext2fs_dirremove(dvp, cnp);
1038 	if (error != 0)
1039 		goto out;
1040 	dp->i_e2fs_nlink--;
1041 	dp->i_flag |= IN_CHANGE;
1042 	cache_purge(dvp);
1043 	vput(dvp);
1044 	dvp = NULL;
1045 	/*
1046 	 * Truncate inode.  The only stuff left
1047 	 * in the directory is "." and "..".  The
1048 	 * "." reference is inconsequential since
1049 	 * we're quashing it.  The ".." reference
1050 	 * has already been adjusted above.  We've
1051 	 * removed the "." reference and the reference
1052 	 * in the parent directory, but there may be
1053 	 * other hard links so decrement by 2 and
1054 	 * worry about them later.
1055 	 */
1056 	ip->i_e2fs_nlink -= 2;
1057 	error = ext2fs_truncate(ip, (off_t)0, IO_SYNC, cnp->cn_cred);
1058 	cache_purge(ITOV(ip));
1059 out:
1060 	if (dvp)
1061 		vput(dvp);
1062 	vput(vp);
1063 	return (error);
1064 }
1065 
1066 /*
1067  * symlink -- make a symbolic link
1068  */
1069 int
1070 ext2fs_symlink(void *v)
1071 {
1072 	struct vop_symlink_args *ap = v;
1073 	struct vnode *vp, **vpp = ap->a_vpp;
1074 	struct inode *ip;
1075 	int len, error;
1076 
1077 	error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1078 			      vpp, ap->a_cnp);
1079 	vput(ap->a_dvp);
1080 	if (error)
1081 		return (error);
1082 	vp = *vpp;
1083 	len = strlen(ap->a_target);
1084 	if (len < EXT2_MAXSYMLINKLEN) {
1085 		ip = VTOI(vp);
1086 		memcpy(ip->i_e2din->e2di_shortlink, ap->a_target, len);
1087 		error = ext2fs_setsize(ip, len);
1088 		if (error)
1089 			goto bad;
1090 		ip->i_flag |= IN_CHANGE | IN_UPDATE;
1091 	} else
1092 		error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1093 		    UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL,
1094 		    curproc);
1095 bad:
1096 	vput(vp);
1097 	return (error);
1098 }
1099 
1100 /*
1101  * Return target name of a symbolic link
1102  */
1103 int
1104 ext2fs_readlink(void *v)
1105 {
1106 	struct vop_readlink_args *ap = v;
1107 	struct vnode *vp = ap->a_vp;
1108 	struct inode *ip = VTOI(vp);
1109 	u_int64_t isize;
1110 
1111 	isize = ext2fs_size(ip);
1112 	if (isize < EXT2_MAXSYMLINKLEN) {
1113 		return (uiomove((char *)ip->i_e2din->e2di_shortlink, isize,
1114 		    ap->a_uio));
1115 	}
1116 	return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
1117 }
1118 
1119 /*
1120  * Return POSIX pathconf information applicable to ext2 filesystems.
1121  */
1122 int
1123 ext2fs_pathconf(void *v)
1124 {
1125 	struct vop_pathconf_args *ap = v;
1126 	int error = 0;
1127 
1128 	switch (ap->a_name) {
1129 	case _PC_TIMESTAMP_RESOLUTION:
1130 		*ap->a_retval = 1000000000;	/* 1 billion nanoseconds */
1131 		break;
1132 	default:
1133 		return (ufs_pathconf(v));
1134 	}
1135 
1136 	return (error);
1137 }
1138 
1139 /*
1140  * Advisory record locking support
1141  */
1142 int
1143 ext2fs_advlock(void *v)
1144 {
1145 	struct vop_advlock_args *ap = v;
1146 	struct inode *ip = VTOI(ap->a_vp);
1147 
1148 	return (lf_advlock(&ip->i_lockf, ext2fs_size(ip), ap->a_id, ap->a_op,
1149 	    ap->a_fl, ap->a_flags));
1150 }
1151 
1152 /*
1153  * Allocate a new inode.
1154  */
1155 int
1156 ext2fs_makeinode(int mode, struct vnode *dvp, struct vnode **vpp,
1157     struct componentname *cnp)
1158 {
1159 	struct inode *ip, *pdir;
1160 	struct vnode *tvp;
1161 	int error;
1162 
1163 	pdir = VTOI(dvp);
1164 #ifdef DIAGNOSTIC
1165 	if ((cnp->cn_flags & HASBUF) == 0)
1166 		panic("ext2fs_makeinode: no name");
1167 #endif
1168 	*vpp = NULL;
1169 	if ((mode & IFMT) == 0)
1170 		mode |= IFREG;
1171 
1172 	if ((error = ext2fs_inode_alloc(pdir, mode, cnp->cn_cred, &tvp))
1173 	    != 0) {
1174 		pool_put(&namei_pool, cnp->cn_pnbuf);
1175 		return (error);
1176 	}
1177 	ip = VTOI(tvp);
1178 	ip->i_e2fs_gid = pdir->i_e2fs_gid;
1179 	ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
1180 	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1181 	ip->i_e2fs_mode = mode;
1182 	tvp->v_type = IFTOVT(mode);	/* Rest init'd in getnewvnode(). */
1183 	ip->i_e2fs_nlink = 1;
1184 	if ((ip->i_e2fs_mode & ISGID) &&
1185 		!groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
1186 	    suser_ucred(cnp->cn_cred))
1187 		ip->i_e2fs_mode &= ~ISGID;
1188 
1189 	/*
1190 	 * Make sure inode goes to disk before directory entry.
1191 	 */
1192 	if ((error = ext2fs_update(ip, 1)) != 0)
1193 		goto bad;
1194 	error = ext2fs_direnter(ip, dvp, cnp);
1195 	if (error != 0)
1196 		goto bad;
1197 	if ((cnp->cn_flags & SAVESTART) == 0)
1198 		pool_put(&namei_pool, cnp->cn_pnbuf);
1199 	*vpp = tvp;
1200 	return (0);
1201 
1202 bad:
1203 	/*
1204 	 * Write error occurred trying to update the inode
1205 	 * or the directory so must deallocate the inode.
1206 	 */
1207 	pool_put(&namei_pool, cnp->cn_pnbuf);
1208 	ip->i_e2fs_nlink = 0;
1209 	ip->i_flag |= IN_CHANGE;
1210 	tvp->v_type = VNON;
1211 	vput(tvp);
1212 	return (error);
1213 }
1214 
1215 /*
1216  * Synch an open file.
1217  */
1218 /* ARGSUSED */
1219 int
1220 ext2fs_fsync(void *v)
1221 {
1222 	struct vop_fsync_args *ap = v;
1223 	struct vnode *vp = ap->a_vp;
1224 
1225 	vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
1226 	return (ext2fs_update(VTOI(ap->a_vp), ap->a_waitfor == MNT_WAIT));
1227 }
1228 
1229 /*
1230  * Reclaim an inode so that it can be used for other purposes.
1231  */
1232 int
1233 ext2fs_reclaim(void *v)
1234 {
1235 	struct vop_reclaim_args *ap = v;
1236 	struct vnode *vp = ap->a_vp;
1237 	struct inode *ip;
1238 #ifdef DIAGNOSTIC
1239 	extern int prtactive;
1240 
1241 	if (prtactive && vp->v_usecount != 0)
1242 		vprint("ext2fs_reclaim: pushing active", vp);
1243 #endif
1244 
1245 	/*
1246 	 * Remove the inode from its hash chain.
1247 	 */
1248 	ip = VTOI(vp);
1249 	ufs_ihashrem(ip);
1250 
1251 	/*
1252 	 * Purge old data structures associated with the inode.
1253 	 */
1254 	cache_purge(vp);
1255 	if (ip->i_devvp)
1256 		vrele(ip->i_devvp);
1257 
1258 	if (ip->i_e2din != NULL)
1259 		pool_put(&ext2fs_dinode_pool, ip->i_e2din);
1260 
1261 	pool_put(&ext2fs_inode_pool, ip);
1262 
1263 	vp->v_data = NULL;
1264 
1265 	return (0);
1266 }
1267 
1268 /* Global vfs data structures for ext2fs. */
1269 const struct vops ext2fs_vops = {
1270         .vop_lookup     = ext2fs_lookup,
1271         .vop_create     = ext2fs_create,
1272         .vop_mknod      = ext2fs_mknod,
1273         .vop_open       = ext2fs_open,
1274         .vop_close      = ufs_close,
1275         .vop_access     = ext2fs_access,
1276         .vop_getattr    = ext2fs_getattr,
1277         .vop_setattr    = ext2fs_setattr,
1278         .vop_read       = ext2fs_read,
1279         .vop_write      = ext2fs_write,
1280         .vop_ioctl      = ufs_ioctl,
1281         .vop_poll       = ufs_poll,
1282         .vop_kqfilter   = ufs_kqfilter,
1283         .vop_revoke     = NULL,
1284         .vop_fsync      = ext2fs_fsync,
1285         .vop_remove     = ext2fs_remove,
1286         .vop_link       = ext2fs_link,
1287         .vop_rename     = ext2fs_rename,
1288         .vop_mkdir      = ext2fs_mkdir,
1289         .vop_rmdir      = ext2fs_rmdir,
1290         .vop_symlink    = ext2fs_symlink,
1291         .vop_readdir    = ext2fs_readdir,
1292         .vop_readlink   = ext2fs_readlink,
1293         .vop_abortop    = vop_generic_abortop,
1294         .vop_inactive   = ext2fs_inactive,
1295         .vop_reclaim    = ext2fs_reclaim,
1296         .vop_lock       = ufs_lock,
1297         .vop_unlock     = ufs_unlock,
1298         .vop_bmap       = ext2fs_bmap,
1299         .vop_strategy   = ufs_strategy,
1300         .vop_print      = ufs_print,
1301         .vop_islocked   = ufs_islocked,
1302         .vop_pathconf   = ext2fs_pathconf,
1303         .vop_advlock    = ext2fs_advlock,
1304         .vop_bwrite     = vop_generic_bwrite,
1305 };
1306 
1307 const struct vops ext2fs_specvops = {
1308         .vop_close      = ufsspec_close,
1309         .vop_access     = ext2fs_access,
1310         .vop_getattr    = ext2fs_getattr,
1311         .vop_setattr    = ext2fs_setattr,
1312         .vop_read       = ufsspec_read,
1313         .vop_write      = ufsspec_write,
1314         .vop_fsync      = ext2fs_fsync,
1315         .vop_inactive   = ext2fs_inactive,
1316         .vop_reclaim    = ext2fs_reclaim,
1317         .vop_lock       = ufs_lock,
1318         .vop_unlock     = ufs_unlock,
1319         .vop_print      = ufs_print,
1320         .vop_islocked   = ufs_islocked,
1321 
1322         /* XXX: Keep in sync with spec_vops. */
1323 	.vop_lookup	= vop_generic_lookup,
1324 	.vop_create	= vop_generic_badop,
1325 	.vop_mknod	= vop_generic_badop,
1326 	.vop_open	= spec_open,
1327 	.vop_ioctl	= spec_ioctl,
1328 	.vop_poll	= spec_poll,
1329 	.vop_kqfilter	= spec_kqfilter,
1330 	.vop_revoke	= vop_generic_revoke,
1331 	.vop_remove	= vop_generic_badop,
1332 	.vop_link	= vop_generic_badop,
1333 	.vop_rename	= vop_generic_badop,
1334 	.vop_mkdir	= vop_generic_badop,
1335 	.vop_rmdir	= vop_generic_badop,
1336 	.vop_symlink	= vop_generic_badop,
1337 	.vop_readdir	= vop_generic_badop,
1338 	.vop_readlink	= vop_generic_badop,
1339 	.vop_abortop	= vop_generic_badop,
1340 	.vop_bmap	= vop_generic_bmap,
1341 	.vop_strategy	= spec_strategy,
1342 	.vop_pathconf	= spec_pathconf,
1343 	.vop_advlock	= spec_advlock,
1344 	.vop_bwrite	= vop_generic_bwrite,
1345 };
1346 
1347 #ifdef FIFO
1348 const struct vops ext2fs_fifovops = {
1349         .vop_close      = ufsfifo_close,
1350         .vop_access     = ufsfifo_close,
1351         .vop_getattr    = ext2fs_getattr,
1352         .vop_setattr    = ext2fs_setattr,
1353         .vop_read       = ufsfifo_read,
1354         .vop_write      = ufsfifo_write,
1355         .vop_fsync      = ext2fs_fsync,
1356         .vop_inactive   = ext2fs_inactive,
1357         .vop_reclaim    = ext2fsfifo_reclaim,
1358         .vop_lock       = ufs_lock,
1359         .vop_unlock     = ufs_unlock,
1360         .vop_print      = ufs_print,
1361         .vop_islocked   = ufs_islocked,
1362         .vop_bwrite     = vop_generic_bwrite,
1363 
1364         /* XXX: Keep in sync with fifo_vops */
1365 	.vop_lookup	= vop_generic_lookup,
1366 	.vop_create	= vop_generic_badop,
1367 	.vop_mknod	= vop_generic_badop,
1368 	.vop_open	= fifo_open,
1369 	.vop_ioctl	= fifo_ioctl,
1370 	.vop_poll	= fifo_poll,
1371 	.vop_kqfilter	= fifo_kqfilter,
1372 	.vop_revoke	= vop_generic_revoke,
1373 	.vop_remove	= vop_generic_badop,
1374 	.vop_link	= vop_generic_badop,
1375 	.vop_rename	= vop_generic_badop,
1376 	.vop_mkdir	= vop_generic_badop,
1377 	.vop_rmdir	= vop_generic_badop,
1378 	.vop_symlink	= vop_generic_badop,
1379 	.vop_readdir	= vop_generic_badop,
1380 	.vop_readlink	= vop_generic_badop,
1381 	.vop_abortop	= vop_generic_badop,
1382 	.vop_bmap	= vop_generic_bmap,
1383 	.vop_strategy	= vop_generic_badop,
1384 	.vop_pathconf	= fifo_pathconf,
1385 	.vop_advlock	= fifo_advlock,
1386 };
1387 
1388 int
1389 ext2fsfifo_reclaim(void *v)
1390 {
1391 	fifo_reclaim(v);
1392 	return (ext2fs_reclaim(v));
1393 }
1394 #endif /* FIFO */
1395