xref: /minix3/sys/ufs/ext2fs/ext2fs_lookup.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: ext2fs_lookup.c,v 1.78 2015/03/27 17:27:56 riastradh Exp $	*/
2d65f6f70SBen Gras 
3d65f6f70SBen Gras /*
4d65f6f70SBen Gras  * Modified for NetBSD 1.2E
5d65f6f70SBen Gras  * May 1997, Manuel Bouyer
6d65f6f70SBen Gras  * Laboratoire d'informatique de Paris VI
7d65f6f70SBen Gras  */
8d65f6f70SBen Gras /*
9d65f6f70SBen Gras  *  modified for Lites 1.1
10d65f6f70SBen Gras  *
11d65f6f70SBen Gras  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
12d65f6f70SBen Gras  *  University of Utah, Department of Computer Science
13d65f6f70SBen Gras  */
14d65f6f70SBen Gras /*
15d65f6f70SBen Gras  * Copyright (c) 1989, 1993
16d65f6f70SBen Gras  *	The Regents of the University of California.  All rights reserved.
17d65f6f70SBen Gras  * (c) UNIX System Laboratories, Inc.
18d65f6f70SBen Gras  * All or some portions of this file are derived from material licensed
19d65f6f70SBen Gras  * to the University of California by American Telephone and Telegraph
20d65f6f70SBen Gras  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
21d65f6f70SBen Gras  * the permission of UNIX System Laboratories, Inc.
22d65f6f70SBen Gras  *
23d65f6f70SBen Gras  * Redistribution and use in source and binary forms, with or without
24d65f6f70SBen Gras  * modification, are permitted provided that the following conditions
25d65f6f70SBen Gras  * are met:
26d65f6f70SBen Gras  * 1. Redistributions of source code must retain the above copyright
27d65f6f70SBen Gras  *    notice, this list of conditions and the following disclaimer.
28d65f6f70SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
29d65f6f70SBen Gras  *    notice, this list of conditions and the following disclaimer in the
30d65f6f70SBen Gras  *    documentation and/or other materials provided with the distribution.
31d65f6f70SBen Gras  * 3. Neither the name of the University nor the names of its contributors
32d65f6f70SBen Gras  *    may be used to endorse or promote products derived from this software
33d65f6f70SBen Gras  *    without specific prior written permission.
34d65f6f70SBen Gras  *
35d65f6f70SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36d65f6f70SBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37d65f6f70SBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38d65f6f70SBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39d65f6f70SBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40d65f6f70SBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41d65f6f70SBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42d65f6f70SBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43d65f6f70SBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44d65f6f70SBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45d65f6f70SBen Gras  * SUCH DAMAGE.
46d65f6f70SBen Gras  *
47d65f6f70SBen Gras  *	@(#)ufs_lookup.c	8.6 (Berkeley) 4/1/94
48d65f6f70SBen Gras  */
49d65f6f70SBen Gras 
50d65f6f70SBen Gras #include <sys/cdefs.h>
51*0a6a1f1dSLionel Sambuc __KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.78 2015/03/27 17:27:56 riastradh Exp $");
52d65f6f70SBen Gras 
53d65f6f70SBen Gras #include <sys/param.h>
54d65f6f70SBen Gras #include <sys/systm.h>
55d65f6f70SBen Gras #include <sys/namei.h>
56d65f6f70SBen Gras #include <sys/buf.h>
57d65f6f70SBen Gras #include <sys/file.h>
58d65f6f70SBen Gras #include <sys/mount.h>
59d65f6f70SBen Gras #include <sys/vnode.h>
6084d9c625SLionel Sambuc #include <sys/kmem.h>
61d65f6f70SBen Gras #include <sys/malloc.h>
62d65f6f70SBen Gras #include <sys/dirent.h>
63d65f6f70SBen Gras #include <sys/kauth.h>
64d65f6f70SBen Gras #include <sys/proc.h>
65d65f6f70SBen Gras 
66d65f6f70SBen Gras #include <ufs/ufs/inode.h>
67d65f6f70SBen Gras #include <ufs/ufs/ufsmount.h>
68d65f6f70SBen Gras #include <ufs/ufs/ufs_extern.h>
69d65f6f70SBen Gras 
70d65f6f70SBen Gras #include <ufs/ext2fs/ext2fs_extern.h>
71d65f6f70SBen Gras #include <ufs/ext2fs/ext2fs_dir.h>
72d65f6f70SBen Gras #include <ufs/ext2fs/ext2fs.h>
73d65f6f70SBen Gras 
7484d9c625SLionel Sambuc #include <miscfs/genfs/genfs.h>
7584d9c625SLionel Sambuc 
76d65f6f70SBen Gras extern	int dirchk;
77d65f6f70SBen Gras 
78d65f6f70SBen Gras static void	ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir,
79d65f6f70SBen Gras 					  struct dirent *ffsdir);
80d65f6f70SBen Gras static int	ext2fs_dirbadentry(struct vnode *dp,
81d65f6f70SBen Gras 					  struct ext2fs_direct *de,
82d65f6f70SBen Gras 					  int entryoffsetinblock);
83d65f6f70SBen Gras 
84d65f6f70SBen Gras /*
85d65f6f70SBen Gras  * the problem that is tackled below is the fact that FFS
86d65f6f70SBen Gras  * includes the terminating zero on disk while EXT2FS doesn't
87d65f6f70SBen Gras  * this implies that we need to introduce some padding.
88d65f6f70SBen Gras  * For instance, a filename "sbin" has normally a reclen 12
89d65f6f70SBen Gras  * in EXT2, but 16 in FFS.
90d65f6f70SBen Gras  * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...'
91d65f6f70SBen Gras  * If it wasn't for that, the complete ufs code for directories would
92d65f6f70SBen Gras  * have worked w/o changes (except for the difference in DIRBLKSIZ)
93d65f6f70SBen Gras  */
94d65f6f70SBen Gras static void
ext2fs_dirconv2ffs(struct ext2fs_direct * e2dir,struct dirent * ffsdir)95d65f6f70SBen Gras ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, struct dirent *ffsdir)
96d65f6f70SBen Gras {
97d65f6f70SBen Gras 	memset(ffsdir, 0, sizeof(struct dirent));
98d65f6f70SBen Gras 	ffsdir->d_fileno = fs2h32(e2dir->e2d_ino);
99d65f6f70SBen Gras 	ffsdir->d_namlen = e2dir->e2d_namlen;
100d65f6f70SBen Gras 
101d65f6f70SBen Gras 	ffsdir->d_type = DT_UNKNOWN;		/* don't know more here */
102d65f6f70SBen Gras #ifdef DIAGNOSTIC
103d65f6f70SBen Gras #if MAXNAMLEN < E2FS_MAXNAMLEN
104d65f6f70SBen Gras 	/*
105d65f6f70SBen Gras 	 * we should handle this more gracefully !
106d65f6f70SBen Gras 	 */
107d65f6f70SBen Gras 	if (e2dir->e2d_namlen > MAXNAMLEN)
108d65f6f70SBen Gras 		panic("ext2fs: e2dir->e2d_namlen");
109d65f6f70SBen Gras #endif
110d65f6f70SBen Gras #endif
111d65f6f70SBen Gras 	strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen);
112d65f6f70SBen Gras 
113d65f6f70SBen Gras 	/* Godmar thinks: since e2dir->e2d_reclen can be big and means
114d65f6f70SBen Gras 	   nothing anyway, we compute our own reclen according to what
115d65f6f70SBen Gras 	   we think is right
116d65f6f70SBen Gras 	 */
117d65f6f70SBen Gras 	ffsdir->d_reclen = _DIRENT_SIZE(ffsdir);
118d65f6f70SBen Gras }
119d65f6f70SBen Gras 
120d65f6f70SBen Gras /*
121d65f6f70SBen Gras  * Vnode op for reading directories.
122d65f6f70SBen Gras  *
123d65f6f70SBen Gras  * Convert the on-disk entries to <sys/dirent.h> entries.
124d65f6f70SBen Gras  * the problem is that the conversion will blow up some entries by four bytes,
125d65f6f70SBen Gras  * so it can't be done in place. This is too bad. Right now the conversion is
126d65f6f70SBen Gras  * done entry by entry, the converted entry is sent via uiomove.
127d65f6f70SBen Gras  *
128d65f6f70SBen Gras  * XXX allocate a buffer, convert as many entries as possible, then send
129d65f6f70SBen Gras  * the whole buffer to uiomove
130d65f6f70SBen Gras  */
131d65f6f70SBen Gras int
ext2fs_readdir(void * v)132d65f6f70SBen Gras ext2fs_readdir(void *v)
133d65f6f70SBen Gras {
134d65f6f70SBen Gras 	struct vop_readdir_args /* {
135d65f6f70SBen Gras 		struct vnode *a_vp;
136d65f6f70SBen Gras 		struct uio *a_uio;
137d65f6f70SBen Gras 		kauth_cred_t a_cred;
138d65f6f70SBen Gras 		int **a_eofflag;
139d65f6f70SBen Gras 		off_t **a_cookies;
140d65f6f70SBen Gras 		int ncookies;
141d65f6f70SBen Gras 	} */ *ap = v;
142d65f6f70SBen Gras 	struct uio *uio = ap->a_uio;
143d65f6f70SBen Gras 	int error;
144d65f6f70SBen Gras 	size_t e2fs_count, readcnt;
145d65f6f70SBen Gras 	struct vnode *vp = ap->a_vp;
146d65f6f70SBen Gras 	struct m_ext2fs *fs = VTOI(vp)->i_e2fs;
147d65f6f70SBen Gras 
148d65f6f70SBen Gras 	struct ext2fs_direct *dp;
149d65f6f70SBen Gras 	struct dirent *dstd;
150d65f6f70SBen Gras 	struct uio auio;
151d65f6f70SBen Gras 	struct iovec aiov;
152d65f6f70SBen Gras 	void *dirbuf;
153d65f6f70SBen Gras 	off_t off = uio->uio_offset;
154d65f6f70SBen Gras 	off_t *cookies = NULL;
155d65f6f70SBen Gras 	int nc = 0, ncookies = 0;
156d65f6f70SBen Gras 	int e2d_reclen;
157d65f6f70SBen Gras 
158d65f6f70SBen Gras 	if (vp->v_type != VDIR)
159d65f6f70SBen Gras 		return (ENOTDIR);
160d65f6f70SBen Gras 
161d65f6f70SBen Gras 	e2fs_count = uio->uio_resid;
162d65f6f70SBen Gras 	/* Make sure we don't return partial entries. */
163d65f6f70SBen Gras 	e2fs_count -= (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize -1);
164d65f6f70SBen Gras 	if (e2fs_count <= 0)
165d65f6f70SBen Gras 		return (EINVAL);
166d65f6f70SBen Gras 
167d65f6f70SBen Gras 	auio = *uio;
168d65f6f70SBen Gras 	auio.uio_iov = &aiov;
169d65f6f70SBen Gras 	auio.uio_iovcnt = 1;
170d65f6f70SBen Gras 	aiov.iov_len = e2fs_count;
171d65f6f70SBen Gras 	auio.uio_resid = e2fs_count;
172d65f6f70SBen Gras 	UIO_SETUP_SYSSPACE(&auio);
17384d9c625SLionel Sambuc 	dirbuf = kmem_alloc(e2fs_count, KM_SLEEP);
17484d9c625SLionel Sambuc 	dstd = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
175d65f6f70SBen Gras 	if (ap->a_ncookies) {
176d65f6f70SBen Gras 		nc = e2fs_count / _DIRENT_MINSIZE((struct dirent *)0);
177d65f6f70SBen Gras 		ncookies = nc;
178d65f6f70SBen Gras 		cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK);
179d65f6f70SBen Gras 		*ap->a_cookies = cookies;
180d65f6f70SBen Gras 	}
181d65f6f70SBen Gras 	aiov.iov_base = dirbuf;
182d65f6f70SBen Gras 
183*0a6a1f1dSLionel Sambuc 	error = UFS_BUFRD(ap->a_vp, &auio, 0, ap->a_cred);
184d65f6f70SBen Gras 	if (error == 0) {
185d65f6f70SBen Gras 		readcnt = e2fs_count - auio.uio_resid;
186d65f6f70SBen Gras 		for (dp = (struct ext2fs_direct *)dirbuf;
187d65f6f70SBen Gras 			(char *)dp < (char *)dirbuf + readcnt; ) {
188d65f6f70SBen Gras 			e2d_reclen = fs2h16(dp->e2d_reclen);
189d65f6f70SBen Gras 			if (e2d_reclen == 0) {
190d65f6f70SBen Gras 				error = EIO;
191d65f6f70SBen Gras 				break;
192d65f6f70SBen Gras 			}
193d65f6f70SBen Gras 			ext2fs_dirconv2ffs(dp, dstd);
194d65f6f70SBen Gras 			if(dstd->d_reclen > uio->uio_resid) {
195d65f6f70SBen Gras 				break;
196d65f6f70SBen Gras 			}
197d65f6f70SBen Gras 			error = uiomove(dstd, dstd->d_reclen, uio);
198d65f6f70SBen Gras 			if (error != 0) {
199d65f6f70SBen Gras 				break;
200d65f6f70SBen Gras 			}
201d65f6f70SBen Gras 			off = off + e2d_reclen;
202d65f6f70SBen Gras 			if (cookies != NULL) {
203d65f6f70SBen Gras 				*cookies++ = off;
204d65f6f70SBen Gras 				if (--ncookies <= 0){
205d65f6f70SBen Gras 					break;  /* out of cookies */
206d65f6f70SBen Gras 				}
207d65f6f70SBen Gras 			}
208d65f6f70SBen Gras 			/* advance dp */
209d65f6f70SBen Gras 			dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen);
210d65f6f70SBen Gras 		}
211d65f6f70SBen Gras 		/* we need to correct uio_offset */
212d65f6f70SBen Gras 		uio->uio_offset = off;
213d65f6f70SBen Gras 	}
21484d9c625SLionel Sambuc 	kmem_free(dirbuf, e2fs_count);
21584d9c625SLionel Sambuc 	kmem_free(dstd, sizeof(*dstd));
216d65f6f70SBen Gras 	*ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset;
217d65f6f70SBen Gras 	if (ap->a_ncookies) {
218d65f6f70SBen Gras 		if (error) {
219d65f6f70SBen Gras 			free(*ap->a_cookies, M_TEMP);
220d65f6f70SBen Gras 			*ap->a_ncookies = 0;
221d65f6f70SBen Gras 			*ap->a_cookies = NULL;
222d65f6f70SBen Gras 		} else
223d65f6f70SBen Gras 			*ap->a_ncookies = nc - ncookies;
224d65f6f70SBen Gras 	}
225d65f6f70SBen Gras 	return (error);
226d65f6f70SBen Gras }
227d65f6f70SBen Gras 
228d65f6f70SBen Gras /*
229d65f6f70SBen Gras  * Convert a component of a pathname into a pointer to a locked inode.
230d65f6f70SBen Gras  * This is a very central and rather complicated routine.
231d65f6f70SBen Gras  * If the file system is not maintained in a strict tree hierarchy,
232d65f6f70SBen Gras  * this can result in a deadlock situation (see comments in code below).
233d65f6f70SBen Gras  *
234d65f6f70SBen Gras  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
235d65f6f70SBen Gras  * on whether the name is to be looked up, created, renamed, or deleted.
236d65f6f70SBen Gras  * When CREATE, RENAME, or DELETE is specified, information usable in
237d65f6f70SBen Gras  * creating, renaming, or deleting a directory entry may be calculated.
238d65f6f70SBen Gras  * If flag has LOCKPARENT or'ed into it and the target of the pathname
239d65f6f70SBen Gras  * exists, lookup returns both the target and its parent directory locked.
240d65f6f70SBen Gras  * When creating or renaming and LOCKPARENT is specified, the target may
241d65f6f70SBen Gras  * not be ".".  When deleting and LOCKPARENT is specified, the target may
242d65f6f70SBen Gras  * be "."., but the caller must check to ensure it does an vrele and vput
243d65f6f70SBen Gras  * instead of two vputs.
244d65f6f70SBen Gras  *
245d65f6f70SBen Gras  * Overall outline of ext2fs_lookup:
246d65f6f70SBen Gras  *
247d65f6f70SBen Gras  *	check accessibility of directory
248d65f6f70SBen Gras  *	look for name in cache, if found, then if at end of path
249d65f6f70SBen Gras  *	  and deleting or creating, drop it, else return name
250d65f6f70SBen Gras  *	search for name in directory, to found or notfound
251d65f6f70SBen Gras  * notfound:
252d65f6f70SBen Gras  *	if creating, return locked directory, leaving info on available slots
253d65f6f70SBen Gras  *	else return error
254d65f6f70SBen Gras  * found:
255d65f6f70SBen Gras  *	if at end of path and deleting, return information to allow delete
256d65f6f70SBen Gras  *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
257d65f6f70SBen Gras  *	  inode and return info to allow rewrite
258d65f6f70SBen Gras  *	if not at end, add name to cache; if at end and neither creating
259d65f6f70SBen Gras  *	  nor deleting, add name to cache
260d65f6f70SBen Gras  */
261d65f6f70SBen Gras int
ext2fs_lookup(void * v)262d65f6f70SBen Gras ext2fs_lookup(void *v)
263d65f6f70SBen Gras {
264*0a6a1f1dSLionel Sambuc 	struct vop_lookup_v2_args /* {
265d65f6f70SBen Gras 		struct vnode *a_dvp;
266d65f6f70SBen Gras 		struct vnode **a_vpp;
267d65f6f70SBen Gras 		struct componentname *a_cnp;
268d65f6f70SBen Gras 	} */ *ap = v;
269d65f6f70SBen Gras 	struct vnode *vdp = ap->a_dvp;	/* vnode for directory being searched */
270d65f6f70SBen Gras 	struct inode *dp = VTOI(vdp);	/* inode for directory being searched */
271d65f6f70SBen Gras 	struct buf *bp;			/* a buffer of directory entries */
272d65f6f70SBen Gras 	struct ext2fs_direct *ep;	/* the current directory entry */
273d65f6f70SBen Gras 	int entryoffsetinblock;		/* offset of ep in bp's buffer */
274d65f6f70SBen Gras 	enum {NONE, COMPACT, FOUND} slotstatus;
275d65f6f70SBen Gras 	doff_t slotoffset;		/* offset of area with free space */
276d65f6f70SBen Gras 	int slotsize;			/* size of area at slotoffset */
277d65f6f70SBen Gras 	int slotfreespace;		/* amount of space free in slot */
278d65f6f70SBen Gras 	int slotneeded;			/* size of the entry we're seeking */
279d65f6f70SBen Gras 	int numdirpasses;		/* strategy for directory search */
280d65f6f70SBen Gras 	doff_t endsearch;		/* offset to end directory search */
281d65f6f70SBen Gras 	doff_t prevoff;			/* prev entry dp->i_offset */
282*0a6a1f1dSLionel Sambuc 	struct vnode *tdp;		/* returned by vcache_get */
283d65f6f70SBen Gras 	doff_t enduseful;		/* pointer past last used dir slot */
284d65f6f70SBen Gras 	u_long bmask;			/* block offset mask */
285d65f6f70SBen Gras 	int namlen, error;
286d65f6f70SBen Gras 	struct vnode **vpp = ap->a_vpp;
287d65f6f70SBen Gras 	struct componentname *cnp = ap->a_cnp;
288d65f6f70SBen Gras 	kauth_cred_t cred = cnp->cn_cred;
289d65f6f70SBen Gras 	int flags;
290d65f6f70SBen Gras 	int nameiop = cnp->cn_nameiop;
291d65f6f70SBen Gras 	struct ufsmount *ump = dp->i_ump;
292d65f6f70SBen Gras 	int dirblksiz = ump->um_dirblksiz;
293d65f6f70SBen Gras 	ino_t foundino;
294d65f6f70SBen Gras 	struct ufs_lookup_results *results;
295d65f6f70SBen Gras 
296d65f6f70SBen Gras 	flags = cnp->cn_flags;
297d65f6f70SBen Gras 
298d65f6f70SBen Gras 	bp = NULL;
299d65f6f70SBen Gras 	slotoffset = -1;
300d65f6f70SBen Gras 	*vpp = NULL;
301d65f6f70SBen Gras 
302d65f6f70SBen Gras 	/*
303d65f6f70SBen Gras 	 * Produce the auxiliary lookup results into i_crap. Increment
304d65f6f70SBen Gras 	 * its serial number so elsewhere we can tell if we're using
305d65f6f70SBen Gras 	 * stale results. This should not be done this way. XXX.
306d65f6f70SBen Gras 	 */
307d65f6f70SBen Gras 	results = &dp->i_crap;
308d65f6f70SBen Gras 	dp->i_crapcounter++;
309d65f6f70SBen Gras 
310d65f6f70SBen Gras 	/*
311d65f6f70SBen Gras 	 * Check accessiblity of directory.
312d65f6f70SBen Gras 	 */
313d65f6f70SBen Gras 	if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
314d65f6f70SBen Gras 		return (error);
315d65f6f70SBen Gras 
316d65f6f70SBen Gras 	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
317d65f6f70SBen Gras 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
318d65f6f70SBen Gras 		return (EROFS);
319d65f6f70SBen Gras 
320d65f6f70SBen Gras 	/*
321d65f6f70SBen Gras 	 * We now have a segment name to search for, and a directory to search.
322d65f6f70SBen Gras 	 *
323d65f6f70SBen Gras 	 * Before tediously performing a linear scan of the directory,
324d65f6f70SBen Gras 	 * check the name cache to see if the directory/name pair
325d65f6f70SBen Gras 	 * we are looking for is known already.
326d65f6f70SBen Gras 	 */
32784d9c625SLionel Sambuc 	if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen,
32884d9c625SLionel Sambuc 			 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
32984d9c625SLionel Sambuc 		return *vpp == NULLVP ? ENOENT : 0;
33084d9c625SLionel Sambuc 	}
331d65f6f70SBen Gras 
332d65f6f70SBen Gras 	/*
333d65f6f70SBen Gras 	 * Suppress search for slots unless creating
334d65f6f70SBen Gras 	 * file and at end of pathname, in which case
335d65f6f70SBen Gras 	 * we watch for a place to put the new file in
336d65f6f70SBen Gras 	 * case it doesn't already exist.
337d65f6f70SBen Gras 	 */
338d65f6f70SBen Gras 	slotstatus = FOUND;
339d65f6f70SBen Gras 	slotfreespace = slotsize = slotneeded = 0;
340d65f6f70SBen Gras 	if ((nameiop == CREATE || nameiop == RENAME) &&
341d65f6f70SBen Gras 	    (flags & ISLASTCN)) {
342d65f6f70SBen Gras 		slotstatus = NONE;
343d65f6f70SBen Gras 		slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
344d65f6f70SBen Gras 	}
345d65f6f70SBen Gras 
346d65f6f70SBen Gras 	/*
347d65f6f70SBen Gras 	 * If there is cached information on a previous search of
348d65f6f70SBen Gras 	 * this directory, pick up where we last left off.
349d65f6f70SBen Gras 	 * We cache only lookups as these are the most common
350d65f6f70SBen Gras 	 * and have the greatest payoff. Caching CREATE has little
351d65f6f70SBen Gras 	 * benefit as it usually must search the entire directory
352d65f6f70SBen Gras 	 * to determine that the entry does not exist. Caching the
353d65f6f70SBen Gras 	 * location of the last DELETE or RENAME has not reduced
354d65f6f70SBen Gras 	 * profiling time and hence has been removed in the interest
355d65f6f70SBen Gras 	 * of simplicity.
356d65f6f70SBen Gras 	 */
357d65f6f70SBen Gras 	bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
358d65f6f70SBen Gras 	if (nameiop != LOOKUP || results->ulr_diroff == 0 ||
359d65f6f70SBen Gras 	    results->ulr_diroff >= ext2fs_size(dp)) {
360d65f6f70SBen Gras 		entryoffsetinblock = 0;
361d65f6f70SBen Gras 		results->ulr_offset = 0;
362d65f6f70SBen Gras 		numdirpasses = 1;
363d65f6f70SBen Gras 	} else {
364d65f6f70SBen Gras 		results->ulr_offset = results->ulr_diroff;
365d65f6f70SBen Gras 		if ((entryoffsetinblock = results->ulr_offset & bmask) &&
366d65f6f70SBen Gras 		    (error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL, &bp)))
367d65f6f70SBen Gras 			return (error);
368d65f6f70SBen Gras 		numdirpasses = 2;
369*0a6a1f1dSLionel Sambuc 		namecache_count_2passes();
370d65f6f70SBen Gras 	}
371d65f6f70SBen Gras 	prevoff = results->ulr_offset;
372d65f6f70SBen Gras 	endsearch = roundup(ext2fs_size(dp), dirblksiz);
373d65f6f70SBen Gras 	enduseful = 0;
374d65f6f70SBen Gras 
375d65f6f70SBen Gras searchloop:
376d65f6f70SBen Gras 	while (results->ulr_offset < endsearch) {
377d65f6f70SBen Gras 		if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
378d65f6f70SBen Gras 			preempt();
379d65f6f70SBen Gras 		/*
380d65f6f70SBen Gras 		 * If necessary, get the next directory block.
381d65f6f70SBen Gras 		 */
382d65f6f70SBen Gras 		if ((results->ulr_offset & bmask) == 0) {
383d65f6f70SBen Gras 			if (bp != NULL)
384d65f6f70SBen Gras 				brelse(bp, 0);
385d65f6f70SBen Gras 			error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL,
386d65f6f70SBen Gras 			    &bp);
387d65f6f70SBen Gras 			if (error != 0)
388d65f6f70SBen Gras 				return (error);
389d65f6f70SBen Gras 			entryoffsetinblock = 0;
390d65f6f70SBen Gras 		}
391d65f6f70SBen Gras 		/*
392d65f6f70SBen Gras 		 * If still looking for a slot, and at a dirblksize
393d65f6f70SBen Gras 		 * boundary, have to start looking for free space again.
394d65f6f70SBen Gras 		 */
395d65f6f70SBen Gras 		if (slotstatus == NONE &&
396d65f6f70SBen Gras 		    (entryoffsetinblock & (dirblksiz - 1)) == 0) {
397d65f6f70SBen Gras 			slotoffset = -1;
398d65f6f70SBen Gras 			slotfreespace = 0;
399d65f6f70SBen Gras 		}
400d65f6f70SBen Gras 		/*
401d65f6f70SBen Gras 		 * Get pointer to next entry.
402d65f6f70SBen Gras 		 * Full validation checks are slow, so we only check
403d65f6f70SBen Gras 		 * enough to insure forward progress through the
404d65f6f70SBen Gras 		 * directory. Complete checks can be run by patching
405d65f6f70SBen Gras 		 * "dirchk" to be true.
406d65f6f70SBen Gras 		 */
407d65f6f70SBen Gras 		KASSERT(bp != NULL);
408d65f6f70SBen Gras 		ep = (struct ext2fs_direct *)
409d65f6f70SBen Gras 			((char *)bp->b_data + entryoffsetinblock);
410d65f6f70SBen Gras 		if (ep->e2d_reclen == 0 ||
411d65f6f70SBen Gras 		    (dirchk &&
412d65f6f70SBen Gras 		     ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
413d65f6f70SBen Gras 			int i;
414d65f6f70SBen Gras 
415d65f6f70SBen Gras 			ufs_dirbad(dp, results->ulr_offset, "mangled entry");
416d65f6f70SBen Gras 			i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
417d65f6f70SBen Gras 			results->ulr_offset += i;
418d65f6f70SBen Gras 			entryoffsetinblock += i;
419d65f6f70SBen Gras 			continue;
420d65f6f70SBen Gras 		}
421d65f6f70SBen Gras 
422d65f6f70SBen Gras 		/*
423d65f6f70SBen Gras 		 * If an appropriate sized slot has not yet been found,
424d65f6f70SBen Gras 		 * check to see if one is available. Also accumulate space
425d65f6f70SBen Gras 		 * in the current block so that we can determine if
426d65f6f70SBen Gras 		 * compaction is viable.
427d65f6f70SBen Gras 		 */
428d65f6f70SBen Gras 		if (slotstatus != FOUND) {
429d65f6f70SBen Gras 			int size = fs2h16(ep->e2d_reclen);
430d65f6f70SBen Gras 
431d65f6f70SBen Gras 			if (ep->e2d_ino != 0)
432d65f6f70SBen Gras 				size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
433d65f6f70SBen Gras 			if (size > 0) {
434d65f6f70SBen Gras 				if (size >= slotneeded) {
435d65f6f70SBen Gras 					slotstatus = FOUND;
436d65f6f70SBen Gras 					slotoffset = results->ulr_offset;
437d65f6f70SBen Gras 					slotsize = fs2h16(ep->e2d_reclen);
438d65f6f70SBen Gras 				} else if (slotstatus == NONE) {
439d65f6f70SBen Gras 					slotfreespace += size;
440d65f6f70SBen Gras 					if (slotoffset == -1)
441d65f6f70SBen Gras 						slotoffset = results->ulr_offset;
442d65f6f70SBen Gras 					if (slotfreespace >= slotneeded) {
443d65f6f70SBen Gras 						slotstatus = COMPACT;
444d65f6f70SBen Gras 						slotsize = results->ulr_offset +
445d65f6f70SBen Gras 						    fs2h16(ep->e2d_reclen) -
446d65f6f70SBen Gras 						    slotoffset;
447d65f6f70SBen Gras 					}
448d65f6f70SBen Gras 				}
449d65f6f70SBen Gras 			}
450d65f6f70SBen Gras 		}
451d65f6f70SBen Gras 
452d65f6f70SBen Gras 		/*
453d65f6f70SBen Gras 		 * Check for a name match.
454d65f6f70SBen Gras 		 */
455d65f6f70SBen Gras 		if (ep->e2d_ino) {
456d65f6f70SBen Gras 			namlen = ep->e2d_namlen;
457d65f6f70SBen Gras 			if (namlen == cnp->cn_namelen &&
458d65f6f70SBen Gras 			    !memcmp(cnp->cn_nameptr, ep->e2d_name,
459d65f6f70SBen Gras 			    (unsigned)namlen)) {
460d65f6f70SBen Gras 				/*
461d65f6f70SBen Gras 				 * Save directory entry's inode number and
462d65f6f70SBen Gras 				 * reclen in ndp->ni_ufs area, and release
463d65f6f70SBen Gras 				 * directory buffer.
464d65f6f70SBen Gras 				 */
465d65f6f70SBen Gras 				foundino = fs2h32(ep->e2d_ino);
466d65f6f70SBen Gras 				results->ulr_reclen = fs2h16(ep->e2d_reclen);
467d65f6f70SBen Gras 				goto found;
468d65f6f70SBen Gras 			}
469d65f6f70SBen Gras 		}
470d65f6f70SBen Gras 		prevoff = results->ulr_offset;
471d65f6f70SBen Gras 		results->ulr_offset += fs2h16(ep->e2d_reclen);
472d65f6f70SBen Gras 		entryoffsetinblock += fs2h16(ep->e2d_reclen);
473d65f6f70SBen Gras 		if (ep->e2d_ino)
474d65f6f70SBen Gras 			enduseful = results->ulr_offset;
475d65f6f70SBen Gras 	}
476d65f6f70SBen Gras /* notfound: */
477d65f6f70SBen Gras 	/*
478d65f6f70SBen Gras 	 * If we started in the middle of the directory and failed
479d65f6f70SBen Gras 	 * to find our target, we must check the beginning as well.
480d65f6f70SBen Gras 	 */
481d65f6f70SBen Gras 	if (numdirpasses == 2) {
482d65f6f70SBen Gras 		numdirpasses--;
483d65f6f70SBen Gras 		results->ulr_offset = 0;
484d65f6f70SBen Gras 		endsearch = results->ulr_diroff;
485d65f6f70SBen Gras 		goto searchloop;
486d65f6f70SBen Gras 	}
487d65f6f70SBen Gras 	if (bp != NULL)
488d65f6f70SBen Gras 		brelse(bp, 0);
489d65f6f70SBen Gras 	/*
490d65f6f70SBen Gras 	 * If creating, and at end of pathname and current
491d65f6f70SBen Gras 	 * directory has not been removed, then can consider
492d65f6f70SBen Gras 	 * allowing file to be created.
493d65f6f70SBen Gras 	 */
494d65f6f70SBen Gras 	if ((nameiop == CREATE || nameiop == RENAME) &&
495d65f6f70SBen Gras 	    (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
496d65f6f70SBen Gras 		/*
497d65f6f70SBen Gras 		 * Access for write is interpreted as allowing
498d65f6f70SBen Gras 		 * creation of files in the directory.
499d65f6f70SBen Gras 		 */
500d65f6f70SBen Gras 		error = VOP_ACCESS(vdp, VWRITE, cred);
501d65f6f70SBen Gras 		if (error)
502d65f6f70SBen Gras 			return (error);
503d65f6f70SBen Gras 		/*
504d65f6f70SBen Gras 		 * Return an indication of where the new directory
505d65f6f70SBen Gras 		 * entry should be put.  If we didn't find a slot,
506d65f6f70SBen Gras 		 * then set results->ulr_count to 0 indicating
507d65f6f70SBen Gras 		 * that the new slot belongs at the end of the
508d65f6f70SBen Gras 		 * directory. If we found a slot, then the new entry
509d65f6f70SBen Gras 		 * can be put in the range from results->ulr_offset to
510d65f6f70SBen Gras 		 * results->ulr_offset + results->ulr_count.
511d65f6f70SBen Gras 		 */
512d65f6f70SBen Gras 		if (slotstatus == NONE) {
513d65f6f70SBen Gras 			results->ulr_offset = roundup(ext2fs_size(dp), dirblksiz);
514d65f6f70SBen Gras 			results->ulr_count = 0;
515d65f6f70SBen Gras 			enduseful = results->ulr_offset;
516d65f6f70SBen Gras 		} else {
517d65f6f70SBen Gras 			results->ulr_offset = slotoffset;
518d65f6f70SBen Gras 			results->ulr_count = slotsize;
519d65f6f70SBen Gras 			if (enduseful < slotoffset + slotsize)
520d65f6f70SBen Gras 				enduseful = slotoffset + slotsize;
521d65f6f70SBen Gras 		}
522d65f6f70SBen Gras 		results->ulr_endoff = roundup(enduseful, dirblksiz);
523d65f6f70SBen Gras #if 0
524d65f6f70SBen Gras 		dp->i_flag |= IN_CHANGE | IN_UPDATE;
525d65f6f70SBen Gras #endif
526d65f6f70SBen Gras 		/*
527d65f6f70SBen Gras 		 * We return with the directory locked, so that
528d65f6f70SBen Gras 		 * the parameters we set up above will still be
529d65f6f70SBen Gras 		 * valid if we actually decide to do a direnter().
530d65f6f70SBen Gras 		 * We return ni_vp == NULL to indicate that the entry
531d65f6f70SBen Gras 		 * does not currently exist; we leave a pointer to
532d65f6f70SBen Gras 		 * the (locked) directory inode in ndp->ni_dvp.
533d65f6f70SBen Gras 		 *
534d65f6f70SBen Gras 		 * NB - if the directory is unlocked, then this
535d65f6f70SBen Gras 		 * information cannot be used.
536d65f6f70SBen Gras 		 */
537d65f6f70SBen Gras 		return (EJUSTRETURN);
538d65f6f70SBen Gras 	}
539d65f6f70SBen Gras 	/*
540d65f6f70SBen Gras 	 * Insert name into cache (as non-existent) if appropriate.
541d65f6f70SBen Gras 	 */
54284d9c625SLionel Sambuc 	if (nameiop != CREATE) {
54384d9c625SLionel Sambuc 		cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
54484d9c625SLionel Sambuc 			    cnp->cn_flags);
54584d9c625SLionel Sambuc 	}
54684d9c625SLionel Sambuc 	return ENOENT;
547d65f6f70SBen Gras 
548d65f6f70SBen Gras found:
549d65f6f70SBen Gras 	if (numdirpasses == 2)
550*0a6a1f1dSLionel Sambuc 		namecache_count_pass2();
551d65f6f70SBen Gras 	/*
552d65f6f70SBen Gras 	 * Check that directory length properly reflects presence
553d65f6f70SBen Gras 	 * of this entry.
554d65f6f70SBen Gras 	 */
555d65f6f70SBen Gras 	if (results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) {
556d65f6f70SBen Gras 		ufs_dirbad(dp, results->ulr_offset, "i_size too small");
557d65f6f70SBen Gras 		error = ext2fs_setsize(dp,
558d65f6f70SBen Gras 				results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen));
559d65f6f70SBen Gras 		if (error) {
560d65f6f70SBen Gras 			brelse(bp, 0);
561d65f6f70SBen Gras 			return (error);
562d65f6f70SBen Gras 		}
563d65f6f70SBen Gras 		dp->i_flag |= IN_CHANGE | IN_UPDATE;
564d65f6f70SBen Gras 		uvm_vnp_setsize(vdp, ext2fs_size(dp));
565d65f6f70SBen Gras 	}
566d65f6f70SBen Gras 	brelse(bp, 0);
567d65f6f70SBen Gras 
568d65f6f70SBen Gras 	/*
569d65f6f70SBen Gras 	 * Found component in pathname.
570d65f6f70SBen Gras 	 * If the final component of path name, save information
571d65f6f70SBen Gras 	 * in the cache as to where the entry was found.
572d65f6f70SBen Gras 	 */
573d65f6f70SBen Gras 	if ((flags & ISLASTCN) && nameiop == LOOKUP)
574d65f6f70SBen Gras 		results->ulr_diroff = results->ulr_offset &~ (dirblksiz - 1);
575d65f6f70SBen Gras 
576d65f6f70SBen Gras 	/*
577d65f6f70SBen Gras 	 * If deleting, and at end of pathname, return
578d65f6f70SBen Gras 	 * parameters which can be used to remove file.
579d65f6f70SBen Gras 	 * Lock the inode, being careful with ".".
580d65f6f70SBen Gras 	 */
581d65f6f70SBen Gras 	if (nameiop == DELETE && (flags & ISLASTCN)) {
582d65f6f70SBen Gras 		/*
583d65f6f70SBen Gras 		 * Return pointer to current entry in results->ulr_offset,
584d65f6f70SBen Gras 		 * and distance past previous entry (if there
585d65f6f70SBen Gras 		 * is a previous entry in this block) in results->ulr_count.
586d65f6f70SBen Gras 		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
587d65f6f70SBen Gras 		 */
588d65f6f70SBen Gras 		if ((results->ulr_offset & (dirblksiz - 1)) == 0)
589d65f6f70SBen Gras 			results->ulr_count = 0;
590d65f6f70SBen Gras 		else
591d65f6f70SBen Gras 			results->ulr_count = results->ulr_offset - prevoff;
592d65f6f70SBen Gras 		if (dp->i_number == foundino) {
593d65f6f70SBen Gras 			vref(vdp);
59484d9c625SLionel Sambuc 			tdp = vdp;
59584d9c625SLionel Sambuc 		} else {
596*0a6a1f1dSLionel Sambuc 			error = vcache_get(vdp->v_mount,
597*0a6a1f1dSLionel Sambuc 			    &foundino, sizeof(foundino), &tdp);
598d65f6f70SBen Gras 			if (error)
599d65f6f70SBen Gras 				return (error);
60084d9c625SLionel Sambuc 		}
60184d9c625SLionel Sambuc 		/*
60284d9c625SLionel Sambuc 		 * Write access to directory required to delete files.
60384d9c625SLionel Sambuc 		 */
60484d9c625SLionel Sambuc 		if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) {
60584d9c625SLionel Sambuc 			vrele(tdp);
60684d9c625SLionel Sambuc 			return (error);
60784d9c625SLionel Sambuc 		}
608d65f6f70SBen Gras 		/*
609d65f6f70SBen Gras 		 * If directory is "sticky", then user must own
610d65f6f70SBen Gras 		 * the directory, or the file in it, else she
611d65f6f70SBen Gras 		 * may not delete it (unless she's root). This
612d65f6f70SBen Gras 		 * implements append-only directories.
613d65f6f70SBen Gras 		 */
61484d9c625SLionel Sambuc 		if (dp->i_e2fs_mode & ISVTX) {
61584d9c625SLionel Sambuc 			error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE,
61684d9c625SLionel Sambuc 			    tdp, vdp, genfs_can_sticky(cred, dp->i_uid,
61784d9c625SLionel Sambuc 			    VTOI(tdp)->i_uid));
61884d9c625SLionel Sambuc 			if (error) {
61984d9c625SLionel Sambuc 				vrele(tdp);
620d65f6f70SBen Gras 				return (EPERM);
621d65f6f70SBen Gras 			}
62284d9c625SLionel Sambuc 		}
623d65f6f70SBen Gras 		*vpp = tdp;
624d65f6f70SBen Gras 		return (0);
625d65f6f70SBen Gras 	}
626d65f6f70SBen Gras 
627d65f6f70SBen Gras 	/*
628d65f6f70SBen Gras 	 * If rewriting (RENAME), return the inode and the
629d65f6f70SBen Gras 	 * information required to rewrite the present directory
630d65f6f70SBen Gras 	 * Must get inode of directory entry to verify it's a
631d65f6f70SBen Gras 	 * regular file, or empty directory.
632d65f6f70SBen Gras 	 */
633d65f6f70SBen Gras 	if (nameiop == RENAME && (flags & ISLASTCN)) {
634d65f6f70SBen Gras 		error = VOP_ACCESS(vdp, VWRITE, cred);
635d65f6f70SBen Gras 		if (error)
636d65f6f70SBen Gras 			return (error);
637d65f6f70SBen Gras 		/*
638d65f6f70SBen Gras 		 * Careful about locking second inode.
639d65f6f70SBen Gras 		 * This can only occur if the target is ".".
640d65f6f70SBen Gras 		 */
641d65f6f70SBen Gras 		if (dp->i_number == foundino)
642d65f6f70SBen Gras 			return (EISDIR);
643*0a6a1f1dSLionel Sambuc 		error = vcache_get(vdp->v_mount,
644*0a6a1f1dSLionel Sambuc 		    &foundino, sizeof(foundino), &tdp);
645d65f6f70SBen Gras 		if (error)
646d65f6f70SBen Gras 			return (error);
647d65f6f70SBen Gras 		*vpp = tdp;
648d65f6f70SBen Gras 		return (0);
649d65f6f70SBen Gras 	}
650d65f6f70SBen Gras 
651*0a6a1f1dSLionel Sambuc 	if (dp->i_number == foundino) {
652d65f6f70SBen Gras 		vref(vdp);	/* we want ourself, ie "." */
653d65f6f70SBen Gras 		*vpp = vdp;
654d65f6f70SBen Gras 	} else {
655*0a6a1f1dSLionel Sambuc 		error = vcache_get(vdp->v_mount,
656*0a6a1f1dSLionel Sambuc 		    &foundino, sizeof(foundino), &tdp);
657d65f6f70SBen Gras 		if (error)
658d65f6f70SBen Gras 			return (error);
659d65f6f70SBen Gras 		*vpp = tdp;
660d65f6f70SBen Gras 	}
661d65f6f70SBen Gras 
662d65f6f70SBen Gras 	/*
663d65f6f70SBen Gras 	 * Insert name into cache if appropriate.
664d65f6f70SBen Gras 	 */
66584d9c625SLionel Sambuc 	cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags);
66684d9c625SLionel Sambuc 	return 0;
667d65f6f70SBen Gras }
668d65f6f70SBen Gras 
669d65f6f70SBen Gras /*
670d65f6f70SBen Gras  * Do consistency checking on a directory entry:
671d65f6f70SBen Gras  *	record length must be multiple of 4
672d65f6f70SBen Gras  *	entry must fit in rest of its dirblksize block
673d65f6f70SBen Gras  *	record must be large enough to contain entry
674d65f6f70SBen Gras  *	name is not longer than EXT2FS_MAXNAMLEN
675d65f6f70SBen Gras  *	name must be as long as advertised, and null terminated
676d65f6f70SBen Gras  */
677d65f6f70SBen Gras /*
678d65f6f70SBen Gras  *	changed so that it confirms to ext2fs_check_dir_entry
679d65f6f70SBen Gras  */
680d65f6f70SBen Gras static int
ext2fs_dirbadentry(struct vnode * dp,struct ext2fs_direct * de,int entryoffsetinblock)681d65f6f70SBen Gras ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
682d65f6f70SBen Gras 		int entryoffsetinblock)
683d65f6f70SBen Gras {
684d65f6f70SBen Gras 	struct ufsmount *ump = VFSTOUFS(dp->v_mount);
685d65f6f70SBen Gras 	int dirblksiz = ump->um_dirblksiz;
686d65f6f70SBen Gras 
687d65f6f70SBen Gras 		const char *error_msg = NULL;
688d65f6f70SBen Gras 		int reclen = fs2h16(de->e2d_reclen);
689d65f6f70SBen Gras 		int namlen = de->e2d_namlen;
690d65f6f70SBen Gras 
691d65f6f70SBen Gras 		if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */
692d65f6f70SBen Gras 			error_msg = "rec_len is smaller than minimal";
693d65f6f70SBen Gras 		else if (reclen % 4 != 0)
694d65f6f70SBen Gras 			error_msg = "rec_len % 4 != 0";
695d65f6f70SBen Gras 		else if (namlen > EXT2FS_MAXNAMLEN)
696d65f6f70SBen Gras 			error_msg = "namlen > EXT2FS_MAXNAMLEN";
697d65f6f70SBen Gras 		else if (reclen < EXT2FS_DIRSIZ(namlen))
698d65f6f70SBen Gras 			error_msg = "reclen is too small for name_len";
699d65f6f70SBen Gras 		else if (entryoffsetinblock + reclen > dirblksiz)
700d65f6f70SBen Gras 			error_msg = "directory entry across blocks";
701d65f6f70SBen Gras 		else if (fs2h32(de->e2d_ino) >
702d65f6f70SBen Gras 		    VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
703d65f6f70SBen Gras 			error_msg = "inode out of bounds";
704d65f6f70SBen Gras 
705d65f6f70SBen Gras 		if (error_msg != NULL) {
706d65f6f70SBen Gras 			printf( "bad directory entry: %s\n"
707d65f6f70SBen Gras 			    "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
708d65f6f70SBen Gras 			    error_msg, entryoffsetinblock,
709d65f6f70SBen Gras 			    (unsigned long) fs2h32(de->e2d_ino),
710d65f6f70SBen Gras 			    reclen, namlen);
711d65f6f70SBen Gras 			panic("ext2fs_dirbadentry");
712d65f6f70SBen Gras 		}
713d65f6f70SBen Gras 		return error_msg == NULL ? 0 : 1;
714d65f6f70SBen Gras }
715d65f6f70SBen Gras 
716d65f6f70SBen Gras /*
717d65f6f70SBen Gras  * Write a directory entry after a call to namei, using the parameters
718d65f6f70SBen Gras  * that it left in nameidata.  The argument ip is the inode which the new
719d65f6f70SBen Gras  * directory entry will refer to.  Dvp is a pointer to the directory to
720d65f6f70SBen Gras  * be written, which was left locked by namei. Remaining parameters
721d65f6f70SBen Gras  * (ulr_offset, ulr_count) indicate how the space for the new
722d65f6f70SBen Gras  * entry is to be obtained.
723d65f6f70SBen Gras  */
724d65f6f70SBen Gras int
ext2fs_direnter(struct inode * ip,struct vnode * dvp,const struct ufs_lookup_results * ulr,struct componentname * cnp)725d65f6f70SBen Gras ext2fs_direnter(struct inode *ip, struct vnode *dvp,
726d65f6f70SBen Gras 		const struct ufs_lookup_results *ulr,
727d65f6f70SBen Gras 		struct componentname *cnp)
728d65f6f70SBen Gras {
729d65f6f70SBen Gras 	struct ext2fs_direct *ep, *nep;
730d65f6f70SBen Gras 	struct inode *dp;
731d65f6f70SBen Gras 	struct buf *bp;
732d65f6f70SBen Gras 	struct ext2fs_direct newdir;
733d65f6f70SBen Gras 	struct iovec aiov;
734d65f6f70SBen Gras 	struct uio auio;
735d65f6f70SBen Gras 	u_int dsize;
736d65f6f70SBen Gras 	int error, loc, newentrysize, spacefree;
737d65f6f70SBen Gras 	char *dirbuf;
738d65f6f70SBen Gras 	struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
739d65f6f70SBen Gras 	int dirblksiz = ump->um_dirblksiz;
740d65f6f70SBen Gras 
741d65f6f70SBen Gras 	dp = VTOI(dvp);
742d65f6f70SBen Gras 
743d65f6f70SBen Gras 	newdir.e2d_ino = h2fs32(ip->i_number);
744d65f6f70SBen Gras 	newdir.e2d_namlen = cnp->cn_namelen;
745d65f6f70SBen Gras 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
746d65f6f70SBen Gras 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
747d65f6f70SBen Gras 		newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
748d65f6f70SBen Gras 	} else {
749d65f6f70SBen Gras 		newdir.e2d_type = 0;
750d65f6f70SBen Gras 	}
751d65f6f70SBen Gras 	memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
752d65f6f70SBen Gras 	newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
753d65f6f70SBen Gras 	if (ulr->ulr_count == 0) {
754d65f6f70SBen Gras 		/*
755d65f6f70SBen Gras 		 * If ulr_count is 0, then namei could find no
756d65f6f70SBen Gras 		 * space in the directory. Here, ulr_offset will
757d65f6f70SBen Gras 		 * be on a directory block boundary and we will write the
758d65f6f70SBen Gras 		 * new entry into a fresh block.
759d65f6f70SBen Gras 		 */
760d65f6f70SBen Gras 		if (ulr->ulr_offset & (dirblksiz - 1))
761d65f6f70SBen Gras 			panic("ext2fs_direnter: newblk");
762d65f6f70SBen Gras 		auio.uio_offset = ulr->ulr_offset;
763d65f6f70SBen Gras 		newdir.e2d_reclen = h2fs16(dirblksiz);
764d65f6f70SBen Gras 		auio.uio_resid = newentrysize;
765d65f6f70SBen Gras 		aiov.iov_len = newentrysize;
766d65f6f70SBen Gras 		aiov.iov_base = (void *)&newdir;
767d65f6f70SBen Gras 		auio.uio_iov = &aiov;
768d65f6f70SBen Gras 		auio.uio_iovcnt = 1;
769d65f6f70SBen Gras 		auio.uio_rw = UIO_WRITE;
770d65f6f70SBen Gras 		UIO_SETUP_SYSSPACE(&auio);
771d65f6f70SBen Gras 		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
772d65f6f70SBen Gras 		if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize)
773d65f6f70SBen Gras 			/* XXX should grow with balloc() */
774d65f6f70SBen Gras 			panic("ext2fs_direnter: frag size");
775d65f6f70SBen Gras 		else if (!error) {
776d65f6f70SBen Gras 			error = ext2fs_setsize(dp,
777d65f6f70SBen Gras 				roundup(ext2fs_size(dp), dirblksiz));
778d65f6f70SBen Gras 			if (error)
779d65f6f70SBen Gras 				return (error);
780d65f6f70SBen Gras 			dp->i_flag |= IN_CHANGE;
781d65f6f70SBen Gras 			uvm_vnp_setsize(dvp, ext2fs_size(dp));
782d65f6f70SBen Gras 		}
783d65f6f70SBen Gras 		return (error);
784d65f6f70SBen Gras 	}
785d65f6f70SBen Gras 
786d65f6f70SBen Gras 	/*
787d65f6f70SBen Gras 	 * If ulr_count is non-zero, then namei found space
788d65f6f70SBen Gras 	 * for the new entry in the range ulr_offset to
789d65f6f70SBen Gras 	 * ulr_offset + ulr_count in the directory.
790d65f6f70SBen Gras 	 * To use this space, we may have to compact the entries located
791d65f6f70SBen Gras 	 * there, by copying them together towards the beginning of the
792d65f6f70SBen Gras 	 * block, leaving the free space in one usable chunk at the end.
793d65f6f70SBen Gras 	 */
794d65f6f70SBen Gras 
795d65f6f70SBen Gras 	/*
796d65f6f70SBen Gras 	 * Get the block containing the space for the new directory entry.
797d65f6f70SBen Gras 	 */
798d65f6f70SBen Gras 	if ((error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp)) != 0)
799d65f6f70SBen Gras 		return (error);
800d65f6f70SBen Gras 	/*
801d65f6f70SBen Gras 	 * Find space for the new entry. In the simple case, the entry at
802d65f6f70SBen Gras 	 * offset base will have the space. If it does not, then namei
803d65f6f70SBen Gras 	 * arranged that compacting the region ulr_offset to
804d65f6f70SBen Gras 	 * ulr_offset + ulr_count would yield the
805d65f6f70SBen Gras 	 * space.
806d65f6f70SBen Gras 	 */
807d65f6f70SBen Gras 	ep = (struct ext2fs_direct *)dirbuf;
808d65f6f70SBen Gras 	dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
809d65f6f70SBen Gras 	spacefree = fs2h16(ep->e2d_reclen) - dsize;
810d65f6f70SBen Gras 	for (loc = fs2h16(ep->e2d_reclen); loc < ulr->ulr_count; ) {
811d65f6f70SBen Gras 		nep = (struct ext2fs_direct *)(dirbuf + loc);
812d65f6f70SBen Gras 		if (ep->e2d_ino) {
813d65f6f70SBen Gras 			/* trim the existing slot */
814d65f6f70SBen Gras 			ep->e2d_reclen = h2fs16(dsize);
815d65f6f70SBen Gras 			ep = (struct ext2fs_direct *)((char *)ep + dsize);
816d65f6f70SBen Gras 		} else {
817d65f6f70SBen Gras 			/* overwrite; nothing there; header is ours */
818d65f6f70SBen Gras 			spacefree += dsize;
819d65f6f70SBen Gras 		}
820d65f6f70SBen Gras 		dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
821d65f6f70SBen Gras 		spacefree += fs2h16(nep->e2d_reclen) - dsize;
822d65f6f70SBen Gras 		loc += fs2h16(nep->e2d_reclen);
823d65f6f70SBen Gras 		memcpy((void *)ep, (void *)nep, dsize);
824d65f6f70SBen Gras 	}
825d65f6f70SBen Gras 	/*
826d65f6f70SBen Gras 	 * Update the pointer fields in the previous entry (if any),
827d65f6f70SBen Gras 	 * copy in the new entry, and write out the block.
828d65f6f70SBen Gras 	 */
829d65f6f70SBen Gras 	if (ep->e2d_ino == 0) {
830d65f6f70SBen Gras #ifdef DIAGNOSTIC
831d65f6f70SBen Gras 		if (spacefree + dsize < newentrysize)
832d65f6f70SBen Gras 			panic("ext2fs_direnter: compact1");
833d65f6f70SBen Gras #endif
834d65f6f70SBen Gras 		newdir.e2d_reclen = h2fs16(spacefree + dsize);
835d65f6f70SBen Gras 	} else {
836d65f6f70SBen Gras #ifdef DIAGNOSTIC
837d65f6f70SBen Gras 		if (spacefree < newentrysize) {
838d65f6f70SBen Gras 			printf("ext2fs_direnter: compact2 %u %u",
839d65f6f70SBen Gras 			    (u_int)spacefree, (u_int)newentrysize);
840d65f6f70SBen Gras 			panic("ext2fs_direnter: compact2");
841d65f6f70SBen Gras 		}
842d65f6f70SBen Gras #endif
843d65f6f70SBen Gras 		newdir.e2d_reclen = h2fs16(spacefree);
844d65f6f70SBen Gras 		ep->e2d_reclen = h2fs16(dsize);
845d65f6f70SBen Gras 		ep = (struct ext2fs_direct *)((char *)ep + dsize);
846d65f6f70SBen Gras 	}
847d65f6f70SBen Gras 	memcpy((void *)ep, (void *)&newdir, (u_int)newentrysize);
848d65f6f70SBen Gras 	error = VOP_BWRITE(bp->b_vp, bp);
849d65f6f70SBen Gras 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
850d65f6f70SBen Gras 	if (!error && ulr->ulr_endoff && ulr->ulr_endoff < ext2fs_size(dp))
851d65f6f70SBen Gras 		error = ext2fs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC,
852d65f6f70SBen Gras 		    cnp->cn_cred);
853d65f6f70SBen Gras 	return (error);
854d65f6f70SBen Gras }
855d65f6f70SBen Gras 
856d65f6f70SBen Gras /*
857d65f6f70SBen Gras  * Remove a directory entry after a call to namei, using
858d65f6f70SBen Gras  * the auxiliary results it provided. The entry
859d65f6f70SBen Gras  * ulr_offset contains the offset into the directory of the
860d65f6f70SBen Gras  * entry to be eliminated.  The ulr_count field contains the
861d65f6f70SBen Gras  * size of the previous record in the directory.  If this
862d65f6f70SBen Gras  * is 0, the first entry is being deleted, so we need only
863d65f6f70SBen Gras  * zero the inode number to mark the entry as free.  If the
864d65f6f70SBen Gras  * entry is not the first in the directory, we must reclaim
865d65f6f70SBen Gras  * the space of the now empty record by adding the record size
866d65f6f70SBen Gras  * to the size of the previous entry.
867d65f6f70SBen Gras  */
868d65f6f70SBen Gras int
ext2fs_dirremove(struct vnode * dvp,const struct ufs_lookup_results * ulr,struct componentname * cnp)869d65f6f70SBen Gras ext2fs_dirremove(struct vnode *dvp, const struct ufs_lookup_results *ulr,
870d65f6f70SBen Gras 		 struct componentname *cnp)
871d65f6f70SBen Gras {
872d65f6f70SBen Gras 	struct inode *dp;
873d65f6f70SBen Gras 	struct ext2fs_direct *ep;
874d65f6f70SBen Gras 	struct buf *bp;
875d65f6f70SBen Gras 	int error;
876d65f6f70SBen Gras 
877d65f6f70SBen Gras 	dp = VTOI(dvp);
878d65f6f70SBen Gras 
879d65f6f70SBen Gras 	if (ulr->ulr_count == 0) {
880d65f6f70SBen Gras 		/*
881d65f6f70SBen Gras 		 * First entry in block: set d_ino to zero.
882d65f6f70SBen Gras 		 */
883d65f6f70SBen Gras 		error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset,
884d65f6f70SBen Gras 		    (void *)&ep, &bp);
885d65f6f70SBen Gras 		if (error != 0)
886d65f6f70SBen Gras 			return (error);
887d65f6f70SBen Gras 		ep->e2d_ino = 0;
888d65f6f70SBen Gras 		error = VOP_BWRITE(bp->b_vp, bp);
889d65f6f70SBen Gras 		dp->i_flag |= IN_CHANGE | IN_UPDATE;
890d65f6f70SBen Gras 		return (error);
891d65f6f70SBen Gras 	}
892d65f6f70SBen Gras 	/*
893d65f6f70SBen Gras 	 * Collapse new free space into previous entry.
894d65f6f70SBen Gras 	 */
895d65f6f70SBen Gras 	error = ext2fs_blkatoff(dvp, (off_t)(ulr->ulr_offset - ulr->ulr_count),
896d65f6f70SBen Gras 	    (void *)&ep, &bp);
897d65f6f70SBen Gras 	if (error != 0)
898d65f6f70SBen Gras 		return (error);
899d65f6f70SBen Gras 	ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + ulr->ulr_reclen);
900d65f6f70SBen Gras 	error = VOP_BWRITE(bp->b_vp, bp);
901d65f6f70SBen Gras 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
902d65f6f70SBen Gras 	return (error);
903d65f6f70SBen Gras }
904d65f6f70SBen Gras 
905d65f6f70SBen Gras /*
906d65f6f70SBen Gras  * Rewrite an existing directory entry to point at the inode
907d65f6f70SBen Gras  * supplied.  The parameters describing the directory entry are
908d65f6f70SBen Gras  * set up by a call to namei.
909d65f6f70SBen Gras  */
910d65f6f70SBen Gras int
ext2fs_dirrewrite(struct inode * dp,const struct ufs_lookup_results * ulr,struct inode * ip,struct componentname * cnp)911d65f6f70SBen Gras ext2fs_dirrewrite(struct inode *dp, const struct ufs_lookup_results *ulr,
912d65f6f70SBen Gras     struct inode *ip, struct componentname *cnp)
913d65f6f70SBen Gras {
914d65f6f70SBen Gras 	struct buf *bp;
915d65f6f70SBen Gras 	struct ext2fs_direct *ep;
916d65f6f70SBen Gras 	struct vnode *vdp = ITOV(dp);
917d65f6f70SBen Gras 	int error;
918d65f6f70SBen Gras 
919d65f6f70SBen Gras 	error = ext2fs_blkatoff(vdp, (off_t)ulr->ulr_offset, (void *)&ep, &bp);
920d65f6f70SBen Gras 	if (error != 0)
921d65f6f70SBen Gras 		return (error);
922d65f6f70SBen Gras 	ep->e2d_ino = h2fs32(ip->i_number);
923d65f6f70SBen Gras 	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
924d65f6f70SBen Gras 	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
925d65f6f70SBen Gras 		ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
926d65f6f70SBen Gras 	} else {
927d65f6f70SBen Gras 		ep->e2d_type = 0;
928d65f6f70SBen Gras 	}
929d65f6f70SBen Gras 	error = VOP_BWRITE(bp->b_vp, bp);
930d65f6f70SBen Gras 	dp->i_flag |= IN_CHANGE | IN_UPDATE;
931d65f6f70SBen Gras 	return (error);
932d65f6f70SBen Gras }
933d65f6f70SBen Gras 
934d65f6f70SBen Gras /*
935d65f6f70SBen Gras  * Check if a directory is empty or not.
936d65f6f70SBen Gras  * Inode supplied must be locked.
937d65f6f70SBen Gras  *
938d65f6f70SBen Gras  * Using a struct dirtemplate here is not precisely
939d65f6f70SBen Gras  * what we want, but better than using a struct ext2fs_direct.
940d65f6f70SBen Gras  *
941d65f6f70SBen Gras  * NB: does not handle corrupted directories.
942d65f6f70SBen Gras  */
943d65f6f70SBen Gras int
ext2fs_dirempty(struct inode * ip,ino_t parentino,kauth_cred_t cred)944d65f6f70SBen Gras ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
945d65f6f70SBen Gras {
946d65f6f70SBen Gras 	off_t off;
947d65f6f70SBen Gras 	struct ext2fs_dirtemplate dbuf;
948d65f6f70SBen Gras 	struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
949d65f6f70SBen Gras 	int error, namlen;
950d65f6f70SBen Gras 	size_t count;
951d65f6f70SBen Gras 
952d65f6f70SBen Gras #define	MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
953d65f6f70SBen Gras 
954d65f6f70SBen Gras 	for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) {
955*0a6a1f1dSLionel Sambuc 		error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ,
956*0a6a1f1dSLionel Sambuc 		    off, IO_NODELOCKED, cred, &count, NULL);
957d65f6f70SBen Gras 		/*
958d65f6f70SBen Gras 		 * Since we read MINDIRSIZ, residual must
959d65f6f70SBen Gras 		 * be 0 unless we're at end of file.
960d65f6f70SBen Gras 		 */
961d65f6f70SBen Gras 		if (error || count != 0)
962d65f6f70SBen Gras 			return (0);
963d65f6f70SBen Gras 		/* avoid infinite loops */
964d65f6f70SBen Gras 		if (dp->e2d_reclen == 0)
965d65f6f70SBen Gras 			return (0);
966d65f6f70SBen Gras 		/* skip empty entries */
967d65f6f70SBen Gras 		if (dp->e2d_ino == 0)
968d65f6f70SBen Gras 			continue;
969d65f6f70SBen Gras 		/* accept only "." and ".." */
970d65f6f70SBen Gras 		namlen = dp->e2d_namlen;
971d65f6f70SBen Gras 		if (namlen > 2)
972d65f6f70SBen Gras 			return (0);
973d65f6f70SBen Gras 		if (dp->e2d_name[0] != '.')
974d65f6f70SBen Gras 			return (0);
975d65f6f70SBen Gras 		/*
976d65f6f70SBen Gras 		 * At this point namlen must be 1 or 2.
977d65f6f70SBen Gras 		 * 1 implies ".", 2 implies ".." if second
978d65f6f70SBen Gras 		 * char is also "."
979d65f6f70SBen Gras 		 */
980d65f6f70SBen Gras 		if (namlen == 1)
981d65f6f70SBen Gras 			continue;
982d65f6f70SBen Gras 		if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino)
983d65f6f70SBen Gras 			continue;
984d65f6f70SBen Gras 		return (0);
985d65f6f70SBen Gras 	}
986d65f6f70SBen Gras 	return (1);
987d65f6f70SBen Gras }
988