1/*-
2 * Copyright (c) 1991, 1993, 1995
3 *	The Regents of the University of California.  All rights reserved.
4 * (c) UNIX System Laboratories, Inc.
5 * All or some portions of this file are derived from material licensed
6 * to the University of California by American Telephone and Telegraph
7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8 * the permission of UNIX System Laboratories, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	@(#)ufs_inode.c	8.9 (Berkeley) 5/14/95
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include "opt_quota.h"
41#include "opt_ufs.h"
42
43#include <sys/param.h>
44#include <sys/systm.h>
45#include <sys/vnode.h>
46#include <sys/lock.h>
47#include <sys/mount.h>
48#include <sys/malloc.h>
49#include <sys/mutex.h>
50
51#include <ufs/ufs/extattr.h>
52#include <ufs/ufs/quota.h>
53#include <ufs/ufs/inode.h>
54#include <ufs/ufs/ufsmount.h>
55#include <ufs/ufs/ufs_extern.h>
56#ifdef UFS_DIRHASH
57#include <ufs/ufs/dir.h>
58#include <ufs/ufs/dirhash.h>
59#endif
60#ifdef UFS_GJOURNAL
61#include <ufs/ufs/gjournal.h>
62#endif
63
64/*
65 * Last reference to an inode.  If necessary, write or delete it.
66 */
67int
68ufs_inactive(ap)
69	struct vop_inactive_args /* {
70		struct vnode *a_vp;
71		struct thread *a_td;
72	} */ *ap;
73{
74	struct vnode *vp = ap->a_vp;
75	struct inode *ip = VTOI(vp);
76	struct thread *td = ap->a_td;
77	mode_t mode;
78	int error = 0;
79	off_t isize;
80	struct mount *mp;
81
82	mp = NULL;
83	/*
84	 * Ignore inodes related to stale file handles.
85	 */
86	if (ip->i_mode == 0)
87		goto out;
88#ifdef UFS_GJOURNAL
89	ufs_gjournal_close(vp);
90#endif
91#ifdef QUOTA
92	/*
93	 * Before moving off the active list, we must be sure that
94	 * any modified quotas have been pushed since these will no
95	 * longer be checked once the vnode is on the inactive list.
96	 */
97	qsyncvp(vp);
98#endif
99	if ((ip->i_effnlink == 0 && DOINGSOFTDEP(vp)) ||
100	    (ip->i_nlink <= 0 && !UFS_RDONLY(ip))) {
101	loop:
102		if (vn_start_secondary_write(vp, &mp, V_NOWAIT) != 0) {
103			/* Cannot delete file while file system is suspended */
104			if ((vp->v_iflag & VI_DOOMED) != 0) {
105				/* Cannot return before file is deleted */
106				(void) vn_start_secondary_write(vp, &mp,
107								V_WAIT);
108			} else {
109				MNT_ILOCK(mp);
110				if ((mp->mnt_kern_flag &
111				     (MNTK_SUSPEND2 | MNTK_SUSPENDED)) == 0) {
112					MNT_IUNLOCK(mp);
113					goto loop;
114				}
115				/*
116				 * Fail to inactivate vnode now and
117				 * let ffs_snapshot() clean up after
118				 * it has resumed the file system.
119				 */
120				VI_LOCK(vp);
121				vp->v_iflag |= VI_OWEINACT;
122				VI_UNLOCK(vp);
123				MNT_IUNLOCK(mp);
124				return (0);
125			}
126		}
127	}
128	isize = ip->i_size;
129	if (ip->i_ump->um_fstype == UFS2)
130		isize += ip->i_din2->di_extsize;
131	if (ip->i_effnlink <= 0 && isize && !UFS_RDONLY(ip))
132		error = UFS_TRUNCATE(vp, (off_t)0, IO_EXT | IO_NORMAL,
133		    NOCRED, td);
134	if (ip->i_nlink <= 0 && ip->i_mode && !UFS_RDONLY(ip)) {
135#ifdef QUOTA
136		if (!getinoquota(ip))
137			(void)chkiq(ip, -1, NOCRED, FORCE);
138#endif
139#ifdef UFS_EXTATTR
140		ufs_extattr_vnode_inactive(vp, td);
141#endif
142		/*
143		 * Setting the mode to zero needs to wait for the inode
144		 * to be written just as does a change to the link count.
145		 * So, rather than creating a new entry point to do the
146		 * same thing, we just use softdep_change_linkcnt().
147		 */
148		DIP_SET(ip, i_rdev, 0);
149		mode = ip->i_mode;
150		ip->i_mode = 0;
151		DIP_SET(ip, i_mode, 0);
152		ip->i_flag |= IN_CHANGE | IN_UPDATE;
153		if (DOINGSOFTDEP(vp))
154			softdep_change_linkcnt(ip);
155		UFS_VFREE(vp, ip->i_number, mode);
156	}
157	if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
158		if ((ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) == 0 &&
159		    mp == NULL &&
160		    vn_start_secondary_write(vp, &mp, V_NOWAIT)) {
161			mp = NULL;
162			ip->i_flag &= ~IN_ACCESS;
163		} else {
164			if (mp == NULL)
165				(void) vn_start_secondary_write(vp, &mp,
166								V_WAIT);
167			UFS_UPDATE(vp, 0);
168		}
169	}
170out:
171	/*
172	 * If we are done with the inode, reclaim it
173	 * so that it can be reused immediately.
174	 */
175	if (ip->i_mode == 0)
176		vrecycle(vp, td);
177	if (mp != NULL)
178		vn_finished_secondary_write(mp);
179	return (error);
180}
181
182void
183ufs_prepare_reclaim(struct vnode *vp)
184{
185	struct inode *ip;
186#ifdef QUOTA
187	int i;
188#endif
189
190	ip = VTOI(vp);
191
192	vnode_destroy_vobject(vp);
193#ifdef QUOTA
194	for (i = 0; i < MAXQUOTAS; i++) {
195		if (ip->i_dquot[i] != NODQUOT) {
196			dqrele(vp, ip->i_dquot[i]);
197			ip->i_dquot[i] = NODQUOT;
198		}
199	}
200#endif
201#ifdef UFS_DIRHASH
202	if (ip->i_dirhash != NULL)
203		ufsdirhash_free(ip);
204#endif
205}
206
207/*
208 * Reclaim an inode so that it can be used for other purposes.
209 */
210int
211ufs_reclaim(ap)
212	struct vop_reclaim_args /* {
213		struct vnode *a_vp;
214		struct thread *a_td;
215	} */ *ap;
216{
217	struct vnode *vp = ap->a_vp;
218	struct inode *ip = VTOI(vp);
219	struct ufsmount *ump = ip->i_ump;
220
221	ufs_prepare_reclaim(vp);
222
223	if (ip->i_flag & IN_LAZYMOD)
224		ip->i_flag |= IN_MODIFIED;
225	UFS_UPDATE(vp, 0);
226	/*
227	 * Remove the inode from its hash chain.
228	 */
229	vfs_hash_remove(vp);
230
231	/*
232	 * Lock the clearing of v_data so ffs_lock() can inspect it
233	 * prior to obtaining the lock.
234	 */
235	VI_LOCK(vp);
236	vp->v_data = 0;
237	VI_UNLOCK(vp);
238	UFS_IFREE(ump, ip);
239	return (0);
240}
241