xref: /openbsd-src/sys/isofs/udf/udf_vnops.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: udf_vnops.c,v 1.45 2012/06/20 17:30:22 matthew Exp $	*/
2 
3 /*
4  * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD: src/sys/fs/udf/udf_vnops.c,v 1.50 2005/01/28 14:42:16 phk Exp $
29  */
30 
31 /*
32  * Ported to OpenBSD by Pedro Martelletto in February 2005.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/namei.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/mutex.h>
41 #include <sys/stat.h>
42 #include <sys/buf.h>
43 #include <sys/pool.h>
44 #include <sys/lock.h>
45 #include <sys/mount.h>
46 #include <sys/vnode.h>
47 #include <sys/dirent.h>
48 #include <sys/queue.h>
49 #include <sys/unistd.h>
50 #include <sys/endian.h>
51 #include <sys/specdev.h>
52 
53 #include <isofs/udf/ecma167-udf.h>
54 #include <isofs/udf/udf.h>
55 #include <isofs/udf/udf_extern.h>
56 
57 int udf_bmap_internal(struct unode *, off_t, daddr64_t *, uint32_t *);
58 
59 struct vops udf_vops = {
60 	.vop_access	= udf_access,
61 	.vop_bmap	= udf_bmap,
62 	.vop_lookup	= udf_lookup,
63 	.vop_getattr	= udf_getattr,
64 	.vop_open	= udf_open,
65 	.vop_close	= udf_close,
66 	.vop_ioctl	= udf_ioctl,
67 	.vop_read	= udf_read,
68 	.vop_readdir	= udf_readdir,
69 	.vop_readlink	= udf_readlink,
70 	.vop_inactive	= udf_inactive,
71 	.vop_reclaim	= udf_reclaim,
72 	.vop_strategy	= udf_strategy,
73 	.vop_lock	= udf_lock,
74 	.vop_unlock	= udf_unlock,
75 	.vop_islocked	= udf_islocked,
76 	.vop_print	= udf_print
77 };
78 
79 #define UDF_INVALID_BMAP	-1
80 
81 /* Look up a unode based on the ino_t passed in and return its vnode */
82 int
83 udf_hashlookup(struct umount *ump, ino_t id, int flags, struct vnode **vpp)
84 {
85 	struct unode *up;
86 	struct udf_hash_lh *lh;
87 	struct proc *p = curproc;
88 	int error;
89 
90 	*vpp = NULL;
91 
92 loop:
93 	mtx_enter(&ump->um_hashmtx);
94 	lh = &ump->um_hashtbl[id & ump->um_hashsz];
95 	if (lh == NULL) {
96 		mtx_leave(&ump->um_hashmtx);
97 		return (ENOENT);
98 	}
99 
100 	LIST_FOREACH(up, lh, u_le) {
101 		if (up->u_ino == id) {
102 			mtx_leave(&ump->um_hashmtx);
103 			error = vget(up->u_vnode, flags, p);
104 			if (error == ENOENT)
105 				goto loop;
106 			if (error)
107 				return (error);
108 			*vpp = up->u_vnode;
109 			return (0);
110 		}
111 	}
112 
113 	mtx_leave(&ump->um_hashmtx);
114 
115 	return (0);
116 }
117 
118 int
119 udf_hashins(struct unode *up)
120 {
121 	struct umount *ump;
122 	struct udf_hash_lh *lh;
123 	struct proc *p = curproc;
124 
125 	ump = up->u_ump;
126 
127 	vn_lock(up->u_vnode, LK_EXCLUSIVE | LK_RETRY, p);
128 	mtx_enter(&ump->um_hashmtx);
129 	lh = &ump->um_hashtbl[up->u_ino & ump->um_hashsz];
130 	if (lh == NULL)
131 		panic("hash entry is NULL, up->u_ino = %d", up->u_ino);
132 	LIST_INSERT_HEAD(lh, up, u_le);
133 	mtx_leave(&ump->um_hashmtx);
134 
135 	return (0);
136 }
137 
138 int
139 udf_hashrem(struct unode *up)
140 {
141 	struct umount *ump;
142 	struct udf_hash_lh *lh;
143 
144 	ump = up->u_ump;
145 
146 	mtx_enter(&ump->um_hashmtx);
147 	lh = &ump->um_hashtbl[up->u_ino & ump->um_hashsz];
148 	if (lh == NULL)
149 		panic("hash entry is NULL, up->u_ino = %d", up->u_ino);
150 	LIST_REMOVE(up, u_le);
151 	mtx_leave(&ump->um_hashmtx);
152 
153 	return (0);
154 }
155 
156 int
157 udf_allocv(struct mount *mp, struct vnode **vpp, struct proc *p)
158 {
159 	int error;
160 	struct vnode *vp;
161 
162 	error = getnewvnode(VT_UDF, mp, &udf_vops, &vp);
163 	if (error) {
164 		printf("udf_allocv: failed to allocate new vnode\n");
165 		return (error);
166 	}
167 
168 	*vpp = vp;
169 	return (0);
170 }
171 
172 /* Convert file entry permission (5 bits per owner/group/user) to a mode_t */
173 static mode_t
174 udf_permtomode(struct unode *up)
175 {
176 	uint32_t perm;
177 	uint16_t flags;
178 	mode_t mode;
179 
180 	perm = letoh32(up->u_fentry->perm);
181 	flags = letoh16(up->u_fentry->icbtag.flags);
182 
183 	mode = perm & UDF_FENTRY_PERM_USER_MASK;
184 	mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK) >> 2);
185 	mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
186 	mode |= ((flags & UDF_ICB_TAG_FLAGS_STICKY) << 4);
187 	mode |= ((flags & UDF_ICB_TAG_FLAGS_SETGID) << 6);
188 	mode |= ((flags & UDF_ICB_TAG_FLAGS_SETUID) << 8);
189 
190 	return (mode);
191 }
192 
193 int
194 udf_access(void *v)
195 {
196 	struct vop_access_args *ap = v;
197 	struct vnode *vp;
198 	struct unode *up;
199 	mode_t a_mode, mode;
200 
201 	vp = ap->a_vp;
202 	up = VTOU(vp);
203 	a_mode = ap->a_mode;
204 
205 	if (a_mode & VWRITE) {
206 		switch (vp->v_type) {
207 		case VDIR:
208 		case VLNK:
209 		case VREG:
210 			return (EROFS);
211 			/* NOTREACHED */
212 		default:
213 			break;
214 		}
215 	}
216 
217 	mode = udf_permtomode(up);
218 
219 	return (vaccess(vp->v_type, mode, up->u_fentry->uid, up->u_fentry->gid,
220 	    a_mode, ap->a_cred));
221 }
222 
223 static int mon_lens[2][12] = {
224 	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
225 	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
226 };
227 
228 static int
229 udf_isaleapyear(int year)
230 {
231 	int i;
232 
233 	i = (year % 4) ? 0 : 1;
234 	i &= (year % 100) ? 1 : 0;
235 	i |= (year % 400) ? 0 : 1;
236 
237 	return (i);
238 }
239 
240 /*
241  * This is just a rough hack.  Daylight savings isn't calculated and tv_nsec
242  * is ignored.
243  * Timezone calculation compliments of Julian Elischer <julian@elischer.org>.
244  */
245 static void
246 udf_timetotimespec(struct timestamp *time, struct timespec *t)
247 {
248 	int i, lpyear, daysinyear, year;
249 	union {
250 		uint16_t	u_tz_offset;
251 		int16_t		s_tz_offset;
252 	} tz;
253 
254 	t->tv_nsec = 0;
255 
256 	/* DirectCD seems to like using bogus year values */
257 	year = letoh16(time->year);
258 	if (year < 1970) {
259 		t->tv_sec = 0;
260 		return;
261 	}
262 
263 	/* Calculate the time and day */
264 	t->tv_sec = time->second;
265 	t->tv_sec += time->minute * 60;
266 	t->tv_sec += time->hour * 3600;
267 	t->tv_sec += time->day * 3600 * 24;
268 
269 	/* Calculate the month */
270 	lpyear = udf_isaleapyear(year);
271 	for (i = 1; i < time->month; i++)
272 		t->tv_sec += mon_lens[lpyear][i] * 3600 * 24;
273 
274 	/* Speed up the calculation */
275 	if (year > 1979)
276 		t->tv_sec += 315532800;
277 	if (year > 1989)
278 		t->tv_sec += 315619200;
279 	if (year > 1999)
280 		t->tv_sec += 315532800;
281 	for (i = 2000; i < year; i++) {
282 		daysinyear = udf_isaleapyear(i) + 365 ;
283 		t->tv_sec += daysinyear * 3600 * 24;
284 	}
285 
286 	/*
287 	 * Calculate the time zone.  The timezone is 12 bit signed 2's
288 	 * compliment, so we gotta do some extra magic to handle it right.
289 	 */
290 	tz.u_tz_offset = letoh16(time->type_tz);
291 	tz.u_tz_offset &= 0x0fff;
292 	if (tz.u_tz_offset & 0x0800)
293 		tz.u_tz_offset |= 0xf000;	/* extend the sign to 16 bits */
294 	if ((time->type_tz & 0x1000) && (tz.s_tz_offset != -2047))
295 		t->tv_sec -= tz.s_tz_offset * 60;
296 
297 	return;
298 }
299 
300 int
301 udf_getattr(void *v)
302 {
303 	struct vop_getattr_args *ap = v;
304 	struct vnode *vp;
305 	struct unode *up;
306 	struct vattr *vap;
307 	struct extfile_entry *xfentry;
308 	struct file_entry *fentry;
309 	struct timespec ts;
310 
311 	ts.tv_sec = 0;
312 
313 	vp = ap->a_vp;
314 	vap = ap->a_vap;
315 	up = VTOU(vp);
316 
317 	xfentry = up->u_fentry;
318 	fentry = (struct file_entry *)up->u_fentry;
319 
320 	vap->va_fsid = up->u_dev;
321 	vap->va_fileid = up->u_ino;
322 	vap->va_mode = udf_permtomode(up);
323 	vap->va_nlink = letoh16(fentry->link_cnt);
324 	/*
325 	 * The spec says that -1 is valid for uid/gid and indicates an
326 	 * invalid uid/gid.  How should this be represented?
327 	 */
328 	vap->va_uid = (letoh32(fentry->uid) == -1) ? 0 : letoh32(fentry->uid);
329 	vap->va_gid = (letoh32(fentry->gid) == -1) ? 0 : letoh32(fentry->gid);
330 	vap->va_rdev = 0;
331 	if (vp->v_type & VDIR) {
332 		vap->va_nlink++; /* Count a reference to ourselves */
333 		/*
334 		 * Directories that are recorded within their ICB will show
335 		 * as having 0 blocks recorded.  Since tradition dictates
336 		 * that directories consume at least one logical block,
337 		 * make it appear so.
338 		 */
339 		vap->va_size = up->u_ump->um_bsize;
340 	} else
341 		vap->va_size = letoh64(fentry->inf_len);
342 	if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0) {
343 		udf_timetotimespec(&xfentry->atime, &vap->va_atime);
344 		udf_timetotimespec(&xfentry->mtime, &vap->va_mtime);
345 		if ((vp->v_type & VDIR) && xfentry->logblks_rec != 0)
346 			vap->va_size =
347 				    letoh64(xfentry->logblks_rec) * up->u_ump->um_bsize;
348 	} else {
349 		udf_timetotimespec(&fentry->atime, &vap->va_atime);
350 		udf_timetotimespec(&fentry->mtime, &vap->va_mtime);
351 		if ((vp->v_type & VDIR) && fentry->logblks_rec != 0)
352 			vap->va_size =
353 				    letoh64(fentry->logblks_rec) * up->u_ump->um_bsize;
354 	}
355 	vap->va_ctime = vap->va_mtime; /* Stored as an Extended Attribute */
356 	vap->va_flags = 0;
357 	vap->va_gen = 1;
358 	vap->va_blocksize = up->u_ump->um_bsize;
359 	vap->va_bytes = letoh64(fentry->inf_len);
360 	vap->va_type = vp->v_type;
361 	vap->va_filerev = 0;
362 
363 	return (0);
364 }
365 
366 int
367 udf_open(void *v)
368 {
369 	return (0); /* Nothing to be done at this point */
370 }
371 
372 int
373 udf_close(void *v)
374 {
375 	return (0); /* Nothing to be done at this point */
376 }
377 
378 /*
379  * File specific ioctls.
380  */
381 int
382 udf_ioctl(void *v)
383 {
384 	return (ENOTTY);
385 }
386 
387 /*
388  * I'm not sure that this has much value in a read-only filesystem, but
389  * cd9660 has it too.
390  */
391 #if 0
392 static int
393 udf_pathconf(struct vop_pathconf_args *a)
394 {
395 	int error = 0;
396 
397 	switch (ap->a_name) {
398 	case _PC_LINK_MAX:
399 		*ap->a_retval = 65535;
400 		break;
401 	case _PC_NAME_MAX:
402 		*ap->a_retval = NAME_MAX;
403 		break;
404 	case _PC_PATH_MAX:
405 		*ap->a_retval = PATH_MAX;
406 		break;
407 	case _PC_NO_TRUNC:
408 		*ap->a_retval = 1;
409 		break;
410 	default:
411 		error = EINVAL;
412 		break;
413 	}
414 
415 	return (error);
416 }
417 #endif
418 
419 int
420 udf_read(void *v)
421 {
422 	struct vop_read_args *ap = v;
423 	struct vnode *vp = ap->a_vp;
424 	struct uio *uio = ap->a_uio;
425 	struct unode *up = VTOU(vp);
426 	struct buf *bp;
427 	uint8_t *data;
428 	off_t fsize, offset;
429 	int error = 0;
430 	int size;
431 
432 	if (uio->uio_offset < 0)
433 		return (EINVAL);
434 
435 	fsize = letoh64(up->u_fentry->inf_len);
436 
437 	while (uio->uio_offset < fsize && uio->uio_resid > 0) {
438 		offset = uio->uio_offset;
439 		if (uio->uio_resid + offset <= fsize)
440 			size = uio->uio_resid;
441 		else
442 			size = fsize - offset;
443 		error = udf_readatoffset(up, &size, offset, &bp, &data);
444 		if (error == 0)
445 			error = uiomove(data, size, uio);
446 		if (bp != NULL) {
447 			brelse(bp);
448 			bp = NULL;
449 		}
450 		if (error)
451 			break;
452 	};
453 
454 	return (error);
455 }
456 
457 /*
458  * Translate the name from a CS0 dstring to a 16-bit Unicode String.
459  * Hooks need to be placed in here to translate from Unicode to the encoding
460  * that the kernel/user expects.  Return the length of the translated string.
461  */
462 int
463 udf_transname(char *cs0string, char *destname, int len, struct umount *ump)
464 {
465 	unicode_t *transname;
466 	int i, unilen = 0, destlen;
467 
468 	if (len > MAXNAMLEN) {
469 #ifdef DIAGNOSTIC
470 		printf("udf_transname(): name too long\n");
471 #endif
472 		return (0);
473 	}
474 
475 	/* allocate a buffer big enough to hold an 8->16 bit expansion */
476 	transname = pool_get(&udf_trans_pool, PR_WAITOK);
477 
478 	if ((unilen = udf_rawnametounicode(len, cs0string, transname)) == -1) {
479 #ifdef DIAGNOSTIC
480 		printf("udf_transname(): Unicode translation failed\n");
481 #endif
482 		pool_put(&udf_trans_pool, transname);
483 		return (0);
484 	}
485 
486 	/* Pack it back to 8-bit Unicode. */
487 	for (i = 0; i < unilen ; i++)
488 		if (transname[i] & 0xff00)
489 			destname[i] = '?';	/* Fudge the 16bit chars */
490 		else
491 			destname[i] = transname[i] & 0xff;
492 
493 	pool_put(&udf_trans_pool, transname);
494 
495 	/* Don't forget to terminate the string. */
496 	destname[unilen] = 0;
497 	destlen = unilen;
498 
499 	return (destlen);
500 }
501 
502 /*
503  * Compare a CS0 dstring with a name passed in from the VFS layer.  Return
504  * 0 on a successful match, nonzero otherwise.  Unicode work may need to be
505  * done here also.
506  */
507 static int
508 udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen, struct umount *ump)
509 {
510 	char *transname;
511 	int error = 0;
512 
513 	/* This is overkill, but not worth creating a new pool */
514 	transname = pool_get(&udf_trans_pool, PR_WAITOK);
515 
516 	cs0len = udf_transname(cs0string, transname, cs0len, ump);
517 
518 	/* Easy check.  If they aren't the same length, they aren't equal */
519 	if ((cs0len == 0) || (cs0len != cmplen))
520 		error = -1;
521 	else
522 		error = bcmp(transname, cmpname, cmplen);
523 
524 	pool_put(&udf_trans_pool, transname);
525 
526 	return (error);
527 }
528 
529 struct udf_uiodir {
530 	struct dirent *dirent;
531 	u_long *cookies;
532 	int ncookies;
533 	int acookies;
534 	int eofflag;
535 };
536 
537 static int
538 udf_uiodir(struct udf_uiodir *uiodir, int de_size, struct uio *uio, long cookie)
539 {
540 	if (uiodir->cookies != NULL) {
541 		if (++uiodir->acookies > uiodir->ncookies) {
542 			uiodir->eofflag = 0;
543 			return (-1);
544 		}
545 		*uiodir->cookies++ = cookie;
546 	}
547 
548 	if (uio->uio_resid < de_size) {
549 		uiodir->eofflag = 0;
550 		return (-1);
551 	}
552 
553 	return (uiomove(uiodir->dirent, de_size, uio));
554 }
555 
556 static struct udf_dirstream *
557 udf_opendir(struct unode *up, int offset, int fsize, struct umount *ump)
558 {
559 	struct udf_dirstream *ds;
560 
561 	ds = pool_get(&udf_ds_pool, PR_WAITOK | PR_ZERO);
562 
563 	ds->node = up;
564 	ds->offset = offset;
565 	ds->ump = ump;
566 	ds->fsize = fsize;
567 
568 	return (ds);
569 }
570 
571 static struct fileid_desc *
572 udf_getfid(struct udf_dirstream *ds)
573 {
574 	struct fileid_desc *fid;
575 	int error, frag_size = 0, total_fid_size;
576 
577 	/* End of directory? */
578 	if (ds->offset + ds->off >= ds->fsize) {
579 		ds->error = 0;
580 		return (NULL);
581 	}
582 
583 	/* Grab the first extent of the directory */
584 	if (ds->off == 0) {
585 		ds->size = 0;
586 		error = udf_readatoffset(ds->node, &ds->size, ds->offset,
587 		    &ds->bp, &ds->data);
588 		if (error) {
589 			ds->error = error;
590 			if (ds->bp != NULL) {
591 				brelse(ds->bp);
592 				ds->bp = NULL;
593 			}
594 			return (NULL);
595 		}
596 	}
597 
598 	/*
599 	 * Clean up from a previous fragmented FID.
600 	 * Is this the right place for this?
601 	 */
602 	if (ds->fid_fragment && ds->buf != NULL) {
603 		ds->fid_fragment = 0;
604 		free(ds->buf, M_UDFFID);
605 	}
606 
607 	fid = (struct fileid_desc*)&ds->data[ds->off];
608 
609 	/*
610 	 * Check to see if the fid is fragmented. The first test
611 	 * ensures that we don't wander off the end of the buffer
612 	 * looking for the l_iu and l_fi fields.
613 	 */
614 	if (ds->off + UDF_FID_SIZE > ds->size ||
615 	    ds->off + letoh16(fid->l_iu) + fid->l_fi + UDF_FID_SIZE > ds->size){
616 
617 		/* Copy what we have of the fid into a buffer */
618 		frag_size = ds->size - ds->off;
619 		if (frag_size >= ds->ump->um_bsize) {
620 			printf("udf: invalid FID fragment\n");
621 			ds->error = EINVAL;
622 			return (NULL);
623 		}
624 
625 		/*
626 		 * File ID descriptors can only be at most one
627 		 * logical sector in size.
628 		 */
629 		ds->buf = malloc(ds->ump->um_bsize, M_UDFFID, M_WAITOK|M_ZERO);
630 		bcopy(fid, ds->buf, frag_size);
631 
632 		/* Reduce all of the casting magic */
633 		fid = (struct fileid_desc*)ds->buf;
634 
635 		if (ds->bp != NULL) {
636 			brelse(ds->bp);
637 			ds->bp = NULL;
638 		}
639 
640 		/* Fetch the next allocation */
641 		ds->offset += ds->size;
642 		ds->size = 0;
643 		error = udf_readatoffset(ds->node, &ds->size, ds->offset,
644 		    &ds->bp, &ds->data);
645 		if (error) {
646 			ds->error = error;
647 			if (ds->bp != NULL) {
648 				brelse(ds->bp);
649 				ds->bp = NULL;
650 			}
651 			return (NULL);
652 		}
653 
654 		/*
655 		 * If the fragment was so small that we didn't get
656 		 * the l_iu and l_fi fields, copy those in.
657 		 */
658 		if (frag_size < UDF_FID_SIZE)
659 			bcopy(ds->data, &ds->buf[frag_size],
660 			    UDF_FID_SIZE - frag_size);
661 
662 		/*
663 		 * Now that we have enough of the fid to work with,
664 		 * copy in the rest of the fid from the new
665 		 * allocation.
666 		 */
667 		total_fid_size = UDF_FID_SIZE + letoh16(fid->l_iu) + fid->l_fi;
668 		if (total_fid_size > ds->ump->um_bsize) {
669 			printf("udf: invalid FID\n");
670 			ds->error = EIO;
671 			return (NULL);
672 		}
673 		bcopy(ds->data, &ds->buf[frag_size],
674 		    total_fid_size - frag_size);
675 
676 		ds->fid_fragment = 1;
677 	} else {
678 		total_fid_size = letoh16(fid->l_iu) + fid->l_fi + UDF_FID_SIZE;
679 	}
680 
681 	/*
682 	 * Update the offset. Align on a 4 byte boundary because the
683 	 * UDF spec says so.
684 	 */
685 	ds->this_off = ds->off;
686 	if (!ds->fid_fragment) {
687 		ds->off += (total_fid_size + 3) & ~0x03;
688 	} else {
689 		ds->off = (total_fid_size - frag_size + 3) & ~0x03;
690 	}
691 
692 	return (fid);
693 }
694 
695 static void
696 udf_closedir(struct udf_dirstream *ds)
697 {
698 
699 	if (ds->bp != NULL) {
700 		brelse(ds->bp);
701 		ds->bp = NULL;
702 	}
703 
704 	if (ds->fid_fragment && ds->buf != NULL)
705 		free(ds->buf, M_UDFFID);
706 
707 	pool_put(&udf_ds_pool, ds);
708 }
709 
710 int
711 udf_readdir(void *v)
712 {
713 	struct vop_readdir_args *ap = v;
714 	struct vnode *vp;
715 	struct uio *uio;
716 	struct dirent dir;
717 	struct unode *up;
718 	struct umount *ump;
719 	struct fileid_desc *fid;
720 	struct udf_uiodir uiodir;
721 	struct udf_dirstream *ds;
722 	u_long *cookies = NULL;
723 	int ncookies;
724 	int error = 0;
725 
726 #define GENERIC_DIRSIZ(dp) \
727     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
728 
729 	vp = ap->a_vp;
730 	uio = ap->a_uio;
731 	up = VTOU(vp);
732 	ump = up->u_ump;
733 	uiodir.eofflag = 1;
734 
735 	if (ap->a_ncookies != NULL) {
736 		/*
737 		 * Guess how many entries are needed.  If we run out, this
738 		 * function will be called again and thing will pick up were
739 		 * it left off.
740 		 */
741 		ncookies = uio->uio_resid / 8;
742 		cookies = malloc(sizeof(u_long) * ncookies, M_TEMP, M_WAITOK);
743 		uiodir.ncookies = ncookies;
744 		uiodir.cookies = cookies;
745 		uiodir.acookies = 0;
746 	} else {
747 		uiodir.cookies = NULL;
748 	}
749 
750 	/*
751 	 * Iterate through the file id descriptors.  Give the parent dir
752 	 * entry special attention.
753 	 */
754 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
755 		up->u_ump->um_start += up->u_ump->um_meta_start;
756 		up->u_ump->um_len = up->u_ump->um_meta_len;
757 	}
758 	ds = udf_opendir(up, uio->uio_offset,
759 	    letoh64(up->u_fentry->inf_len), up->u_ump);
760 
761 	while ((fid = udf_getfid(ds)) != NULL) {
762 
763 		/* Should we return an error on a bad fid? */
764 		if (udf_checktag(&fid->tag, TAGID_FID)) {
765 			printf("Invalid FID tag (%d)\n", fid->tag.id);
766 			error = EIO;
767 			break;
768 		}
769 
770 		/* Is this a deleted file? */
771 		if (fid->file_char & UDF_FILE_CHAR_DEL)
772 			continue;
773 
774 		if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
775 			/* Do up the '.' and '..' entries.  Dummy values are
776 			 * used for the cookies since the offset here is
777 			 * usually zero, and NFS doesn't like that value
778 			 */
779 			dir.d_fileno = up->u_ino;
780 			dir.d_type = DT_DIR;
781 			dir.d_name[0] = '.';
782 			dir.d_name[1] = '\0';
783 			dir.d_namlen = 1;
784 			dir.d_reclen = GENERIC_DIRSIZ(&dir);
785 			uiodir.dirent = &dir;
786 			error = udf_uiodir(&uiodir, dir.d_reclen, uio, 1);
787 			if (error)
788 				break;
789 
790 			dir.d_fileno = udf_getid(&fid->icb);
791 			dir.d_type = DT_DIR;
792 			dir.d_name[0] = '.';
793 			dir.d_name[1] = '.';
794 			dir.d_name[2] = '\0';
795 			dir.d_namlen = 2;
796 			dir.d_reclen = GENERIC_DIRSIZ(&dir);
797 			uiodir.dirent = &dir;
798 			error = udf_uiodir(&uiodir, dir.d_reclen, uio, 2);
799 		} else {
800 			dir.d_namlen = udf_transname(&fid->data[fid->l_iu],
801 			    &dir.d_name[0], fid->l_fi, ump);
802 			dir.d_fileno = udf_getid(&fid->icb);
803 			dir.d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ?
804 			    DT_DIR : DT_UNKNOWN;
805 			dir.d_reclen = GENERIC_DIRSIZ(&dir);
806 			uiodir.dirent = &dir;
807 			error = udf_uiodir(&uiodir, dir.d_reclen, uio,
808 			    ds->this_off);
809 		}
810 		if (error) {
811 			printf("uiomove returned %d\n", error);
812 			break;
813 		}
814 
815 	}
816 
817 #undef GENERIC_DIRSIZ
818 
819 	/* tell the calling layer whether we need to be called again */
820 	*ap->a_eofflag = uiodir.eofflag;
821 	uio->uio_offset = ds->offset + ds->off;
822 
823 	if (!error)
824 		error = ds->error;
825 
826 	udf_closedir(ds);
827 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
828 		up->u_ump->um_start = up->u_ump->um_realstart;
829 		up->u_ump->um_len = up->u_ump->um_reallen;
830 	}
831 
832 	if (ap->a_ncookies != NULL) {
833 		if (error)
834 			free(cookies, M_TEMP);
835 		else {
836 			*ap->a_ncookies = uiodir.acookies;
837 			*ap->a_cookies = cookies;
838 		}
839 	}
840 
841 	return (error);
842 }
843 
844 /* Are there any implementations out there that do soft-links? */
845 int
846 udf_readlink(void *v)
847 {
848 	return (EOPNOTSUPP);
849 }
850 
851 int
852 udf_strategy(void *v)
853 {
854 	struct vop_strategy_args *ap = v;
855 	struct buf *bp;
856 	struct vnode *vp;
857 	struct unode *up;
858 	int maxsize, s, error;
859 
860 	bp = ap->a_bp;
861 	vp = bp->b_vp;
862 	up = VTOU(vp);
863 
864 	/* cd9660 has this test reversed, but it seems more logical this way */
865 	if (bp->b_blkno != bp->b_lblkno) {
866 		/*
867 		 * Files that are embedded in the fentry don't translate well
868 		 * to a block number.  Reject.
869 		 */
870 		if (udf_bmap_internal(up, bp->b_lblkno * up->u_ump->um_bsize,
871 		    &bp->b_lblkno, &maxsize)) {
872 			clrbuf(bp);
873 			bp->b_blkno = -1;
874 		}
875 	} else {
876 		error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
877 		if (error) {
878 			bp->b_error = error;
879 			bp->b_flags |= B_ERROR;
880 			s = splbio();
881 			biodone(bp);
882 			splx(s);
883 			return (error);
884 		}
885 
886 		if ((long)bp->b_blkno == -1)
887 			clrbuf(bp);
888 	}
889 
890 	if ((long)bp->b_blkno == -1) {
891 		s = splbio();
892 		biodone(bp);
893 		splx(s);
894 	} else {
895 		bp->b_dev = vp->v_rdev;
896 		(up->u_devvp->v_op->vop_strategy)(ap);
897 	}
898 
899 	return (0);
900 }
901 
902 int
903 udf_lock(void *v)
904 {
905 	struct vop_lock_args *ap = v;
906 
907 	struct vnode *vp = ap->a_vp;
908 
909 	return (lockmgr(&VTOU(vp)->u_lock, ap->a_flags, NULL));
910 }
911 
912 int
913 udf_unlock(void *v)
914 {
915 	struct vop_unlock_args *ap = v;
916 
917 	struct vnode *vp = ap->a_vp;
918 
919 	return (lockmgr(&VTOU(vp)->u_lock, ap->a_flags | LK_RELEASE, NULL));
920 }
921 
922 int
923 udf_islocked(void *v)
924 {
925 	struct vop_islocked_args *ap = v;
926 
927 	return (lockstatus(&VTOU(ap->a_vp)->u_lock));
928 }
929 
930 int
931 udf_print(void *v)
932 {
933 	struct vop_print_args *ap = v;
934 	struct vnode *vp = ap->a_vp;
935 	struct unode *up = VTOU(vp);
936 
937 	/*
938 	 * Complete the information given by vprint().
939 	 */
940 	printf("tag VT_UDF, hash id %u\n", up->u_ino);
941 #ifdef DIAGNOSTIC
942 	lockmgr_printinfo(&up->u_lock);
943 	printf("\n");
944 #endif
945 	return (0);
946 }
947 
948 int
949 udf_bmap(void *v)
950 {
951 	struct vop_bmap_args *ap = v;
952 	struct unode *up;
953 	uint32_t max_size;
954 	daddr64_t lsector;
955 	int error;
956 
957 	up = VTOU(ap->a_vp);
958 
959 	if (ap->a_vpp != NULL)
960 		*ap->a_vpp = up->u_devvp;
961 	if (ap->a_bnp == NULL)
962 		return (0);
963 
964 	error = udf_bmap_internal(up, ap->a_bn * up->u_ump->um_bsize,
965 	    &lsector, &max_size);
966 	if (error)
967 		return (error);
968 
969 	/* Translate logical to physical sector number */
970 	*ap->a_bnp = lsector << (up->u_ump->um_bshift - DEV_BSHIFT);
971 
972 	/* Punt on read-ahead for now */
973 	if (ap->a_runp)
974 		*ap->a_runp = 0;
975 
976 	return (0);
977 }
978 
979 /*
980  * The all powerful VOP_LOOKUP().
981  */
982 int
983 udf_lookup(void *v)
984 {
985 	struct vop_lookup_args *ap = v;
986 	struct vnode *dvp;
987 	struct vnode *tdp = NULL;
988 	struct vnode **vpp = ap->a_vpp;
989 	struct unode *up;
990 	struct umount *ump;
991 	struct fileid_desc *fid = NULL;
992 	struct udf_dirstream *ds;
993 	struct proc *p;
994 	u_long nameiop;
995 	u_long flags;
996 	char *nameptr;
997 	long namelen;
998 	ino_t id = 0;
999 	int offset, error = 0;
1000 	int numdirpasses, fsize;
1001 
1002 	extern struct nchstats nchstats;
1003 
1004 	dvp = ap->a_dvp;
1005 	up = VTOU(dvp);
1006 	ump = up->u_ump;
1007 	nameiop = ap->a_cnp->cn_nameiop;
1008 	flags = ap->a_cnp->cn_flags;
1009 	nameptr = ap->a_cnp->cn_nameptr;
1010 	namelen = ap->a_cnp->cn_namelen;
1011 	fsize = letoh64(up->u_fentry->inf_len);
1012 	p = ap->a_cnp->cn_proc;
1013 	*vpp = NULL;
1014 
1015 	/*
1016 	 * Make sure the process can scan the requested directory.
1017 	 */
1018 	error = VOP_ACCESS(dvp, VEXEC, ap->a_cnp->cn_cred, p);
1019 	if (error)
1020 		return (error);
1021 
1022 	/*
1023 	 * Check if the (directory, name) tuple has been already cached.
1024 	 */
1025 	error = cache_lookup(dvp, vpp, ap->a_cnp);
1026 	if (error >= 0)
1027 		return (error);
1028 	else
1029 		error = 0;
1030 
1031 	/*
1032 	 * If dvp is what's being looked up, then return it.
1033 	 */
1034 	if (ap->a_cnp->cn_namelen == 1 && ap->a_cnp->cn_nameptr[0] == '.') {
1035 		vref(dvp);
1036 		*vpp = dvp;
1037 		return (0);
1038 	}
1039 
1040 	/*
1041 	 * If this is a LOOKUP and we've already partially searched through
1042 	 * the directory, pick up where we left off and flag that the
1043 	 * directory may need to be searched twice.  For a full description,
1044 	 * see /sys/isofs/cd9660/cd9660_lookup.c:cd9660_lookup()
1045 	 */
1046 	if (nameiop != LOOKUP || up->u_diroff == 0 || up->u_diroff > fsize) {
1047 		offset = 0;
1048 		numdirpasses = 1;
1049 	} else {
1050 		offset = up->u_diroff;
1051 		numdirpasses = 2;
1052 		nchstats.ncs_2passes++;
1053 	}
1054 
1055 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1056 		up->u_ump->um_start += up->u_ump->um_meta_start;
1057 		up->u_ump->um_len = up->u_ump->um_meta_len;
1058 	}
1059 lookloop:
1060 	ds = udf_opendir(up, offset, fsize, ump);
1061 
1062 	while ((fid = udf_getfid(ds)) != NULL) {
1063 		/* Check for a valid FID tag. */
1064 		if (udf_checktag(&fid->tag, TAGID_FID)) {
1065 			printf("udf_lookup: Invalid tag\n");
1066 			error = EIO;
1067 			break;
1068 		}
1069 
1070 		/* Is this a deleted file? */
1071 		if (fid->file_char & UDF_FILE_CHAR_DEL)
1072 			continue;
1073 
1074 		if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
1075 			if (flags & ISDOTDOT) {
1076 				id = udf_getid(&fid->icb);
1077 				break;
1078 			}
1079 		} else {
1080 			if (!(udf_cmpname(&fid->data[fid->l_iu],
1081 			    nameptr, fid->l_fi, namelen, ump))) {
1082 				id = udf_getid(&fid->icb);
1083 				break;
1084 			}
1085 		}
1086 	}
1087 
1088 	if (!error)
1089 		error = ds->error;
1090 
1091 	if (error) {
1092 		udf_closedir(ds);
1093 		if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1094 			up->u_ump->um_start = up->u_ump->um_realstart;
1095 			up->u_ump->um_len = up->u_ump->um_reallen;
1096 		}
1097 		return (error);
1098 	}
1099 
1100 	/* Did we have a match? */
1101 	if (id) {
1102 		error = udf_vget(ump->um_mountp, id, &tdp);
1103 		if (!error) {
1104 			/*
1105 			 * Remember where this entry was if it's the final
1106 			 * component.
1107 			 */
1108 			if ((flags & ISLASTCN) && nameiop == LOOKUP)
1109 				up->u_diroff = ds->offset + ds->off;
1110 			if (numdirpasses == 2)
1111 				nchstats.ncs_pass2++;
1112 			if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
1113 				ap->a_cnp->cn_flags |= PDIRUNLOCK;
1114 				VOP_UNLOCK(dvp, 0, p);
1115 			}
1116 
1117 			*vpp = tdp;
1118 		}
1119 	} else {
1120 		/* Name wasn't found on this pass.  Do another pass? */
1121 		if (numdirpasses == 2) {
1122 			numdirpasses--;
1123 			offset = 0;
1124 			udf_closedir(ds);
1125 			goto lookloop;
1126 		}
1127 
1128 		if ((flags & ISLASTCN) &&
1129 		    (nameiop == CREATE || nameiop == RENAME)) {
1130 			error = EROFS;
1131 		} else {
1132 			error = ENOENT;
1133 		}
1134 	}
1135 
1136 	/*
1137 	 * Cache the result of this lookup.
1138 	 */
1139 	if (flags & MAKEENTRY)
1140 		cache_enter(dvp, *vpp, ap->a_cnp);
1141 
1142 	udf_closedir(ds);
1143 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1144 		up->u_ump->um_start = up->u_ump->um_realstart;
1145 		up->u_ump->um_len = up->u_ump->um_reallen;
1146 	}
1147 
1148 	return (error);
1149 }
1150 
1151 int
1152 udf_inactive(void *v)
1153 {
1154 	struct vop_inactive_args *ap = v;
1155 	struct vnode *vp = ap->a_vp;
1156 	struct proc *p = ap->a_p;
1157 
1158 	/*
1159 	 * No need to sync anything, so just unlock the vnode and return.
1160 	 */
1161 	VOP_UNLOCK(vp, 0, p);
1162 
1163 	return (0);
1164 }
1165 
1166 int
1167 udf_reclaim(void *v)
1168 {
1169 	struct vop_reclaim_args *ap = v;
1170 	struct vnode *vp;
1171 	struct unode *up;
1172 
1173 	vp = ap->a_vp;
1174 	up = VTOU(vp);
1175 
1176 	if (up != NULL) {
1177 		udf_hashrem(up);
1178 		if (up->u_devvp) {
1179 			vrele(up->u_devvp);
1180 			up->u_devvp = 0;
1181 		}
1182 
1183 		if (up->u_fentry != NULL)
1184 			free(up->u_fentry, M_UDFFENTRY);
1185 
1186 		pool_put(&unode_pool, up);
1187 		vp->v_data = NULL;
1188 	}
1189 
1190 	return (0);
1191 }
1192 
1193 /*
1194  * Read the block and then set the data pointer to correspond with the
1195  * offset passed in.  Only read in at most 'size' bytes, and then set 'size'
1196  * to the number of bytes pointed to.  If 'size' is zero, try to read in a
1197  * whole extent.
1198  *
1199  * Note that *bp may be assigned error or not.
1200  *
1201  */
1202 int
1203 udf_readatoffset(struct unode *up, int *size, off_t offset,
1204     struct buf **bp, uint8_t **data)
1205 {
1206 	struct umount *ump;
1207 	struct extfile_entry *xfentry = NULL;
1208 	struct file_entry *fentry = NULL;
1209 	struct buf *bp1;
1210 	uint32_t max_size;
1211 	daddr64_t sector;
1212 	int error;
1213 
1214 	ump = up->u_ump;
1215 
1216 	*bp = NULL;
1217 	error = udf_bmap_internal(up, offset, &sector, &max_size);
1218 	if (error == UDF_INVALID_BMAP) {
1219 		/*
1220 		 * This error means that the file *data* is stored in the
1221 		 * allocation descriptor field of the file entry.
1222 		 */
1223 		if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0) {
1224 			xfentry = up->u_fentry;
1225 			*data = &xfentry->data[letoh32(xfentry->l_ea)];
1226 			*size = letoh32(xfentry->l_ad);
1227 		} else {
1228 			fentry = (struct file_entry *)up->u_fentry;
1229 			*data = &fentry->data[letoh32(fentry->l_ea)];
1230 			*size = letoh32(fentry->l_ad);
1231 		}
1232 		return (0);
1233 	} else if (error != 0) {
1234 		return (error);
1235 	}
1236 
1237 	/* Adjust the size so that it is within range */
1238 	if (*size == 0 || *size > max_size)
1239 		*size = max_size;
1240 	*size = min(*size, MAXBSIZE);
1241 
1242 	if ((error = udf_readlblks(ump, sector, *size, bp))) {
1243 		printf("warning: udf_readlblks returned error %d\n", error);
1244 		/* note: *bp may be non-NULL */
1245 		return (error);
1246 	}
1247 
1248 	bp1 = *bp;
1249 	*data = (uint8_t *)&bp1->b_data[offset % ump->um_bsize];
1250 	return (0);
1251 }
1252 
1253 /*
1254  * Translate a file offset into a logical block and then into a physical
1255  * block.
1256  */
1257 int
1258 udf_bmap_internal(struct unode *up, off_t offset, daddr64_t *sector,
1259     uint32_t *max_size)
1260 {
1261 	struct umount *ump;
1262 	struct extfile_entry *xfentry;
1263 	struct file_entry *fentry;
1264 	void *icb;
1265 	struct icb_tag *tag;
1266 	uint32_t icblen = 0;
1267 	daddr64_t lsector;
1268 	int ad_offset, ad_num = 0;
1269 	int i, p_offset, l_ea, l_ad;
1270 
1271 	ump = up->u_ump;
1272 	xfentry = up->u_fentry;
1273 	fentry = (struct file_entry *)up->u_fentry;
1274 	tag = &fentry->icbtag;
1275 	if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0) {
1276 		l_ea = letoh32(xfentry->l_ea);
1277 		l_ad = letoh32(xfentry->l_ad);
1278 	} else {
1279 		l_ea = letoh32(fentry->l_ea);
1280 		l_ad = letoh32(fentry->l_ad);
1281 	}
1282 
1283 	switch (letoh16(tag->strat_type)) {
1284 	case 4:
1285 		break;
1286 
1287 	case 4096:
1288 		printf("Cannot deal with strategy4096 yet!\n");
1289 		return (ENODEV);
1290 
1291 	default:
1292 		printf("Unknown strategy type %d\n", tag->strat_type);
1293 		return (ENODEV);
1294 	}
1295 
1296 	switch (letoh16(tag->flags) & 0x7) {
1297 	case 0:
1298 		/*
1299 		 * The allocation descriptor field is filled with short_ad's.
1300 		 * If the offset is beyond the current extent, look for the
1301 		 * next extent.
1302 		 */
1303 		do {
1304 			offset -= icblen;
1305 			ad_offset = sizeof(struct short_ad) * ad_num;
1306 			if (ad_offset > l_ad) {
1307 				printf("SFile offset out of bounds (%d > %d)\n",
1308 				    ad_offset, l_ad);
1309 				return (EINVAL);
1310 			}
1311 
1312 			if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0)
1313 				icb = GETICB(short_ad, xfentry, l_ea + ad_offset);
1314 			else
1315 				icb = GETICB(short_ad, fentry, l_ea + ad_offset);
1316 
1317 			icblen = GETICBLEN(short_ad, icb);
1318 			ad_num++;
1319 		} while(offset >= icblen);
1320 
1321 		lsector = (offset  >> ump->um_bshift) +
1322 		    letoh32(((struct short_ad *)(icb))->lb_num);
1323 
1324 		*max_size = GETICBLEN(short_ad, icb);
1325 
1326 		break;
1327 	case 1:
1328 		/*
1329 		 * The allocation descriptor field is filled with long_ad's
1330 		 * If the offset is beyond the current extent, look for the
1331 		 * next extent.
1332 		 */
1333 		do {
1334 			offset -= icblen;
1335 			ad_offset = sizeof(struct long_ad) * ad_num;
1336 			if (ad_offset > l_ad) {
1337 				printf("LFile offset out of bounds (%d > %d)\n",
1338 				    ad_offset, l_ad);
1339 				return (EINVAL);
1340 			}
1341 			if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0)
1342 				icb = GETICB(long_ad, xfentry, l_ea + ad_offset);
1343 			else
1344 				icb = GETICB(long_ad, fentry, l_ea + ad_offset);
1345 			icblen = GETICBLEN(long_ad, icb);
1346 			ad_num++;
1347 		} while(offset >= icblen);
1348 
1349 		lsector = (offset >> ump->um_bshift) +
1350 		    letoh32(((struct long_ad *)(icb))->loc.lb_num);
1351 
1352 		*max_size = GETICBLEN(long_ad, icb);
1353 
1354 		break;
1355 	case 3:
1356 		/*
1357 		 * This type means that the file *data* is stored in the
1358 		 * allocation descriptor field of the file entry.
1359 		 */
1360 		*max_size = 0;
1361 		*sector = up->u_ino + ump->um_start;
1362 
1363 		return (UDF_INVALID_BMAP);
1364 	case 2:
1365 		/* DirectCD does not use extended_ad's */
1366 	default:
1367 		printf("Unsupported allocation descriptor %d\n",
1368 		       tag->flags & 0x7);
1369 		return (ENODEV);
1370 	}
1371 
1372 	*sector = lsector + ump->um_start;
1373 
1374 	/*
1375 	 * Check the sparing table.  Each entry represents the beginning of
1376 	 * a packet.
1377 	 */
1378 	if (ump->um_stbl != NULL) {
1379 		for (i = 0; i< ump->um_stbl_len; i++) {
1380 			p_offset =
1381 			    lsector - letoh32(ump->um_stbl->entries[i].org);
1382 			if ((p_offset < ump->um_psecs) && (p_offset >= 0)) {
1383 				*sector =
1384 				   letoh32(ump->um_stbl->entries[i].map) +
1385 				    p_offset;
1386 				break;
1387 			}
1388 		}
1389 	}
1390 
1391 	return (0);
1392 }
1393