PageRenderTime 11ms CodeModel.GetById 7ms app.highlight 101ms RepoModel.GetById 1ms app.codeStats 1ms

/sys/ufs/ext2fs/ext2fs_vnops.c

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