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