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