xref: /original-bsd/sys/isofs/cd9660/cd9660_node.c (revision 58b1b499)
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