1 /* $OpenBSD: cd9660_lookup.c,v 1.9 2001/06/23 02:14:22 csapuntz Exp $ */ 2 /* $NetBSD: cd9660_lookup.c,v 1.18 1997/05/08 16:19:59 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1989, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley 9 * by Pace Willisson (pace@blitz.com). The Rock Ridge Extension 10 * Support code is derived from software contributed to Berkeley 11 * by Atsushi Murai (amurai@spec.co.jp). 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * from: @(#)ufs_lookup.c 7.33 (Berkeley) 5/19/91 42 * 43 * @(#)cd9660_lookup.c 8.5 (Berkeley) 12/5/94 44 */ 45 46 #include <sys/param.h> 47 #include <sys/namei.h> 48 #include <sys/buf.h> 49 #include <sys/file.h> 50 #include <sys/vnode.h> 51 #include <sys/mount.h> 52 #include <sys/systm.h> 53 54 #include <isofs/cd9660/iso.h> 55 #include <isofs/cd9660/cd9660_extern.h> 56 #include <isofs/cd9660/cd9660_node.h> 57 #include <isofs/cd9660/iso_rrip.h> 58 #include <isofs/cd9660/cd9660_rrip.h> 59 60 struct nchstats iso_nchstats; 61 62 /* 63 * Convert a component of a pathname into a pointer to a locked inode. 64 * This is a very central and rather complicated routine. 65 * If the file system is not maintained in a strict tree hierarchy, 66 * this can result in a deadlock situation (see comments in code below). 67 * 68 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on 69 * whether the name is to be looked up, created, renamed, or deleted. 70 * When CREATE, RENAME, or DELETE is specified, information usable in 71 * creating, renaming, or deleting a directory entry may be calculated. 72 * If flag has LOCKPARENT or'ed into it and the target of the pathname 73 * exists, lookup returns both the target and its parent directory locked. 74 * When creating or renaming and LOCKPARENT is specified, the target may 75 * not be ".". When deleting and LOCKPARENT is specified, the target may 76 * be "."., but the caller must check to ensure it does an vrele and iput 77 * instead of two iputs. 78 * 79 * Overall outline of cd9660_lookup: 80 * 81 * check accessibility of directory 82 * look for name in cache, if found, then if at end of path 83 * and deleting or creating, drop it, else return name 84 * search for name in directory, to found or notfound 85 * notfound: 86 * if creating, return locked directory, leaving info on available slots 87 * else return error 88 * found: 89 * if at end of path and deleting, return information to allow delete 90 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 91 * inode and return info to allow rewrite 92 * if not at end, add name to cache; if at end and neither creating 93 * nor deleting, add name to cache 94 * 95 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent inode unlocked. 96 */ 97 int 98 cd9660_lookup(v) 99 void *v; 100 { 101 struct vop_lookup_args /* { 102 struct vnode *a_dvp; 103 struct vnode **a_vpp; 104 struct componentname *a_cnp; 105 } */ *ap = v; 106 register struct vnode *vdp; /* vnode for directory being searched */ 107 register struct iso_node *dp; /* inode for directory being searched */ 108 register struct iso_mnt *imp; /* file system that directory is in */ 109 struct buf *bp; /* a buffer of directory entries */ 110 struct iso_directory_record *ep = NULL; 111 /* the current directory entry */ 112 int entryoffsetinblock; /* offset of ep in bp's buffer */ 113 int saveoffset = -1; /* offset of last directory entry in dir */ 114 int numdirpasses; /* strategy for directory search */ 115 doff_t endsearch; /* offset to end directory search */ 116 struct vnode *pdp; /* saved dp during symlink work */ 117 struct vnode *tdp; /* returned by cd9660_vget_internal */ 118 u_long bmask; /* block offset mask */ 119 int lockparent; /* 1 => lockparent flag is set */ 120 int wantparent; /* 1 => wantparent or lockparent flag */ 121 int error; 122 ino_t ino = 0; 123 int reclen; 124 u_short namelen; 125 char altname[NAME_MAX]; 126 int res; 127 int assoc, len; 128 char *name; 129 struct vnode **vpp = ap->a_vpp; 130 struct componentname *cnp = ap->a_cnp; 131 struct ucred *cred = cnp->cn_cred; 132 int flags = cnp->cn_flags; 133 int nameiop = cnp->cn_nameiop; 134 struct proc *p = cnp->cn_proc; 135 136 bp = NULL; 137 *vpp = NULL; 138 vdp = ap->a_dvp; 139 dp = VTOI(vdp); 140 imp = dp->i_mnt; 141 lockparent = flags & LOCKPARENT; 142 wantparent = flags & (LOCKPARENT|WANTPARENT); 143 144 /* 145 * Check accessiblity of directory. 146 */ 147 if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) 148 return (error); 149 150 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 151 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 152 return (EROFS); 153 154 /* 155 * We now have a segment name to search for, and a directory to search. 156 * 157 * Before tediously performing a linear scan of the directory, 158 * check the name cache to see if the directory/name pair 159 * we are looking for is known already. 160 */ 161 if ((error = cache_lookup(vdp, vpp, cnp)) != 0) { 162 int vpid; /* capability number of vnode */ 163 164 if (error == ENOENT) 165 return (error); 166 #ifdef PARANOID 167 if ((vdp->v_flag & VROOT) && (flags & ISDOTDOT)) 168 panic("cd9660_lookup: .. through root"); 169 #endif 170 /* 171 * Get the next vnode in the path. 172 * See comment below starting `Step through' for 173 * an explaination of the locking protocol. 174 */ 175 pdp = vdp; 176 dp = VTOI(*vpp); 177 vdp = *vpp; 178 vpid = vdp->v_id; 179 if (pdp == vdp) { 180 VREF(vdp); 181 error = 0; 182 } else if (flags & ISDOTDOT) { 183 VOP_UNLOCK(pdp, 0, p); 184 error = vget(vdp, LK_EXCLUSIVE, p); 185 if (!error && lockparent && (flags & ISLASTCN)) 186 error = vn_lock(pdp, LK_EXCLUSIVE, p); 187 } else { 188 error = vget(vdp, LK_EXCLUSIVE, p); 189 if (!lockparent || error || !(flags & ISLASTCN)) 190 VOP_UNLOCK(pdp, 0, p); 191 } 192 /* 193 * Check that the capability number did not change 194 * while we were waiting for the lock. 195 */ 196 if (!error) { 197 if (vpid == vdp->v_id) 198 return (0); 199 vput(vdp); 200 if (lockparent && pdp != vdp && (flags & ISLASTCN)) 201 VOP_UNLOCK(pdp, 0, p); 202 } 203 if ((error = vn_lock(pdp, LK_EXCLUSIVE, p)) != 0) 204 return (error); 205 vdp = pdp; 206 dp = VTOI(pdp); 207 *vpp = NULL; 208 } 209 210 len = cnp->cn_namelen; 211 name = cnp->cn_nameptr; 212 /* 213 * A leading `=' means, we are looking for an associated file 214 */ 215 assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR); 216 if (assoc) { 217 len--; 218 name++; 219 } 220 221 /* 222 * If there is cached information on a previous search of 223 * this directory, pick up where we last left off. 224 * We cache only lookups as these are the most common 225 * and have the greatest payoff. Caching CREATE has little 226 * benefit as it usually must search the entire directory 227 * to determine that the entry does not exist. Caching the 228 * location of the last DELETE or RENAME has not reduced 229 * profiling time and hence has been removed in the interest 230 * of simplicity. 231 */ 232 bmask = imp->im_bmask; 233 if (nameiop != LOOKUP || dp->i_diroff == 0 || 234 dp->i_diroff > dp->i_size) { 235 entryoffsetinblock = 0; 236 dp->i_offset = 0; 237 numdirpasses = 1; 238 } else { 239 dp->i_offset = dp->i_diroff; 240 if ((entryoffsetinblock = dp->i_offset & bmask) && 241 (error = cd9660_bufatoff(dp, (off_t)dp->i_offset, NULL, 242 &bp))) 243 return (error); 244 numdirpasses = 2; 245 iso_nchstats.ncs_2passes++; 246 } 247 endsearch = dp->i_size; 248 249 searchloop: 250 while (dp->i_offset < endsearch) { 251 /* 252 * If offset is on a block boundary, 253 * read the next directory block. 254 * Release previous if it exists. 255 */ 256 if ((dp->i_offset & bmask) == 0) { 257 if (bp != NULL) 258 brelse(bp); 259 error = cd9660_bufatoff(dp, (off_t)dp->i_offset, 260 NULL, &bp); 261 if (error) 262 return (error); 263 entryoffsetinblock = 0; 264 } 265 /* 266 * Get pointer to next entry. 267 */ 268 ep = (struct iso_directory_record *) 269 ((char *)bp->b_data + entryoffsetinblock); 270 271 reclen = isonum_711(ep->length); 272 if (reclen == 0) { 273 /* skip to next block, if any */ 274 dp->i_offset = 275 (dp->i_offset & ~bmask) + imp->logical_block_size; 276 continue; 277 } 278 279 if (reclen < ISO_DIRECTORY_RECORD_SIZE) 280 /* illegal entry, stop */ 281 break; 282 283 if (entryoffsetinblock + reclen > imp->logical_block_size) 284 /* entries are not allowed to cross boundaries */ 285 break; 286 287 namelen = isonum_711(ep->name_len); 288 289 if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen) 290 /* illegal entry, stop */ 291 break; 292 293 /* 294 * Check for a name match. 295 */ 296 switch (imp->iso_ftype) { 297 default: 298 if ((!(isonum_711(ep->flags)&4)) == !assoc) { 299 if ((len == 1 300 && *name == '.') 301 || (flags & ISDOTDOT)) { 302 if (namelen == 1 303 && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) { 304 /* 305 * Save directory entry's inode number and 306 * release directory buffer. 307 */ 308 dp->i_ino = isodirino(ep, imp); 309 goto found; 310 } 311 if (namelen != 1 312 || ep->name[0] != 0) 313 goto notfound; 314 } else if (!(res = isofncmp(name, len, 315 ep->name, namelen, imp->joliet_level))) { 316 if (isonum_711(ep->flags)&2) 317 ino = isodirino(ep, imp); 318 else 319 ino = dbtob(bp->b_blkno) 320 + entryoffsetinblock; 321 saveoffset = dp->i_offset; 322 } else if (ino) 323 goto foundino; 324 #ifdef NOSORTBUG /* On some CDs directory entries are not sorted correctly */ 325 else if (res < 0) 326 goto notfound; 327 else if (res > 0 && numdirpasses == 2) 328 numdirpasses++; 329 #endif 330 } 331 break; 332 case ISO_FTYPE_RRIP: 333 if (isonum_711(ep->flags)&2) 334 ino = isodirino(ep, imp); 335 else 336 ino = dbtob(bp->b_blkno) + entryoffsetinblock; 337 dp->i_ino = ino; 338 cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp); 339 if (namelen == cnp->cn_namelen 340 && !bcmp(name,altname,namelen)) 341 goto found; 342 ino = 0; 343 break; 344 } 345 dp->i_offset += reclen; 346 entryoffsetinblock += reclen; 347 } 348 if (ino) { 349 foundino: 350 dp->i_ino = ino; 351 if (saveoffset != dp->i_offset) { 352 if (lblkno(imp, dp->i_offset) != 353 lblkno(imp, saveoffset)) { 354 if (bp != NULL) 355 brelse(bp); 356 if ((error = cd9660_bufatoff(dp, 357 (off_t)saveoffset, NULL, &bp)) != 0) 358 return (error); 359 } 360 entryoffsetinblock = saveoffset & bmask; 361 ep = (struct iso_directory_record *) 362 ((char *)bp->b_data + entryoffsetinblock); 363 dp->i_offset = saveoffset; 364 } 365 goto found; 366 } 367 notfound: 368 /* 369 * If we started in the middle of the directory and failed 370 * to find our target, we must check the beginning as well. 371 */ 372 if (numdirpasses == 2) { 373 numdirpasses--; 374 dp->i_offset = 0; 375 endsearch = dp->i_diroff; 376 goto searchloop; 377 } 378 if (bp != NULL) 379 brelse(bp); 380 381 /* 382 * Insert name into cache (as non-existent) if appropriate. 383 */ 384 if (cnp->cn_flags & MAKEENTRY) 385 cache_enter(vdp, *vpp, cnp); 386 if (nameiop == CREATE || nameiop == RENAME) 387 return (EJUSTRETURN); 388 return (ENOENT); 389 390 found: 391 if (numdirpasses == 2) 392 iso_nchstats.ncs_pass2++; 393 394 /* 395 * Found component in pathname. 396 * If the final component of path name, save information 397 * in the cache as to where the entry was found. 398 */ 399 if ((flags & ISLASTCN) && nameiop == LOOKUP) 400 dp->i_diroff = dp->i_offset; 401 402 /* 403 * Step through the translation in the name. We do not `iput' the 404 * directory because we may need it again if a symbolic link 405 * is relative to the current directory. Instead we save it 406 * unlocked as "pdp". We must get the target inode before unlocking 407 * the directory to insure that the inode will not be removed 408 * before we get it. We prevent deadlock by always fetching 409 * inodes from the root, moving down the directory tree. Thus 410 * when following backward pointers ".." we must unlock the 411 * parent directory before getting the requested directory. 412 * There is a potential race condition here if both the current 413 * and parent directories are removed before the `iget' for the 414 * inode associated with ".." returns. We hope that this occurs 415 * infrequently since we cannot avoid this race condition without 416 * implementing a sophisticated deadlock detection algorithm. 417 * Note also that this simple deadlock detection scheme will not 418 * work if the file system has any hard links other than ".." 419 * that point backwards in the directory structure. 420 */ 421 pdp = vdp; 422 /* 423 * If ino is different from dp->i_ino, 424 * it's a relocated directory. 425 */ 426 if (flags & ISDOTDOT) { 427 brelse(bp); 428 VOP_UNLOCK(pdp, 0, p); /* race to get the inode */ 429 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp, 430 dp->i_ino != ino, NULL); 431 if (error) { 432 vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p); 433 return (error); 434 } 435 if (lockparent && (flags & ISLASTCN) && 436 (error = vn_lock(pdp, LK_EXCLUSIVE, p))) { 437 vput(tdp); 438 return (error); 439 } 440 *vpp = tdp; 441 } else if (dp->i_number == dp->i_ino) { 442 brelse(bp); 443 VREF(vdp); /* we want ourself, ie "." */ 444 *vpp = vdp; 445 } else { 446 error = cd9660_vget_internal(vdp->v_mount, dp->i_ino, &tdp, 447 dp->i_ino != ino, ep); 448 brelse(bp); 449 if (error) 450 return (error); 451 if (!lockparent || !(flags & ISLASTCN)) 452 VOP_UNLOCK(pdp, 0, p); 453 *vpp = tdp; 454 } 455 456 /* 457 * Insert name into cache if appropriate. 458 */ 459 if (cnp->cn_flags & MAKEENTRY) 460 cache_enter(vdp, *vpp, cnp); 461 return (0); 462 } 463 464 /* 465 * Return buffer with the contents of block "offset" from the beginning of 466 * directory "ip". If "res" is non-zero, fill it in with a pointer to the 467 * remaining space in the directory. 468 */ 469 int 470 cd9660_bufatoff(struct iso_node *ip, off_t offset, char **res, 471 struct buf **bpp) 472 { 473 struct iso_mnt *imp; 474 struct buf *bp; 475 daddr_t lbn; 476 int bsize, error; 477 struct vnode *vp = ITOV(ip); 478 479 imp = ip->i_mnt; 480 lbn = lblkno(imp, offset); 481 bsize = blksize(imp, ip, lbn); 482 483 if ((error = bread(vp, lbn, bsize, NOCRED, &bp)) != 0) { 484 brelse(bp); 485 *bpp = NULL; 486 return (error); 487 } 488 if (res) 489 *res = (char *)bp->b_data + blkoff(imp, offset); 490 *bpp = bp; 491 return (0); 492 } 493