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