1 /* $NetBSD: cd9660_lookup.c,v 1.30 2015/03/28 19:24:05 maxv Exp $ */ 2 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 * 38 * @(#)cd9660_lookup.c 8.5 (Berkeley) 5/27/95 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: cd9660_lookup.c,v 1.30 2015/03/28 19:24:05 maxv Exp $"); 43 44 #include <sys/param.h> 45 #include <sys/namei.h> 46 #include <sys/buf.h> 47 #include <sys/file.h> 48 #include <sys/vnode.h> 49 #include <sys/mount.h> 50 #include <sys/systm.h> 51 #include <sys/kauth.h> 52 53 #include <fs/cd9660/iso.h> 54 #include <fs/cd9660/cd9660_extern.h> 55 #include <fs/cd9660/cd9660_node.h> 56 #include <fs/cd9660/iso_rrip.h> 57 #include <fs/cd9660/cd9660_rrip.h> 58 #include <fs/cd9660/cd9660_mount.h> 59 60 /* 61 * Convert a component of a pathname into a pointer to a locked inode. 62 * This is a very central and rather complicated routine. 63 * If the file system is not maintained in a strict tree hierarchy, 64 * this can result in a deadlock situation (see comments in code below). 65 * 66 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on 67 * whether the name is to be looked up, created, renamed, or deleted. 68 * When CREATE, RENAME, or DELETE is specified, information usable in 69 * creating, renaming, or deleting a directory entry may be calculated. 70 * If the target of the pathname exists, lookup returns both the target 71 * and its parent directory locked. 72 * When creating or renaming, the target may * not be ".". 73 * When deleting , the target may be "."., but the caller must check 74 * to ensure it does an vrele and vput instead of two vputs. 75 * 76 * Overall outline of ufs_lookup: 77 * 78 * check accessibility of directory 79 * look for name in cache, if found, then if at end of path 80 * and deleting or creating, drop it, else return name 81 * search for name in directory, to found or notfound 82 * notfound: 83 * if creating, return locked directory, leaving info on available slots 84 * else return error 85 * found: 86 * if at end of path and deleting, return information to allow delete 87 * if at end of path and rewriting (RENAME), lock target 88 * inode and return info to allow rewrite 89 * if not at end, add name to cache; if at end and neither creating 90 * nor deleting, add name to cache 91 */ 92 int 93 cd9660_lookup(void *v) 94 { 95 struct vop_lookup_v2_args /* { 96 struct vnode *a_dvp; 97 struct vnode **a_vpp; 98 struct componentname *a_cnp; 99 } */ *ap = v; 100 struct vnode *vdp; /* vnode for directory being searched */ 101 struct iso_node *dp; /* inode for directory being searched */ 102 struct iso_mnt *imp; /* file system that directory is in */ 103 struct buf *bp; /* a buffer of directory entries */ 104 struct iso_directory_record *ep = NULL; 105 /* the current directory entry */ 106 int entryoffsetinblock; /* offset of ep in bp's buffer */ 107 int saveoffset = -1; /* offset of last directory entry in dir */ 108 int numdirpasses; /* strategy for directory search */ 109 doff_t endsearch; /* offset to end directory search */ 110 struct vnode *tdp; /* returned by vcache_get */ 111 u_long bmask; /* block offset mask */ 112 int error; 113 ino_t ino = 0; 114 int reclen; 115 u_short namelen; 116 char altname[ISO_MAXNAMLEN]; 117 int res; 118 int assoc, len; 119 const char *name; 120 struct vnode **vpp = ap->a_vpp; 121 struct componentname *cnp = ap->a_cnp; 122 kauth_cred_t cred = cnp->cn_cred; 123 int flags; 124 int nameiop = cnp->cn_nameiop; 125 126 flags = cnp->cn_flags; 127 128 bp = NULL; 129 *vpp = NULL; 130 vdp = ap->a_dvp; 131 dp = VTOI(vdp); 132 imp = dp->i_mnt; 133 134 /* 135 * Check accessiblity of directory. 136 */ 137 if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) 138 return (error); 139 140 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 141 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 142 return (EROFS); 143 144 /* 145 * We now have a segment name to search for, and a directory to search. 146 * 147 * Before tediously performing a linear scan of the directory, 148 * check the name cache to see if the directory/name pair 149 * we are looking for is known already. 150 */ 151 if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen, 152 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) { 153 return *vpp == NULLVP ? ENOENT : 0; 154 } 155 156 len = cnp->cn_namelen; 157 name = cnp->cn_nameptr; 158 /* 159 * A leading `=' means, we are looking for an associated file 160 */ 161 assoc = (imp->iso_ftype != ISO_FTYPE_RRIP && *name == ASSOCCHAR); 162 if (assoc) { 163 len--; 164 name++; 165 } 166 167 /* 168 * If there is cached information on a previous search of 169 * this directory, pick up where we last left off. 170 * We cache only lookups as these are the most common 171 * and have the greatest payoff. Caching CREATE has little 172 * benefit as it usually must search the entire directory 173 * to determine that the entry does not exist. Caching the 174 * location of the last DELETE or RENAME has not reduced 175 * profiling time and hence has been removed in the interest 176 * of simplicity. 177 */ 178 bmask = imp->im_bmask; 179 if (nameiop != LOOKUP || dp->i_diroff == 0 || 180 dp->i_diroff > dp->i_size) { 181 entryoffsetinblock = 0; 182 dp->i_offset = 0; 183 numdirpasses = 1; 184 } else { 185 dp->i_offset = dp->i_diroff; 186 if ((entryoffsetinblock = dp->i_offset & bmask) && 187 (error = cd9660_blkatoff(vdp, (off_t)dp->i_offset, NULL, 188 &bp))) 189 return (error); 190 numdirpasses = 2; 191 namecache_count_2passes(); 192 } 193 endsearch = dp->i_size; 194 195 searchloop: 196 while (dp->i_offset < endsearch) { 197 /* 198 * If offset is on a block boundary, 199 * read the next directory block. 200 * Release previous if it exists. 201 */ 202 if ((dp->i_offset & bmask) == 0) { 203 if (bp != NULL) 204 brelse(bp, 0); 205 error = cd9660_blkatoff(vdp, (off_t)dp->i_offset, 206 NULL, &bp); 207 if (error) 208 return (error); 209 entryoffsetinblock = 0; 210 } 211 /* 212 * Get pointer to next entry. 213 */ 214 KASSERT(bp != NULL); 215 ep = (struct iso_directory_record *) 216 ((char *)bp->b_data + entryoffsetinblock); 217 218 reclen = isonum_711(ep->length); 219 if (reclen == 0) { 220 /* skip to next block, if any */ 221 dp->i_offset = 222 (dp->i_offset & ~bmask) + imp->logical_block_size; 223 continue; 224 } 225 226 if (reclen < ISO_DIRECTORY_RECORD_SIZE) 227 /* illegal entry, stop */ 228 break; 229 230 if (entryoffsetinblock + reclen > imp->logical_block_size) 231 /* entries are not allowed to cross boundaries */ 232 break; 233 234 namelen = isonum_711(ep->name_len); 235 236 if (reclen < ISO_DIRECTORY_RECORD_SIZE + namelen) 237 /* illegal entry, stop */ 238 break; 239 240 /* 241 * Check for a name match. 242 */ 243 switch (imp->iso_ftype) { 244 default: 245 if ((!(isonum_711(ep->flags)&4)) == !assoc) { 246 if ((len == 1 247 && *name == '.') 248 || (flags & ISDOTDOT)) { 249 if (namelen == 1 250 && ep->name[0] == ((flags & ISDOTDOT) ? 1 : 0)) { 251 /* 252 * Save directory entry's inode number and 253 * release directory buffer. 254 */ 255 dp->i_ino = isodirino(ep, imp); 256 goto found; 257 } 258 if (namelen != 1 259 || ep->name[0] != 0) 260 goto notfound; 261 } else if (!(res = isofncmp(name,len, 262 ep->name,namelen, 263 imp->im_joliet_level))) { 264 if (isonum_711(ep->flags)&2) 265 ino = isodirino(ep, imp); 266 else 267 ino = dbtob(bp->b_blkno) 268 + entryoffsetinblock; 269 saveoffset = dp->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 dp->i_ino = ino; 286 cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp); 287 if (namelen == cnp->cn_namelen) { 288 if (imp->im_flags & ISOFSMNT_RRCASEINS) { 289 if (strncasecmp(name, altname, namelen) == 0) 290 goto found; 291 } else { 292 if (memcmp(name, altname, namelen) == 0) 293 goto found; 294 } 295 } 296 ino = 0; 297 break; 298 } 299 dp->i_offset += reclen; 300 entryoffsetinblock += reclen; 301 } 302 if (ino) { 303 foundino: 304 dp->i_ino = ino; 305 if (saveoffset != dp->i_offset) { 306 if (cd9660_lblkno(imp, dp->i_offset) != 307 cd9660_lblkno(imp, saveoffset)) { 308 if (bp != NULL) 309 brelse(bp, 0); 310 if ((error = cd9660_blkatoff(vdp, 311 (off_t)saveoffset, NULL, &bp)) != 0) 312 return (error); 313 } 314 entryoffsetinblock = saveoffset & bmask; 315 ep = (struct iso_directory_record *) 316 ((char *)bp->b_data + entryoffsetinblock); 317 dp->i_offset = saveoffset; 318 } 319 goto found; 320 } 321 notfound: 322 /* 323 * If we started in the middle of the directory and failed 324 * to find our target, we must check the beginning as well. 325 */ 326 if (numdirpasses == 2) { 327 numdirpasses--; 328 dp->i_offset = 0; 329 endsearch = dp->i_diroff; 330 goto searchloop; 331 } 332 if (bp != NULL) 333 brelse(bp, 0); 334 335 /* 336 * Insert name into cache (as non-existent) if appropriate. 337 */ 338 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); 339 return (nameiop == CREATE || nameiop == RENAME) ? EROFS : ENOENT; 340 341 found: 342 if (numdirpasses == 2) 343 namecache_count_pass2(); 344 brelse(bp, 0); 345 346 /* 347 * Found component in pathname. 348 * If the final component of path name, save information 349 * in the cache as to where the entry was found. 350 */ 351 if ((flags & ISLASTCN) && nameiop == LOOKUP) 352 dp->i_diroff = dp->i_offset; 353 354 if (dp->i_number == dp->i_ino) { 355 vref(vdp); /* we want ourself, ie "." */ 356 *vpp = vdp; 357 } else { 358 error = vcache_get(vdp->v_mount, 359 &dp->i_ino, sizeof(dp->i_ino), &tdp); 360 if (error) 361 return (error); 362 *vpp = tdp; 363 } 364 365 /* 366 * Insert name into cache if appropriate. 367 */ 368 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); 369 370 return 0; 371 } 372 373 /* 374 * Return buffer with the contents of block "offset" from the beginning of 375 * directory "ip". If "res" is non-zero, fill it in with a pointer to the 376 * remaining space in the directory. 377 */ 378 int 379 cd9660_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp) 380 { 381 struct iso_node *ip; 382 struct iso_mnt *imp; 383 struct vnode *devvp; 384 struct buf *bp; 385 daddr_t lbn; 386 int bsize, error; 387 388 ip = VTOI(vp); 389 imp = ip->i_mnt; 390 lbn = cd9660_lblkno(imp, offset); 391 bsize = cd9660_blksize(imp, ip, lbn); 392 393 if ((error = VOP_BMAP(vp, lbn, &devvp, &lbn, NULL)) != 0) { 394 *bpp = NULL; 395 return error; 396 } 397 if ((error = bread(devvp, lbn, bsize, 0, &bp)) != 0) { 398 *bpp = NULL; 399 return (error); 400 } 401 if (res) 402 *res = (char *)bp->b_data + cd9660_blkoff(imp, offset); 403 *bpp = bp; 404 return (0); 405 } 406