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