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