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