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 * %sccs.include.redist.c%
11 *
12 * @(#)cd9660_node.c 8.8 (Berkeley) 05/22/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 struct simplelock cd9660_ihash_slock;
38
39 #ifdef ISODEVMAP
40 struct iso_node **idvhashtbl;
41 u_long idvhash;
42 #define DNOHASH(device, inum) (((device) + ((inum)>>12)) & idvhash)
43 #endif
44
45 int prtactive; /* 1 => print out reclaim of active vnodes */
46
47 /*
48 * Initialize hash links for inodes and dnodes.
49 */
50 cd9660_init(vfsp)
51 struct vfsconf *vfsp;
52 {
53
54 isohashtbl = hashinit(desiredvnodes, M_ISOFSMNT, &isohash);
55 simple_lock_init(&cd9660_ihash_slock);
56 #ifdef ISODEVMAP
57 idvhashtbl = hashinit(desiredvnodes / 8, M_ISOFSMNT, &idvhash);
58 #endif
59 }
60
61 #ifdef ISODEVMAP
62 /*
63 * Enter a new node into the device hash list
64 */
65 struct iso_dnode *
iso_dmap(device,inum,create)66 iso_dmap(device, inum, create)
67 dev_t device;
68 ino_t inum;
69 int create;
70 {
71 register struct iso_dnode **dpp, *dp, *dq;
72
73 dpp = &idvhashtbl[DNOHASH(device, inum)];
74 for (dp = *dpp;; dp = dp->d_next) {
75 if (dp == NULL)
76 return (NULL);
77 if (inum == dp->i_number && device == dp->i_dev)
78 return (dp);
79
80 if (!create)
81 return (NULL);
82
83 MALLOC(dp, struct iso_dnode *, sizeof(struct iso_dnode), M_CACHE,
84 M_WAITOK);
85 dp->i_dev = dev;
86 dp->i_number = ino;
87
88 if (dq = *dpp)
89 dq->d_prev = dp->d_next;
90 dp->d_next = dq;
91 dp->d_prev = dpp;
92 *dpp = dp;
93
94 return (dp);
95 }
96
97 void
98 iso_dunmap(device)
99 dev_t device;
100 {
101 struct iso_dnode **dpp, *dp, *dq;
102
103 for (dpp = idvhashtbl; dpp <= idvhashtbl + idvhash; dpp++) {
104 for (dp = *dpp; dp != NULL; dp = dq)
105 dq = dp->d_next;
106 if (device == dp->i_dev) {
107 if (dq)
108 dq->d_prev = dp->d_prev;
109 *dp->d_prev = dq;
110 FREE(dp, M_CACHE);
111 }
112 }
113 }
114 }
115 #endif
116
117 /*
118 * Use the device/inum pair to find the incore inode, and return a pointer
119 * to it. If it is in core, but locked, wait for it.
120 */
121 struct vnode *
cd9660_ihashget(dev,inum)122 cd9660_ihashget(dev, inum)
123 dev_t dev;
124 ino_t inum;
125 {
126 struct proc *p = curproc; /* XXX */
127 struct iso_node *ip;
128 struct vnode *vp;
129
130 loop:
131 simple_lock(&cd9660_ihash_slock);
132 for (ip = isohashtbl[INOHASH(dev, inum)]; ip; ip = ip->i_next) {
133 if (inum == ip->i_number && dev == ip->i_dev) {
134 vp = ITOV(ip);
135 simple_lock(&vp->v_interlock);
136 simple_unlock(&cd9660_ihash_slock);
137 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p))
138 goto loop;
139 return (vp);
140 }
141 }
142 simple_unlock(&cd9660_ihash_slock);
143 return (NULL);
144 }
145
146 /*
147 * Insert the inode into the hash table, and return it locked.
148 */
149 void
cd9660_ihashins(ip)150 cd9660_ihashins(ip)
151 struct iso_node *ip;
152 {
153 struct proc *p = curproc; /* XXX */
154 struct iso_node **ipp, *iq;
155
156 simple_lock(&cd9660_ihash_slock);
157 ipp = &isohashtbl[INOHASH(ip->i_dev, ip->i_number)];
158 if (iq = *ipp)
159 iq->i_prev = &ip->i_next;
160 ip->i_next = iq;
161 ip->i_prev = ipp;
162 *ipp = ip;
163 simple_unlock(&cd9660_ihash_slock);
164
165 lockmgr(&ip->i_lock, LK_EXCLUSIVE, (struct simplelock *)0, p);
166 }
167
168 /*
169 * Remove the inode from the hash table.
170 */
171 void
cd9660_ihashrem(ip)172 cd9660_ihashrem(ip)
173 register struct iso_node *ip;
174 {
175 register struct iso_node *iq;
176
177 simple_lock(&cd9660_ihash_slock);
178 if (iq = ip->i_next)
179 iq->i_prev = ip->i_prev;
180 *ip->i_prev = iq;
181 #ifdef DIAGNOSTIC
182 ip->i_next = NULL;
183 ip->i_prev = NULL;
184 #endif
185 simple_unlock(&cd9660_ihash_slock);
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
cd9660_inactive(ap)193 cd9660_inactive(ap)
194 struct vop_inactive_args /* {
195 struct vnode *a_vp;
196 struct proc *a_p;
197 } */ *ap;
198 {
199 struct vnode *vp = ap->a_vp;
200 struct proc *p = ap->a_p;
201 register struct iso_node *ip = VTOI(vp);
202 int mode, error = 0;
203
204 if (prtactive && vp->v_usecount != 0)
205 vprint("cd9660_inactive: pushing active", vp);
206
207 ip->i_flag = 0;
208 VOP_UNLOCK(vp, 0, p);
209 /*
210 * If we are done with the inode, reclaim it
211 * so that it can be reused immediately.
212 */
213 if (ip->inode.iso_mode == 0)
214 vrecycle(vp, (struct simplelock *)0, p);
215 return error;
216 }
217
218 /*
219 * Reclaim an inode so that it can be used for other purposes.
220 */
221 int
cd9660_reclaim(ap)222 cd9660_reclaim(ap)
223 struct vop_reclaim_args /* {
224 struct vnode *a_vp;
225 struct proc *a_p;
226 } */ *ap;
227 {
228 register struct vnode *vp = ap->a_vp;
229 register struct iso_node *ip = VTOI(vp);
230 int i;
231
232 if (prtactive && vp->v_usecount != 0)
233 vprint("cd9660_reclaim: pushing active", vp);
234 /*
235 * Remove the inode from its hash chain.
236 */
237 cd9660_ihashrem(ip);
238 /*
239 * Purge old data structures associated with the inode.
240 */
241 cache_purge(vp);
242 if (ip->i_devvp) {
243 vrele(ip->i_devvp);
244 ip->i_devvp = 0;
245 }
246 FREE(vp->v_data, M_ISOFSNODE);
247 vp->v_data = NULL;
248 return (0);
249 }
250
251 /*
252 * File attributes
253 */
254 void
cd9660_defattr(isodir,inop,bp)255 cd9660_defattr(isodir, inop, bp)
256 struct iso_directory_record *isodir;
257 struct iso_node *inop;
258 struct buf *bp;
259 {
260 struct buf *bp2 = NULL;
261 struct iso_mnt *imp;
262 struct iso_extended_attributes *ap = NULL;
263 int off;
264
265 if (isonum_711(isodir->flags)&2) {
266 inop->inode.iso_mode = S_IFDIR;
267 /*
268 * If we return 2, fts() will assume there are no subdirectories
269 * (just links for the path and .), so instead we return 1.
270 */
271 inop->inode.iso_links = 1;
272 } else {
273 inop->inode.iso_mode = S_IFREG;
274 inop->inode.iso_links = 1;
275 }
276 if (!bp
277 && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT)
278 && (off = isonum_711(isodir->ext_attr_length))) {
279 VOP_BLKATOFF(ITOV(inop), (off_t)-(off << imp->im_bshift), NULL,
280 &bp2);
281 bp = bp2;
282 }
283 if (bp) {
284 ap = (struct iso_extended_attributes *)bp->b_data;
285
286 if (isonum_711(ap->version) == 1) {
287 if (!(ap->perm[0]&0x40))
288 inop->inode.iso_mode |= VEXEC >> 6;
289 if (!(ap->perm[0]&0x10))
290 inop->inode.iso_mode |= VREAD >> 6;
291 if (!(ap->perm[0]&4))
292 inop->inode.iso_mode |= VEXEC >> 3;
293 if (!(ap->perm[0]&1))
294 inop->inode.iso_mode |= VREAD >> 3;
295 if (!(ap->perm[1]&0x40))
296 inop->inode.iso_mode |= VEXEC;
297 if (!(ap->perm[1]&0x10))
298 inop->inode.iso_mode |= VREAD;
299 inop->inode.iso_uid = isonum_723(ap->owner); /* what about 0? */
300 inop->inode.iso_gid = isonum_723(ap->group); /* what about 0? */
301 } else
302 ap = NULL;
303 }
304 if (!ap) {
305 inop->inode.iso_mode |= VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6;
306 inop->inode.iso_uid = (uid_t)0;
307 inop->inode.iso_gid = (gid_t)0;
308 }
309 if (bp2)
310 brelse(bp2);
311 }
312
313 /*
314 * Time stamps
315 */
316 void
cd9660_deftstamp(isodir,inop,bp)317 cd9660_deftstamp(isodir,inop,bp)
318 struct iso_directory_record *isodir;
319 struct iso_node *inop;
320 struct buf *bp;
321 {
322 struct buf *bp2 = NULL;
323 struct iso_mnt *imp;
324 struct iso_extended_attributes *ap = NULL;
325 int off;
326
327 if (!bp
328 && ((imp = inop->i_mnt)->im_flags & ISOFSMNT_EXTATT)
329 && (off = isonum_711(isodir->ext_attr_length))) {
330 VOP_BLKATOFF(ITOV(inop), (off_t)-(off << imp->im_bshift), NULL,
331 &bp2);
332 bp = bp2;
333 }
334 if (bp) {
335 ap = (struct iso_extended_attributes *)bp->b_data;
336
337 if (isonum_711(ap->version) == 1) {
338 if (!cd9660_tstamp_conv17(ap->ftime,&inop->inode.iso_atime))
339 cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_atime);
340 if (!cd9660_tstamp_conv17(ap->ctime,&inop->inode.iso_ctime))
341 inop->inode.iso_ctime = inop->inode.iso_atime;
342 if (!cd9660_tstamp_conv17(ap->mtime,&inop->inode.iso_mtime))
343 inop->inode.iso_mtime = inop->inode.iso_ctime;
344 } else
345 ap = NULL;
346 }
347 if (!ap) {
348 cd9660_tstamp_conv7(isodir->date,&inop->inode.iso_ctime);
349 inop->inode.iso_atime = inop->inode.iso_ctime;
350 inop->inode.iso_mtime = inop->inode.iso_ctime;
351 }
352 if (bp2)
353 brelse(bp2);
354 }
355
356 int
cd9660_tstamp_conv7(pi,pu)357 cd9660_tstamp_conv7(pi,pu)
358 u_char *pi;
359 struct timespec *pu;
360 {
361 int i;
362 int crtime, days;
363 int y, m, d, hour, minute, second, tz;
364
365 y = pi[0] + 1900;
366 m = pi[1];
367 d = pi[2];
368 hour = pi[3];
369 minute = pi[4];
370 second = pi[5];
371 tz = pi[6];
372
373 if (y < 1970) {
374 pu->ts_sec = 0;
375 pu->ts_nsec = 0;
376 return 0;
377 } else {
378 #ifdef ORIGINAL
379 /* computes day number relative to Sept. 19th,1989 */
380 /* don't even *THINK* about changing formula. It works! */
381 days = 367*(y-1980)-7*(y+(m+9)/12)/4-3*((y+(m-9)/7)/100+1)/4+275*m/9+d-100;
382 #else
383 /*
384 * Changed :-) to make it relative to Jan. 1st, 1970
385 * and to disambiguate negative division
386 */
387 days = 367*(y-1960)-7*(y+(m+9)/12)/4-3*((y+(m+9)/12-1)/100+1)/4+275*m/9+d-239;
388 #endif
389 crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second;
390
391 /* timezone offset is unreliable on some disks */
392 if (-48 <= tz && tz <= 52)
393 crtime -= tz * 15 * 60;
394 }
395 pu->ts_sec = crtime;
396 pu->ts_nsec = 0;
397 return 1;
398 }
399
400 static u_int
cd9660_chars2ui(begin,len)401 cd9660_chars2ui(begin,len)
402 u_char *begin;
403 int len;
404 {
405 u_int rc;
406
407 for (rc = 0; --len >= 0;) {
408 rc *= 10;
409 rc += *begin++ - '0';
410 }
411 return rc;
412 }
413
414 int
cd9660_tstamp_conv17(pi,pu)415 cd9660_tstamp_conv17(pi,pu)
416 u_char *pi;
417 struct timespec *pu;
418 {
419 u_char buf[7];
420
421 /* year:"0001"-"9999" -> -1900 */
422 buf[0] = cd9660_chars2ui(pi,4) - 1900;
423
424 /* month: " 1"-"12" -> 1 - 12 */
425 buf[1] = cd9660_chars2ui(pi + 4,2);
426
427 /* day: " 1"-"31" -> 1 - 31 */
428 buf[2] = cd9660_chars2ui(pi + 6,2);
429
430 /* hour: " 0"-"23" -> 0 - 23 */
431 buf[3] = cd9660_chars2ui(pi + 8,2);
432
433 /* minute:" 0"-"59" -> 0 - 59 */
434 buf[4] = cd9660_chars2ui(pi + 10,2);
435
436 /* second:" 0"-"59" -> 0 - 59 */
437 buf[5] = cd9660_chars2ui(pi + 12,2);
438
439 /* difference of GMT */
440 buf[6] = pi[16];
441
442 return cd9660_tstamp_conv7(buf,pu);
443 }
444
445 ino_t
isodirino(isodir,imp)446 isodirino(isodir, imp)
447 struct iso_directory_record *isodir;
448 struct iso_mnt *imp;
449 {
450 ino_t ino;
451
452 ino = (isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length))
453 << imp->im_bshift;
454 return (ino);
455 }
456