1 /* $OpenBSD: cd9660_node.c,v 1.11 2001/06/23 02:14:22 csapuntz Exp $ */ 2 /* $NetBSD: cd9660_node.c,v 1.17 1997/05/05 07:13:57 mycroft Exp $ */ 3 4 /*- 5 * Copyright (c) 1982, 1986, 1989, 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 * @(#)cd9660_node.c 8.5 (Berkeley) 12/5/94 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/mount.h> 47 #include <sys/proc.h> 48 #include <sys/file.h> 49 #include <sys/buf.h> 50 #include <sys/vnode.h> 51 #include <sys/namei.h> 52 #include <sys/kernel.h> 53 #include <sys/malloc.h> 54 #include <sys/stat.h> 55 56 #include <isofs/cd9660/iso.h> 57 #include <isofs/cd9660/cd9660_extern.h> 58 #include <isofs/cd9660/cd9660_node.h> 59 #include <isofs/cd9660/iso_rrip.h> 60 61 /* 62 * Structures associated with iso_node caching. 63 */ 64 struct iso_node **isohashtbl; 65 u_long isohash; 66 #define INOHASH(device, inum) (((device) + ((inum)>>12)) & isohash) 67 struct simplelock cd9660_ihash_slock; 68 69 #ifdef ISODEVMAP 70 struct iso_node **idvhashtbl; 71 u_long idvhash; 72 #define DNOHASH(device, inum) (((device) + ((inum)>>12)) & idvhash) 73 #endif 74 75 int prtactive; /* 1 => print out reclaim of active vnodes */ 76 77 static u_int cd9660_chars2ui __P((u_char *, int)); 78 79 /* 80 * Initialize hash links for inodes and dnodes. 81 */ 82 int 83 cd9660_init(vfsp) 84 struct vfsconf *vfsp; 85 { 86 87 isohashtbl = hashinit(desiredvnodes, M_ISOFSMNT, M_WAITOK, &isohash); 88 simple_lock_init(&cd9660_ihash_slock); 89 #ifdef ISODEVMAP 90 idvhashtbl = hashinit(desiredvnodes / 8, M_ISOFSMNT, M_WAITOK, &idvhash); 91 #endif 92 return (0); 93 } 94 95 #ifdef ISODEVMAP 96 /* 97 * Enter a new node into the device hash list 98 */ 99 struct iso_dnode * 100 iso_dmap(device, inum, create) 101 dev_t device; 102 ino_t inum; 103 int create; 104 { 105 register struct iso_dnode **dpp, *dp, *dq; 106 107 dpp = &idvhashtbl[DNOHASH(device, inum)]; 108 for (dp = *dpp;; dp = dp->d_next) { 109 if (dp == NULL) 110 return (NULL); 111 if (inum == dp->i_number && device == dp->i_dev) 112 return (dp); 113 } 114 115 if (!create) 116 return (NULL); 117 118 MALLOC(dp, struct iso_dnode *, sizeof(struct iso_dnode), M_CACHE, 119 M_WAITOK); 120 dp->i_dev = dev; 121 dp->i_number = ino; 122 123 if (dq = *dpp) 124 dq->d_prev = dp->d_next; 125 dp->d_next = dq; 126 dp->d_prev = dpp; 127 *dpp = dp; 128 129 return (dp); 130 } 131 132 void 133 iso_dunmap(device) 134 dev_t device; 135 { 136 struct iso_dnode **dpp, *dp, *dq; 137 138 for (dpp = idvhashtbl; dpp <= idvhashtbl + idvhash; dpp++) { 139 for (dp = *dpp; dp != NULL; dp = dq) { 140 dq = dp->d_next; 141 if (device == dp->i_dev) { 142 if (dq) 143 dq->d_prev = dp->d_prev; 144 *dp->d_prev = dq; 145 FREE(dp, M_CACHE); 146 } 147 } 148 } 149 } 150 #endif 151 152 /* 153 * Use the device/inum pair to find the incore inode, and return a pointer 154 * to it. If it is in core, but locked, wait for it. 155 */ 156 struct vnode * 157 cd9660_ihashget(dev, inum) 158 dev_t dev; 159 ino_t inum; 160 { 161 struct proc *p = curproc; /* XXX */ 162 struct iso_node *ip; 163 struct vnode *vp; 164 165 loop: 166 simple_lock(&cd9660_ihash_slock); 167 for (ip = isohashtbl[INOHASH(dev, inum)]; ip; ip = ip->i_next) { 168 if (inum == ip->i_number && dev == ip->i_dev) { 169 vp = ITOV(ip); 170 simple_lock(&vp->v_interlock); 171 simple_unlock(&cd9660_ihash_slock); 172 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) 173 goto loop; 174 return (vp); 175 } 176 } 177 simple_unlock(&cd9660_ihash_slock); 178 return (NULL); 179 } 180 181 /* 182 * Insert the inode into the hash table, and return it locked. 183 */ 184 int 185 cd9660_ihashins(ip) 186 struct iso_node *ip; 187 { 188 struct proc *p = curproc; 189 struct iso_node **ipp, *iq; 190 191 simple_lock(&cd9660_ihash_slock); 192 ipp = &isohashtbl[INOHASH(ip->i_dev, ip->i_number)]; 193 194 for (iq = *ipp; iq; iq = iq->i_next) { 195 if (iq->i_dev == ip->i_dev && 196 iq->i_number == ip->i_number) 197 return (EEXIST); 198 } 199 200 if ((iq = *ipp) != NULL) 201 iq->i_prev = &ip->i_next; 202 ip->i_next = iq; 203 ip->i_prev = ipp; 204 *ipp = ip; 205 simple_unlock(&cd9660_ihash_slock); 206 207 lockmgr(&ip->i_lock, LK_EXCLUSIVE, 0, p); 208 return (0); 209 } 210 211 /* 212 * Remove the inode from the hash table. 213 */ 214 void 215 cd9660_ihashrem(ip) 216 register struct iso_node *ip; 217 { 218 register struct iso_node *iq; 219 220 if (ip->i_prev == NULL) 221 return; 222 223 simple_lock(&cd9660_ihash_slock); 224 if ((iq = ip->i_next) != NULL) 225 iq->i_prev = ip->i_prev; 226 *ip->i_prev = iq; 227 #ifdef DIAGNOSTIC 228 ip->i_next = NULL; 229 ip->i_prev = NULL; 230 #endif 231 simple_unlock(&cd9660_ihash_slock); 232 } 233 234 /* 235 * Last reference to an inode, write the inode out and if necessary, 236 * truncate and deallocate the file. 237 */ 238 int 239 cd9660_inactive(v) 240 void *v; 241 { 242 struct vop_inactive_args /* { 243 struct vnode *a_vp; 244 struct proc *a_p; 245 } */ *ap = v; 246 struct vnode *vp = ap->a_vp; 247 struct proc *p = ap->a_p; 248 register struct iso_node *ip = VTOI(vp); 249 int error = 0; 250 251 if (prtactive && vp->v_usecount != 0) 252 vprint("cd9660_inactive: pushing active", vp); 253 254 ip->i_flag = 0; 255 VOP_UNLOCK(vp, 0, p); 256 /* 257 * If we are done with the inode, reclaim it 258 * so that it can be reused immediately. 259 */ 260 if (ip->inode.iso_mode == 0) 261 vrecycle(vp, (struct simplelock *)0, p); 262 263 return (error); 264 } 265 266 /* 267 * Reclaim an inode so that it can be used for other purposes. 268 */ 269 int 270 cd9660_reclaim(v) 271 void *v; 272 { 273 struct vop_reclaim_args /* { 274 struct vnode *a_vp; 275 } */ *ap = v; 276 register struct vnode *vp = ap->a_vp; 277 register struct iso_node *ip = VTOI(vp); 278 279 if (prtactive && vp->v_usecount != 0) 280 vprint("cd9660_reclaim: pushing active", vp); 281 /* 282 * Remove the inode from its hash chain. 283 */ 284 cd9660_ihashrem(ip); 285 /* 286 * Purge old data structures associated with the inode. 287 */ 288 cache_purge(vp); 289 if (ip->i_devvp) { 290 vrele(ip->i_devvp); 291 ip->i_devvp = 0; 292 } 293 FREE(vp->v_data, M_ISOFSNODE); 294 vp->v_data = NULL; 295 return (0); 296 } 297 298 /* 299 * File attributes 300 */ 301 void 302 cd9660_defattr(isodir, inop, bp) 303 struct iso_directory_record *isodir; 304 struct iso_node *inop; 305 struct buf *bp; 306 { 307 struct buf *bp2 = NULL; 308 struct iso_mnt *imp; 309 struct iso_extended_attributes *ap = NULL; 310 int off; 311 312 if (isonum_711(isodir->flags)&2) { 313 inop->inode.iso_mode = S_IFDIR; 314 /* 315 * If we return 2, fts() will assume there are no subdirectories 316 * (just links for the path and .), so instead we return 1. 317 */ 318 inop->inode.iso_links = 1; 319 } else { 320 inop->inode.iso_mode = S_IFREG; 321 inop->inode.iso_links = 1; 322 } 323 if (!bp 324 && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT) 325 && (off = isonum_711(isodir->ext_attr_length))) { 326 cd9660_bufatoff(inop, (off_t)-(off << imp->im_bshift), NULL, 327 &bp2); 328 bp = bp2; 329 } 330 if (bp) { 331 ap = (struct iso_extended_attributes *)bp->b_data; 332 333 if (isonum_711(ap->version) == 1) { 334 if (!(ap->perm[1]&0x10)) 335 inop->inode.iso_mode |= S_IRUSR; 336 if (!(ap->perm[1]&0x40)) 337 inop->inode.iso_mode |= S_IXUSR; 338 if (!(ap->perm[0]&0x01)) 339 inop->inode.iso_mode |= S_IRGRP; 340 if (!(ap->perm[0]&0x04)) 341 inop->inode.iso_mode |= S_IXGRP; 342 if (!(ap->perm[0]&0x10)) 343 inop->inode.iso_mode |= S_IROTH; 344 if (!(ap->perm[0]&0x40)) 345 inop->inode.iso_mode |= S_IXOTH; 346 inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */ 347 inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */ 348 } else 349 ap = NULL; 350 } 351 if (!ap) { 352 inop->inode.iso_mode |= 353 S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 354 inop->inode.iso_uid = (uid_t)0; 355 inop->inode.iso_gid = (gid_t)0; 356 } 357 if (bp2) 358 brelse(bp2); 359 } 360 361 /* 362 * Time stamps 363 */ 364 void 365 cd9660_deftstamp(isodir,inop,bp) 366 struct iso_directory_record *isodir; 367 struct iso_node *inop; 368 struct buf *bp; 369 { 370 struct buf *bp2 = NULL; 371 struct iso_mnt *imp; 372 struct iso_extended_attributes *ap = NULL; 373 int off; 374 375 if (!bp 376 && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT) 377 && (off = isonum_711(isodir->ext_attr_length))) { 378 cd9660_bufatoff(inop, (off_t)-(off << imp->im_bshift), NULL, 379 &bp2); 380 bp = bp2; 381 } 382 if (bp) { 383 ap = (struct iso_extended_attributes *)bp->b_data; 384 385 if (isonum_711(ap->version) == 1) { 386 if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime)) 387 cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime); 388 if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime)) 389 inop->inode.iso_ctime = inop->inode.iso_atime; 390 if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime)) 391 inop->inode.iso_mtime = inop->inode.iso_ctime; 392 } else 393 ap = NULL; 394 } 395 if (!ap) { 396 cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime); 397 inop->inode.iso_atime = inop->inode.iso_ctime; 398 inop->inode.iso_mtime = inop->inode.iso_ctime; 399 } 400 if (bp2) 401 brelse(bp2); 402 } 403 404 int 405 cd9660_tstamp_conv7(pi,pu) 406 u_char *pi; 407 struct timespec *pu; 408 { 409 int crtime, days; 410 int y, m, d, hour, minute, second; 411 signed char tz; 412 413 y = pi[0] + 1900; 414 m = pi[1]; 415 d = pi[2]; 416 hour = pi[3]; 417 minute = pi[4]; 418 second = pi[5]; 419 tz = (signed char) pi[6]; 420 421 if (y < 1970) { 422 pu->tv_sec = 0; 423 pu->tv_nsec = 0; 424 return (0); 425 } else { 426 #ifdef ORIGINAL 427 /* computes day number relative to Sept. 19th,1989 */ 428 /* don't even *THINK* about changing formula. It works! */ 429 days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100; 430 #else 431 /* 432 * Changed :-) to make it relative to Jan. 1st, 1970 433 * and to disambiguate negative division 434 */ 435 days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239; 436 #endif 437 crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; 438 439 /* timezone offset is unreliable on some disks */ 440 if (-48 <= tz && tz <= 52) 441 crtime -= tz * 15 * 60; 442 } 443 pu->tv_sec = crtime; 444 pu->tv_nsec = 0; 445 return (1); 446 } 447 448 static u_int 449 cd9660_chars2ui(begin,len) 450 u_char *begin; 451 int len; 452 { 453 u_int rc; 454 455 for (rc = 0; --len >= 0;) { 456 rc *= 10; 457 rc += *begin++ - '0'; 458 } 459 return (rc); 460 } 461 462 int 463 cd9660_tstamp_conv17(pi,pu) 464 u_char *pi; 465 struct timespec *pu; 466 { 467 u_char buf[7]; 468 469 /* year:"0001"-"9999" -> -1900 */ 470 buf[0] = cd9660_chars2ui(pi,4) - 1900; 471 472 /* month: " 1"-"12" -> 1 - 12 */ 473 buf[1] = cd9660_chars2ui(pi + 4,2); 474 475 /* day: " 1"-"31" -> 1 - 31 */ 476 buf[2] = cd9660_chars2ui(pi + 6,2); 477 478 /* hour: " 0"-"23" -> 0 - 23 */ 479 buf[3] = cd9660_chars2ui(pi + 8,2); 480 481 /* minute:" 0"-"59" -> 0 - 59 */ 482 buf[4] = cd9660_chars2ui(pi + 10,2); 483 484 /* second:" 0"-"59" -> 0 - 59 */ 485 buf[5] = cd9660_chars2ui(pi + 12,2); 486 487 /* difference of GMT */ 488 buf[6] = pi[16]; 489 490 return (cd9660_tstamp_conv7(buf,pu)); 491 } 492 493 ino_t 494 isodirino(isodir, imp) 495 struct iso_directory_record *isodir; 496 struct iso_mnt *imp; 497 { 498 ino_t ino; 499 500 ino = (isonum_733(isodir->extent) + 501 isonum_711(isodir->ext_attr_length)) << imp->im_bshift; 502 return (ino); 503 } 504