xref: /freebsd/sys/fs/cd9660/cd9660_node.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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  * 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)cd9660_node.c	8.2 (Berkeley) 1/23/94
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/mount.h>
44 #include <sys/proc.h>
45 #include <sys/file.h>
46 #include <sys/buf.h>
47 #include <sys/vnode.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/stat.h>
51 
52 #include <isofs/cd9660/iso.h>
53 #include <isofs/cd9660/cd9660_node.h>
54 #include <isofs/cd9660/iso_rrip.h>
55 
56 #define	INOHSZ	512
57 #if	((INOHSZ&(INOHSZ-1)) == 0)
58 #define	INOHASH(dev,ino)	(((dev)+((ino)>>12))&(INOHSZ-1))
59 #else
60 #define	INOHASH(dev,ino)	(((unsigned)((dev)+((ino)>>12)))%INOHSZ)
61 #endif
62 
63 union iso_ihead {
64 	union  iso_ihead *ih_head[2];
65 	struct iso_node *ih_chain[2];
66 } iso_ihead[INOHSZ];
67 
68 #ifdef	ISODEVMAP
69 #define	DNOHSZ	64
70 #if	((DNOHSZ&(DNOHSZ-1)) == 0)
71 #define	DNOHASH(dev,ino)	(((dev)+((ino)>>12))&(DNOHSZ-1))
72 #else
73 #define	DNOHASH(dev,ino)	(((unsigned)((dev)+((ino)>>12)))%DNOHSZ)
74 #endif
75 
76 union iso_dhead {
77 	union  iso_dhead  *dh_head[2];
78 	struct iso_dnode *dh_chain[2];
79 } iso_dhead[DNOHSZ];
80 #endif
81 
82 int prtactive;	/* 1 => print out reclaim of active vnodes */
83 
84 /*
85  * Initialize hash links for inodes and dnodes.
86  */
87 int
88 cd9660_init()
89 {
90 	register int i;
91 	register union iso_ihead *ih = iso_ihead;
92 #ifdef	ISODEVMAP
93 	register union iso_dhead *dh = iso_dhead;
94 #endif
95 
96 	for (i = INOHSZ; --i >= 0; ih++) {
97 		ih->ih_head[0] = ih;
98 		ih->ih_head[1] = ih;
99 	}
100 #ifdef	ISODEVMAP
101 	for (i = DNOHSZ; --i >= 0; dh++) {
102 		dh->dh_head[0] = dh;
103 		dh->dh_head[1] = dh;
104 	}
105 #endif
106 	return (0);
107 }
108 
109 #ifdef	ISODEVMAP
110 /*
111  * Enter a new node into the device hash list
112  */
113 struct iso_dnode *
114 iso_dmap(dev,ino,create)
115 	dev_t	dev;
116 	ino_t	ino;
117 	int	create;
118 {
119 	struct iso_dnode *dp;
120 	union iso_dhead *dh;
121 
122 	dh = &iso_dhead[DNOHASH(dev, ino)];
123 	for (dp = dh->dh_chain[0];
124 	     dp != (struct iso_dnode *)dh;
125 	     dp = dp->d_forw)
126 		if (ino == dp->i_number && dev == dp->i_dev)
127 			return dp;
128 
129 	if (!create)
130 		return (struct iso_dnode *)0;
131 
132 	MALLOC(dp,struct iso_dnode *,sizeof(struct iso_dnode),M_CACHE,M_WAITOK);
133 	dp->i_dev = dev;
134 	dp->i_number = ino;
135 	insque(dp,dh);
136 
137 	return dp;
138 }
139 
140 void
141 iso_dunmap(dev)
142 	dev_t	dev;
143 {
144 	struct iso_dnode *dp, *dq;
145 	union iso_dhead *dh;
146 
147 	for (dh = iso_dhead; dh < iso_dhead + DNOHSZ; dh++) {
148 		for (dp = dh->dh_chain[0];
149 		     dp != (struct iso_dnode *)dh;
150 		     dp = dq) {
151 			dq = dp->d_forw;
152 			if (dev == dp->i_dev) {
153 				remque(dp);
154 				FREE(dp,M_CACHE);
155 			}
156 		}
157 	}
158 }
159 #endif
160 
161 /*
162  * Look up a ISOFS dinode number to find its incore vnode.
163  * If it is not in core, read it in from the specified device.
164  * If it is in core, wait for the lock bit to clear, then
165  * return the inode locked. Detection and handling of mount
166  * points must be done by the calling routine.
167  */
168 int
169 iso_iget(xp, ino, relocated, ipp, isodir)
170 	struct iso_node *xp;
171 	ino_t ino;
172 	int relocated;
173 	struct iso_node **ipp;
174 	struct iso_directory_record *isodir;
175 {
176 	dev_t dev = xp->i_dev;
177 	struct mount *mntp = ITOV(xp)->v_mount;
178 	register struct iso_node *ip, *iq;
179 	register struct vnode *vp;
180 	register struct iso_dnode *dp;
181 	struct vnode *nvp;
182 	struct buf *bp = NULL, *bp2 = NULL;
183 	union iso_ihead *ih;
184 	union iso_dhead *dh;
185 	int i, error, result;
186 	struct iso_mnt *imp;
187 	ino_t defino;
188 
189 	ih = &iso_ihead[INOHASH(dev, ino)];
190 loop:
191 	for (ip = ih->ih_chain[0];
192 	     ip != (struct iso_node *)ih;
193 	     ip = ip->i_forw) {
194 		if (ino != ip->i_number || dev != ip->i_dev)
195 			continue;
196 		if ((ip->i_flag&ILOCKED) != 0) {
197 			ip->i_flag |= IWANT;
198 			sleep((caddr_t)ip, PINOD);
199 			goto loop;
200 		}
201 		if (vget(ITOV(ip), 1))
202 			goto loop;
203 		*ipp = ip;
204 		return 0;
205 	}
206 	/*
207 	 * Allocate a new vnode/iso_node.
208 	 */
209 	if (error = getnewvnode(VT_ISOFS, mntp, cd9660_vnodeop_p, &nvp)) {
210 		*ipp = 0;
211 		return error;
212 	}
213 	MALLOC(ip, struct iso_node *, sizeof(struct iso_node),
214 	       M_ISOFSNODE, M_WAITOK);
215 	bzero((caddr_t)ip, sizeof(struct iso_node));
216 	nvp->v_data = ip;
217 	ip->i_vnode = nvp;
218 	ip->i_flag = 0;
219 	ip->i_devvp = 0;
220 	ip->i_diroff = 0;
221 	ip->i_lockf = 0;
222 
223 	/*
224 	 * Put it onto its hash chain and lock it so that other requests for
225 	 * this inode will block if they arrive while we are sleeping waiting
226 	 * for old data structures to be purged or for the contents of the
227 	 * disk portion of this inode to be read.
228 	 */
229 	ip->i_dev = dev;
230 	ip->i_number = ino;
231 	insque(ip, ih);
232 	ISO_ILOCK(ip);
233 
234 	imp = VFSTOISOFS (mntp);
235 	ip->i_mnt = imp;
236 	ip->i_devvp = imp->im_devvp;
237 	VREF(ip->i_devvp);
238 
239 	if (relocated) {
240 		/*
241 		 * On relocated directories we must
242 		 * read the `.' entry out of a dir.
243 		 */
244 		ip->iso_start = ino >> imp->im_bshift;
245 		if (error = iso_blkatoff(ip,0,&bp)) {
246 			vrele(ip->i_devvp);
247 			remque(ip);
248 			ip->i_forw = ip;
249 			ip->i_back = ip;
250 			iso_iput(ip);
251 			*ipp = 0;
252 			return error;
253 		}
254 		isodir = (struct iso_directory_record *)bp->b_un.b_addr;
255 	}
256 
257 	ip->iso_extent = isonum_733(isodir->extent);
258 	ip->i_size = isonum_733(isodir->size);
259 	ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
260 
261 	vp = ITOV(ip);
262 
263 	/*
264 	 * Setup time stamp, attribute
265 	 */
266 	vp->v_type = VNON;
267 	switch (imp->iso_ftype) {
268 	default:	/* ISO_FTYPE_9660 */
269 		if ((imp->im_flags&ISOFSMNT_EXTATT)
270 		    && isonum_711(isodir->ext_attr_length))
271 			iso_blkatoff(ip,-isonum_711(isodir->ext_attr_length),
272 				     &bp2);
273 		cd9660_defattr(isodir,ip,bp2 );
274 		cd9660_deftstamp(isodir,ip,bp2 );
275 		break;
276 	case ISO_FTYPE_RRIP:
277 		result = cd9660_rrip_analyze(isodir,ip,imp);
278 		break;
279 	}
280 	if (bp2)
281 		brelse(bp2);
282 	if (bp)
283 		brelse(bp);
284 
285 	/*
286 	 * Initialize the associated vnode
287 	 */
288 	vp->v_type = IFTOVT(ip->inode.iso_mode);
289 
290 	if ( vp->v_type == VFIFO ) {
291 #ifdef	FIFO
292 		extern int (**cd9660_fifoop_p)();
293 		vp->v_op = cd9660_fifoop_p;
294 #else
295 		iso_iput(ip);
296 		*ipp = 0;
297 		return EOPNOTSUPP;
298 #endif	/* FIFO */
299 	} else if ( vp->v_type == VCHR || vp->v_type == VBLK ) {
300 		extern int (**cd9660_specop_p)();
301 
302 		/*
303 		 * if device, look at device number table for translation
304 		 */
305 #ifdef	ISODEVMAP
306 		if (dp = iso_dmap(dev,ino,0))
307 			ip->inode.iso_rdev = dp->d_dev;
308 #endif
309 		vp->v_op = cd9660_specop_p;
310 		if (nvp = checkalias(vp, ip->inode.iso_rdev, mntp)) {
311 			/*
312 			 * Reinitialize aliased inode.
313 			 */
314 			vp = nvp;
315 			iq = VTOI(vp);
316 			iq->i_vnode = vp;
317 			iq->i_flag = 0;
318 			ISO_ILOCK(iq);
319 			iq->i_dev = dev;
320 			iq->i_number = ino;
321 			iq->i_mnt = ip->i_mnt;
322 			bcopy(&ip->iso_extent,&iq->iso_extent,
323 			      (char *)(ip + 1) - (char *)&ip->iso_extent);
324 			insque(iq, ih);
325 			/*
326 			 * Discard unneeded vnode
327 			 * (This introduces the need of INACTIVE modification)
328 			 */
329 			ip->inode.iso_mode = 0;
330 			iso_iput(ip);
331 			ip = iq;
332 		}
333 	}
334 
335 	if (ip->iso_extent == imp->root_extent)
336 		vp->v_flag |= VROOT;
337 
338 	*ipp = ip;
339 	return 0;
340 }
341 
342 /*
343  * Unlock and decrement the reference count of an inode structure.
344  */
345 int
346 iso_iput(ip)
347 	register struct iso_node *ip;
348 {
349 
350 	if ((ip->i_flag & ILOCKED) == 0)
351 		panic("iso_iput");
352 	ISO_IUNLOCK(ip);
353 	vrele(ITOV(ip));
354 	return (0);
355 }
356 
357 /*
358  * Last reference to an inode, write the inode out and if necessary,
359  * truncate and deallocate the file.
360  */
361 int
362 cd9660_inactive(ap)
363 	struct vop_inactive_args /* {
364 		struct vnode *a_vp;
365 	} */ *ap;
366 {
367 	struct vnode *vp = ap->a_vp;
368 	register struct iso_node *ip = VTOI(vp);
369 	int mode, error = 0;
370 
371 	if (prtactive && vp->v_usecount != 0)
372 		vprint("cd9660_inactive: pushing active", vp);
373 
374 	ip->i_flag = 0;
375 	/*
376 	 * If we are done with the inode, reclaim it
377 	 * so that it can be reused immediately.
378 	 */
379 	if (vp->v_usecount == 0 && ip->inode.iso_mode == 0)
380 		vgone(vp);
381 	return error;
382 }
383 
384 /*
385  * Reclaim an inode so that it can be used for other purposes.
386  */
387 int
388 cd9660_reclaim(ap)
389 	struct vop_reclaim_args /* {
390 		struct vnode *a_vp;
391 	} */ *ap;
392 {
393 	register struct vnode *vp = ap->a_vp;
394 	register struct iso_node *ip = VTOI(vp);
395 	int i;
396 
397 	if (prtactive && vp->v_usecount != 0)
398 		vprint("cd9660_reclaim: pushing active", vp);
399 	/*
400 	 * Remove the inode from its hash chain.
401 	 */
402 	remque(ip);
403 	ip->i_forw = ip;
404 	ip->i_back = ip;
405 	/*
406 	 * Purge old data structures associated with the inode.
407 	 */
408 	cache_purge(vp);
409 	if (ip->i_devvp) {
410 		vrele(ip->i_devvp);
411 		ip->i_devvp = 0;
412 	}
413 	FREE(vp->v_data, M_ISOFSNODE);
414 	vp->v_data = NULL;
415 	return 0;
416 }
417 
418 /*
419  * Lock an inode. If its already locked, set the WANT bit and sleep.
420  */
421 int
422 iso_ilock(ip)
423 	register struct iso_node *ip;
424 {
425 
426 	while (ip->i_flag & ILOCKED) {
427 		ip->i_flag |= IWANT;
428 		if (ip->i_spare0 == curproc->p_pid)
429 			panic("locking against myself");
430 		ip->i_spare1 = curproc->p_pid;
431 		(void) sleep((caddr_t)ip, PINOD);
432 	}
433 	ip->i_spare1 = 0;
434 	ip->i_spare0 = curproc->p_pid;
435 	ip->i_flag |= ILOCKED;
436 	return (0);
437 }
438 
439 /*
440  * Unlock an inode.  If WANT bit is on, wakeup.
441  */
442 int
443 iso_iunlock(ip)
444 	register struct iso_node *ip;
445 {
446 
447 	if ((ip->i_flag & ILOCKED) == 0)
448 		vprint("iso_iunlock: unlocked inode", ITOV(ip));
449 	ip->i_spare0 = 0;
450 	ip->i_flag &= ~ILOCKED;
451 	if (ip->i_flag&IWANT) {
452 		ip->i_flag &= ~IWANT;
453 		wakeup((caddr_t)ip);
454 	}
455 	return (0);
456 }
457 
458 /*
459  * File attributes
460  */
461 void
462 cd9660_defattr(isodir,inop,bp)
463 	struct iso_directory_record *isodir;
464 	struct iso_node *inop;
465 	struct buf *bp;
466 {
467 	struct buf *bp2 = NULL;
468 	struct iso_mnt *imp;
469 	struct iso_extended_attributes *ap = NULL;
470 	int off;
471 
472 	if (isonum_711(isodir->flags)&2) {
473 		inop->inode.iso_mode = S_IFDIR;
474 		/*
475 		 * If we return 2, fts() will assume there are no subdirectories
476 		 * (just links for the path and .), so instead we return 1.
477 		 */
478 		inop->inode.iso_links = 1;
479 	} else {
480 		inop->inode.iso_mode = S_IFREG;
481 		inop->inode.iso_links = 1;
482 	}
483 	if (!bp
484 	    && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT)
485 	    && (off = isonum_711(isodir->ext_attr_length))) {
486 		iso_blkatoff(inop,-off * imp->logical_block_size,&bp2);
487 		bp = bp2;
488 	}
489 	if (bp) {
490 		ap = (struct iso_extended_attributes *)bp->b_un.b_addr;
491 
492 		if (isonum_711(ap->version) == 1) {
493 			if (!(ap->perm[0]&0x40))
494 				inop->inode.iso_mode |= VEXEC >> 6;
495 			if (!(ap->perm[0]&0x10))
496 				inop->inode.iso_mode |= VREAD >> 6;
497 			if (!(ap->perm[0]&4))
498 				inop->inode.iso_mode |= VEXEC >> 3;
499 			if (!(ap->perm[0]&1))
500 				inop->inode.iso_mode |= VREAD >> 3;
501 			if (!(ap->perm[1]&0x40))
502 				inop->inode.iso_mode |= VEXEC;
503 			if (!(ap->perm[1]&0x10))
504 				inop->inode.iso_mode |= VREAD;
505 			inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */
506 			inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */
507 		} else
508 			ap = NULL;
509 	}
510 	if (!ap) {
511 		inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6;
512 		inop->inode.iso_uid = (uid_t)0;
513 		inop->inode.iso_gid = (gid_t)0;
514 	}
515 	if (bp2)
516 		brelse(bp2);
517 }
518 
519 /*
520  * Time stamps
521  */
522 void
523 cd9660_deftstamp(isodir,inop,bp)
524 	struct iso_directory_record *isodir;
525 	struct iso_node *inop;
526 	struct buf *bp;
527 {
528 	struct buf *bp2 = NULL;
529 	struct iso_mnt *imp;
530 	struct iso_extended_attributes *ap = NULL;
531 	int off;
532 
533 	if (!bp
534 	    && ((imp = inop->i_mnt)->im_flags&ISOFSMNT_EXTATT)
535 	    && (off = isonum_711(isodir->ext_attr_length))) {
536 		iso_blkatoff(inop,-off * imp->logical_block_size,&bp2);
537 		bp = bp2;
538 	}
539 	if (bp) {
540 		ap = (struct iso_extended_attributes *)bp->b_un.b_addr;
541 
542 		if (isonum_711(ap->version) == 1) {
543 			if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime))
544 				cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime);
545 			if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime))
546 				inop->inode.iso_ctime = inop->inode.iso_atime;
547 			if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime))
548 				inop->inode.iso_mtime = inop->inode.iso_ctime;
549 		} else
550 			ap = NULL;
551 	}
552 	if (!ap) {
553 		cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime);
554 		inop->inode.iso_atime = inop->inode.iso_ctime;
555 		inop->inode.iso_mtime = inop->inode.iso_ctime;
556 	}
557 	if (bp2)
558 		brelse(bp2);
559 }
560 
561 int
562 cd9660_tstamp_conv7(pi,pu)
563 char *pi;
564 struct timeval *pu;
565 {
566 	int i;
567 	int crtime, days;
568 	int y, m, d, hour, minute, second, tz;
569 
570 	y = pi[0] + 1900;
571 	m = pi[1];
572 	d = pi[2];
573 	hour = pi[3];
574 	minute = pi[4];
575 	second = pi[5];
576 	tz = pi[6];
577 
578 	if (y < 1970) {
579 		pu->tv_sec  = 0;
580 		pu->tv_usec = 0;
581 		return 0;
582 	} else {
583 #ifdef	ORIGINAL
584 		/* computes day number relative to Sept. 19th,1989 */
585 		/* don't even *THINK* about changing formula. It works! */
586 		days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100;
587 #else
588 		/*
589 		 * Changed :-) to make it relative to Jan. 1st, 1970
590 		 * and to disambiguate negative division
591 		 */
592 		days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239;
593 #endif
594 		crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second;
595 
596 		/* timezone offset is unreliable on some disks */
597 		if (-48 <= tz && tz <= 52)
598 			crtime += tz * 15 * 60;
599 	}
600 	pu->tv_sec  = crtime;
601 	pu->tv_usec = 0;
602 	return 1;
603 }
604 
605 static unsigned
606 cd9660_chars2ui(begin,len)
607 	unsigned char *begin;
608 	int len;
609 {
610 	unsigned rc;
611 
612 	for (rc = 0; --len >= 0;) {
613 		rc *= 10;
614 		rc += *begin++ - '0';
615 	}
616 	return rc;
617 }
618 
619 int
620 cd9660_tstamp_conv17(pi,pu)
621 	unsigned char *pi;
622 	struct timeval *pu;
623 {
624 	unsigned char buf[7];
625 
626 	/* year:"0001"-"9999" -> -1900  */
627 	buf[0] = cd9660_chars2ui(pi,4) - 1900;
628 
629 	/* month: " 1"-"12"      -> 1 - 12 */
630 	buf[1] = cd9660_chars2ui(pi + 4,2);
631 
632 	/* day:   " 1"-"31"      -> 1 - 31 */
633 	buf[2] = cd9660_chars2ui(pi + 6,2);
634 
635 	/* hour:  " 0"-"23"      -> 0 - 23 */
636 	buf[3] = cd9660_chars2ui(pi + 8,2);
637 
638 	/* minute:" 0"-"59"      -> 0 - 59 */
639 	buf[4] = cd9660_chars2ui(pi + 10,2);
640 
641 	/* second:" 0"-"59"      -> 0 - 59 */
642 	buf[5] = cd9660_chars2ui(pi + 12,2);
643 
644 	/* difference of GMT */
645 	buf[6] = pi[16];
646 
647 	return cd9660_tstamp_conv7(buf,pu);
648 }
649 
650 void
651 isodirino(inump,isodir,imp)
652 	ino_t *inump;
653 	struct iso_directory_record *isodir;
654 	struct iso_mnt *imp;
655 {
656 	*inump = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length))
657 		 * imp->logical_block_size;
658 }
659