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