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