1 /*- 2 * Copyright (c) 1982, 1986, 1989, 1994, 1995 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 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)cd9660_node.c 8.2 (Berkeley) 1/23/94 35 * $FreeBSD: src/sys/isofs/cd9660/cd9660_node.c,v 1.29.2.1 2000/07/08 14:35:56 bp Exp $ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/mount.h> 41 #include <sys/proc.h> 42 #include <sys/buf.h> 43 #include <sys/vnode.h> 44 #include <sys/malloc.h> 45 #include <sys/stat.h> 46 47 #include "iso.h" 48 #include "cd9660_node.h" 49 #include "cd9660_mount.h" 50 51 #define CD9660_HASH_SIZE_LIMIT 8192 52 53 /* 54 * Structures associated with iso_node caching. 55 */ 56 static struct iso_node **isohashtbl; 57 static u_long isohash; 58 #define INOHASH(device, inum) ((minor(device) + ((inum)>>12)) & isohash) 59 #ifndef NULL_SIMPLELOCKS 60 static struct lwkt_token cd9660_ihash_token; 61 #endif 62 63 static void cd9660_ihashrem(struct iso_node *); 64 static unsigned cd9660_chars2ui(unsigned char *begin, int len); 65 66 /* 67 * Initialize hash links for inodes and dnodes. CDs and DVDs are small 68 * and slow compared to hard disks, there is no need to have a huge hash 69 * table so the size is capped at CD9660_HASH_SIZE_LIMIT. 70 */ 71 int 72 cd9660_init(struct vfsconf *vfsp) 73 { 74 int hsize; 75 76 hsize = vfs_inodehashsize(); 77 78 if (hsize < CD9660_HASH_SIZE_LIMIT) 79 hsize = CD9660_HASH_SIZE_LIMIT; 80 81 isohash = 16; 82 while (isohash < hsize) 83 isohash <<= 1; 84 isohashtbl = kmalloc(sizeof(void *) * isohash, 85 M_ISOFSMNT, M_WAITOK|M_ZERO); 86 --isohash; 87 lwkt_token_init(&cd9660_ihash_token, "cd9660ihash"); 88 89 return (0); 90 } 91 92 int 93 cd9660_uninit(struct vfsconf *vfsp) 94 { 95 96 if (isohashtbl != NULL) 97 kfree(isohashtbl, M_ISOFSMNT); 98 return (0); 99 } 100 101 102 /* 103 * Use the device/inum pair to find the incore inode, and return a pointer 104 * to it. If it is in core, but locked, wait for it. 105 */ 106 struct vnode * 107 cd9660_ihashget(cdev_t dev, ino_t inum) 108 { 109 struct iso_node *ip; 110 struct vnode *vp; 111 112 lwkt_gettoken(&cd9660_ihash_token); 113 loop: 114 for (ip = isohashtbl[INOHASH(dev, inum)]; ip; ip = ip->i_next) { 115 if (inum != ip->i_number || dev != ip->i_dev) 116 continue; 117 vp = ITOV(ip); 118 if (vget(vp, LK_EXCLUSIVE)) 119 goto loop; 120 /* 121 * We must check to see if the inode has been ripped 122 * out from under us after blocking. 123 */ 124 for (ip = isohashtbl[INOHASH(dev, inum)]; ip; ip = ip->i_next) { 125 if (inum == ip->i_number && dev == ip->i_dev) 126 break; 127 } 128 if (ip == NULL || ITOV(ip) != vp) { 129 goto loop; 130 } 131 lwkt_reltoken(&cd9660_ihash_token); 132 return (vp); 133 } 134 lwkt_reltoken(&cd9660_ihash_token); 135 return (NULL); 136 } 137 138 /* 139 * Insert the inode into the hash table, return 0 on success, non-zero 140 * if the inode has already been found to be in the hash table. 141 */ 142 int 143 cd9660_ihashins(struct iso_node *ip) 144 { 145 struct iso_node **ipp, *iq; 146 147 lwkt_gettoken(&cd9660_ihash_token); 148 ipp = &isohashtbl[INOHASH(ip->i_dev, ip->i_number)]; 149 while ((iq = *ipp) != NULL) { 150 if (iq->i_dev == ip->i_dev && iq->i_number == ip->i_number) { 151 lwkt_reltoken(&cd9660_ihash_token); 152 return(EBUSY); 153 } 154 ipp = &iq->i_next; 155 } 156 ip->i_next = NULL; 157 *ipp = ip; 158 lwkt_reltoken(&cd9660_ihash_token); 159 return(0); 160 } 161 162 /* 163 * Remove the inode from the hash table. 164 */ 165 static void 166 cd9660_ihashrem(struct iso_node *ip) 167 { 168 struct iso_node **ipp, *iq; 169 170 lwkt_gettoken(&cd9660_ihash_token); 171 ipp = &isohashtbl[INOHASH(ip->i_dev, ip->i_number)]; 172 while ((iq = *ipp) != NULL) { 173 if (ip == iq) 174 break; 175 ipp = &iq->i_next; 176 } 177 KKASSERT(ip == iq); 178 *ipp = ip->i_next; 179 ip->i_next = NULL; 180 lwkt_reltoken(&cd9660_ihash_token); 181 } 182 183 /* 184 * Last reference to an inode, write the inode out and if necessary, 185 * truncate and deallocate the file. 186 * 187 * cd9660_inactive(struct vnode *a_vp) 188 */ 189 int 190 cd9660_inactive(struct vop_inactive_args *ap) 191 { 192 struct vnode *vp = ap->a_vp; 193 struct iso_node *ip = VTOI(vp); 194 int error = 0; 195 196 if (prtactive && VREFCNT(vp) > 1) 197 vprint("cd9660_inactive: pushing active", vp); 198 199 if (ip) 200 ip->i_flag = 0; 201 /* 202 * If we are done with the inode, reclaim it 203 * so that it can be reused immediately. 204 */ 205 if (ip == NULL || ip->inode.iso_mode == 0) 206 vrecycle(vp); 207 return error; 208 } 209 210 /* 211 * Reclaim an inode so that it can be used for other purposes. 212 * 213 * cd9660_reclaim(struct vnode *a_vp, struct proc *a_p) 214 */ 215 int 216 cd9660_reclaim(struct vop_reclaim_args *ap) 217 { 218 struct vnode *vp = ap->a_vp; 219 struct iso_node *ip = VTOI(vp); 220 221 if (prtactive && VREFCNT(vp) > 1) 222 vprint("cd9660_reclaim: pushing active", vp); 223 /* 224 * Remove the inode from its hash chain. 225 */ 226 vp->v_data = NULL; 227 if (ip) { 228 cd9660_ihashrem(ip); 229 if (ip->i_devvp) { 230 vrele(ip->i_devvp); 231 ip->i_devvp = 0; 232 } 233 kfree(ip, M_ISOFSNODE); 234 } 235 return (0); 236 } 237 238 /* 239 * File attributes 240 */ 241 void 242 cd9660_defattr(struct iso_directory_record *isodir, struct iso_node *inop, 243 struct buf *bp, enum ISO_FTYPE ftype) 244 { 245 struct buf *bp2 = NULL; 246 struct iso_mnt *imp; 247 struct iso_extended_attributes *ap = NULL; 248 int off; 249 250 /* high sierra does not have timezone data, flag is one byte ahead */ 251 if (isonum_711(ftype == ISO_FTYPE_HIGH_SIERRA? 252 &isodir->date[6]: isodir->flags)&2) { 253 inop->inode.iso_mode = S_IFDIR; 254 /* 255 * If we return 2, fts() will assume there are no subdirectories 256 * (just links for the path and .), so instead we return 1. 257 */ 258 inop->inode.iso_links = 1; 259 } else { 260 inop->inode.iso_mode = S_IFREG; 261 inop->inode.iso_links = 1; 262 } 263 if (!bp 264 && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT) 265 && (off = isonum_711(isodir->ext_attr_length))) { 266 cd9660_devblkatoff(ITOV(inop), (off_t)-(off << imp->im_bshift), NULL, 267 &bp2); 268 bp = bp2; 269 } 270 if (bp) { 271 ap = (struct iso_extended_attributes *)bp->b_data; 272 273 if (isonum_711(ap->version) == 1) { 274 if (!(ap->perm[0]&0x40)) 275 inop->inode.iso_mode |= VEXEC >> 6; 276 if (!(ap->perm[0]&0x10)) 277 inop->inode.iso_mode |= VREAD >> 6; 278 if (!(ap->perm[0]&4)) 279 inop->inode.iso_mode |= VEXEC >> 3; 280 if (!(ap->perm[0]&1)) 281 inop->inode.iso_mode |= VREAD >> 3; 282 if (!(ap->perm[1]&0x40)) 283 inop->inode.iso_mode |= VEXEC; 284 if (!(ap->perm[1]&0x10)) 285 inop->inode.iso_mode |= VREAD; 286 inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */ 287 inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */ 288 } else 289 ap = NULL; 290 } 291 if (!ap) { 292 inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6; 293 inop->inode.iso_uid = (uid_t)0; 294 inop->inode.iso_gid = (gid_t)0; 295 } 296 if (bp2) 297 brelse(bp2); 298 } 299 300 /* 301 * Time stamps 302 */ 303 void 304 cd9660_deftstamp(struct iso_directory_record *isodir, struct iso_node *inop, 305 struct buf *bp, enum ISO_FTYPE ftype) 306 { 307 struct buf *bp2 = NULL; 308 struct iso_mnt *imp; 309 struct iso_extended_attributes *ap = NULL; 310 int off; 311 312 if (!bp 313 && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT) 314 && (off = isonum_711(isodir->ext_attr_length))) { 315 cd9660_devblkatoff(ITOV(inop), (off_t)-(off << imp->im_bshift), NULL, 316 &bp2); 317 bp = bp2; 318 } 319 if (bp) { 320 ap = (struct iso_extended_attributes *)bp->b_data; 321 322 if (ftype != ISO_FTYPE_HIGH_SIERRA 323 && isonum_711(ap->version) == 1) { 324 if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime)) 325 cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime); 326 if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime)) 327 inop->inode.iso_ctime = inop->inode.iso_atime; 328 if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime)) 329 inop->inode.iso_mtime = inop->inode.iso_ctime; 330 } else 331 ap = NULL; 332 } 333 if (!ap) { 334 cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime,ftype); 335 inop->inode.iso_atime = inop->inode.iso_ctime; 336 inop->inode.iso_mtime = inop->inode.iso_ctime; 337 } 338 if (bp2) 339 brelse(bp2); 340 } 341 342 int 343 cd9660_tstamp_conv7(u_char *pi, struct timespec *pu, enum ISO_FTYPE ftype) 344 { 345 int crtime, days; 346 int y, m, d, hour, minute, second, tz; 347 348 y = pi[0] + 1900; 349 m = pi[1]; 350 d = pi[2]; 351 hour = pi[3]; 352 minute = pi[4]; 353 second = pi[5]; 354 if(ftype != ISO_FTYPE_HIGH_SIERRA) 355 tz = pi[6]; 356 else 357 /* original high sierra misses timezone data */ 358 tz = 0; 359 360 if (y < 1970) { 361 pu->tv_sec = 0; 362 pu->tv_nsec = 0; 363 return 0; 364 } else { 365 #ifdef ORIGINAL 366 /* computes day number relative to Sept. 19th,1989 */ 367 /* don't even *THINK* about changing formula. It works! */ 368 days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100; 369 #else 370 /* 371 * Changed :-) to make it relative to Jan. 1st, 1970 372 * and to disambiguate negative division 373 */ 374 days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239; 375 #endif 376 crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; 377 378 /* timezone offset is unreliable on some disks */ 379 if (-48 <= tz && tz <= 52) 380 crtime -= tz * 15 * 60; 381 } 382 pu->tv_sec = crtime; 383 pu->tv_nsec = 0; 384 return 1; 385 } 386 387 static u_int 388 cd9660_chars2ui(u_char *begin, int len) 389 { 390 u_int rc; 391 392 for (rc = 0; --len >= 0;) { 393 rc *= 10; 394 rc += *begin++ - '0'; 395 } 396 return rc; 397 } 398 399 int 400 cd9660_tstamp_conv17(u_char *pi, struct timespec *pu) 401 { 402 u_char buf[7]; 403 404 /* year:"0001"-"9999" -> -1900 */ 405 buf[0] = cd9660_chars2ui(pi,4) - 1900; 406 407 /* month: " 1"-"12" -> 1 - 12 */ 408 buf[1] = cd9660_chars2ui(pi + 4,2); 409 410 /* day: " 1"-"31" -> 1 - 31 */ 411 buf[2] = cd9660_chars2ui(pi + 6,2); 412 413 /* hour: " 0"-"23" -> 0 - 23 */ 414 buf[3] = cd9660_chars2ui(pi + 8,2); 415 416 /* minute:" 0"-"59" -> 0 - 59 */ 417 buf[4] = cd9660_chars2ui(pi + 10,2); 418 419 /* second:" 0"-"59" -> 0 - 59 */ 420 buf[5] = cd9660_chars2ui(pi + 12,2); 421 422 /* difference of GMT */ 423 buf[6] = pi[16]; 424 425 return cd9660_tstamp_conv7(buf, pu, ISO_FTYPE_DEFAULT); 426 } 427 428 ino_t 429 isodirino(struct iso_directory_record *isodir, struct iso_mnt *imp) 430 { 431 ino_t ino; 432 433 ino = (ino_t)(isonum_733(isodir->extent) + 434 isonum_711(isodir->ext_attr_length)) << imp->im_bshift; 435 return (ino); 436 } 437