xref: /openbsd-src/sys/isofs/udf/udf_vnops.c (revision 5a0ec8146b3a8f74af8f596985d293fb896d1dcb)
1*5a0ec814Smiod /*	$OpenBSD: udf_vnops.c,v 1.75 2024/10/18 05:52:32 miod Exp $	*/
2d9ac8608Spedro 
3d9ac8608Spedro /*
4d9ac8608Spedro  * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
5d9ac8608Spedro  * All rights reserved.
6d9ac8608Spedro  *
7d9ac8608Spedro  * Redistribution and use in source and binary forms, with or without
8d9ac8608Spedro  * modification, are permitted provided that the following conditions
9d9ac8608Spedro  * are met:
10d9ac8608Spedro  * 1. Redistributions of source code must retain the above copyright
11d9ac8608Spedro  *    notice, this list of conditions and the following disclaimer.
12d9ac8608Spedro  * 2. Redistributions in binary form must reproduce the above copyright
13d9ac8608Spedro  *    notice, this list of conditions and the following disclaimer in the
14d9ac8608Spedro  *    documentation and/or other materials provided with the distribution.
15d9ac8608Spedro  *
16d9ac8608Spedro  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17d9ac8608Spedro  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d9ac8608Spedro  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d9ac8608Spedro  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20d9ac8608Spedro  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d9ac8608Spedro  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d9ac8608Spedro  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d9ac8608Spedro  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d9ac8608Spedro  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d9ac8608Spedro  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d9ac8608Spedro  * SUCH DAMAGE.
27d9ac8608Spedro  *
28d9ac8608Spedro  * $FreeBSD: src/sys/fs/udf/udf_vnops.c,v 1.50 2005/01/28 14:42:16 phk Exp $
29d9ac8608Spedro  */
30d9ac8608Spedro 
31d9ac8608Spedro /*
32fcbf5195Sjmc  * Ported to OpenBSD by Pedro Martelletto in February 2005.
33d9ac8608Spedro  */
34d9ac8608Spedro 
35d9ac8608Spedro #include <sys/param.h>
36d9ac8608Spedro #include <sys/systm.h>
37d9ac8608Spedro #include <sys/namei.h>
38d9ac8608Spedro #include <sys/malloc.h>
39d9ac8608Spedro #include <sys/mutex.h>
40d9ac8608Spedro #include <sys/stat.h>
41d9ac8608Spedro #include <sys/buf.h>
42d9ac8608Spedro #include <sys/pool.h>
43d9ac8608Spedro #include <sys/lock.h>
44d9ac8608Spedro #include <sys/mount.h>
45d9ac8608Spedro #include <sys/vnode.h>
46d9ac8608Spedro #include <sys/dirent.h>
47d9ac8608Spedro #include <sys/queue.h>
48d9ac8608Spedro #include <sys/endian.h>
49544451c3Sderaadt #include <sys/specdev.h>
50782ebdf8Stedu #include <sys/unistd.h>
51d9ac8608Spedro 
529dc9bb81Sdlg #include <crypto/siphash.h>
539dc9bb81Sdlg 
54d9ac8608Spedro #include <isofs/udf/ecma167-udf.h>
55d9ac8608Spedro #include <isofs/udf/udf.h>
56d9ac8608Spedro #include <isofs/udf/udf_extern.h>
57d9ac8608Spedro 
581abdbfdeSderaadt int udf_bmap_internal(struct unode *, off_t, daddr_t *, uint32_t *);
59d9ac8608Spedro 
602d6b9e38Sclaudio const struct vops udf_vops = {
61dc81e71aSthib 	.vop_access	= udf_access,
62dc81e71aSthib 	.vop_bmap	= udf_bmap,
63dc81e71aSthib 	.vop_lookup	= udf_lookup,
64dc81e71aSthib 	.vop_getattr	= udf_getattr,
65dc81e71aSthib 	.vop_open	= udf_open,
66dc81e71aSthib 	.vop_close	= udf_close,
67dc81e71aSthib 	.vop_ioctl	= udf_ioctl,
68dc81e71aSthib 	.vop_read	= udf_read,
69dc81e71aSthib 	.vop_readdir	= udf_readdir,
70dc81e71aSthib 	.vop_readlink	= udf_readlink,
71dc81e71aSthib 	.vop_inactive	= udf_inactive,
72dc81e71aSthib 	.vop_reclaim	= udf_reclaim,
73dc81e71aSthib 	.vop_strategy	= udf_strategy,
74dc81e71aSthib 	.vop_lock	= udf_lock,
75dc81e71aSthib 	.vop_unlock	= udf_unlock,
76bb394a57Sderaadt 	.vop_pathconf	= udf_pathconf,
77dc81e71aSthib 	.vop_islocked	= udf_islocked,
786e85720aSsemarie 	.vop_print	= udf_print,
796e85720aSsemarie 
806e85720aSsemarie 	.vop_abortop	= NULL,
816e85720aSsemarie 	.vop_advlock	= NULL,
826e85720aSsemarie 	.vop_bwrite	= NULL,
836e85720aSsemarie 	.vop_create	= NULL,
846e85720aSsemarie 	.vop_fsync	= NULL,
856e85720aSsemarie 	.vop_link	= NULL,
866e85720aSsemarie 	.vop_mknod	= NULL,
87aa47c49aSsemarie 	.vop_remove	= eopnotsupp,
886e85720aSsemarie 	.vop_rename	= NULL,
896e85720aSsemarie 	.vop_revoke	= NULL,
906e85720aSsemarie 	.vop_mkdir	= NULL,
916e85720aSsemarie 	.vop_rmdir	= NULL,
926e85720aSsemarie 	.vop_setattr	= NULL,
936e85720aSsemarie 	.vop_symlink	= NULL,
946e85720aSsemarie 	.vop_write	= NULL,
956e85720aSsemarie 	.vop_kqfilter	= NULL
96d9ac8608Spedro };
97d9ac8608Spedro 
98d9ac8608Spedro #define UDF_INVALID_BMAP	-1
99d9ac8608Spedro 
1000cad8b22Sguenther /* Look up a unode based on the udfino_t passed in and return its vnode */
101d9ac8608Spedro int
1020cad8b22Sguenther udf_hashlookup(struct umount *ump, udfino_t id, int flags, struct vnode **vpp)
103d9ac8608Spedro {
1041af96cccSpedro 	struct unode *up;
105d9ac8608Spedro 	struct udf_hash_lh *lh;
106d9ac8608Spedro 	int error;
107d9ac8608Spedro 
108d9ac8608Spedro 	*vpp = NULL;
109d9ac8608Spedro 
110d9ac8608Spedro loop:
111170e22feSpedro 	mtx_enter(&ump->um_hashmtx);
1129dc9bb81Sdlg 	lh = &ump->um_hashtbl[SipHash24(&ump->um_hashkey, &id, sizeof(id)) &
1139dc9bb81Sdlg 	    ump->um_hashsz];
114fc8d56caSpat 	if (lh == NULL) {
115170e22feSpedro 		mtx_leave(&ump->um_hashmtx);
116d9ac8608Spedro 		return (ENOENT);
117fc8d56caSpat 	}
118fc8d56caSpat 
1191af96cccSpedro 	LIST_FOREACH(up, lh, u_le) {
1201af96cccSpedro 		if (up->u_ino == id) {
121170e22feSpedro 			mtx_leave(&ump->um_hashmtx);
12208107a0bSvisa 			error = vget(up->u_vnode, flags);
123d9ac8608Spedro 			if (error == ENOENT)
124d9ac8608Spedro 				goto loop;
125d9ac8608Spedro 			if (error)
126d9ac8608Spedro 				return (error);
1271af96cccSpedro 			*vpp = up->u_vnode;
128d9ac8608Spedro 			return (0);
129d9ac8608Spedro 		}
130d9ac8608Spedro 	}
131d9ac8608Spedro 
132170e22feSpedro 	mtx_leave(&ump->um_hashmtx);
133d9ac8608Spedro 
134d9ac8608Spedro 	return (0);
135d9ac8608Spedro }
136d9ac8608Spedro 
137d9ac8608Spedro int
1381af96cccSpedro udf_hashins(struct unode *up)
139d9ac8608Spedro {
140170e22feSpedro 	struct umount *ump;
141d9ac8608Spedro 	struct udf_hash_lh *lh;
142d9ac8608Spedro 
143170e22feSpedro 	ump = up->u_ump;
144d9ac8608Spedro 
1456e880534Svisa 	vn_lock(up->u_vnode, LK_EXCLUSIVE | LK_RETRY);
146170e22feSpedro 	mtx_enter(&ump->um_hashmtx);
1479dc9bb81Sdlg 	lh = &ump->um_hashtbl[SipHash24(&ump->um_hashkey,
1489dc9bb81Sdlg 	    &up->u_ino, sizeof(up->u_ino)) & ump->um_hashsz];
149d9ac8608Spedro 	if (lh == NULL)
15099b3a160Schl 		panic("hash entry is NULL, up->u_ino = %d", up->u_ino);
1511af96cccSpedro 	LIST_INSERT_HEAD(lh, up, u_le);
152170e22feSpedro 	mtx_leave(&ump->um_hashmtx);
153d9ac8608Spedro 
154d9ac8608Spedro 	return (0);
155d9ac8608Spedro }
156d9ac8608Spedro 
157d9ac8608Spedro int
1581af96cccSpedro udf_hashrem(struct unode *up)
159d9ac8608Spedro {
160170e22feSpedro 	struct umount *ump;
161d9ac8608Spedro 	struct udf_hash_lh *lh;
162d9ac8608Spedro 
163170e22feSpedro 	ump = up->u_ump;
164d9ac8608Spedro 
165170e22feSpedro 	mtx_enter(&ump->um_hashmtx);
1669dc9bb81Sdlg 	lh = &ump->um_hashtbl[SipHash24(&ump->um_hashkey,
1679dc9bb81Sdlg 	    &up->u_ino, sizeof(up->u_ino)) & ump->um_hashsz];
168d9ac8608Spedro 	if (lh == NULL)
1691af96cccSpedro 		panic("hash entry is NULL, up->u_ino = %d", up->u_ino);
1701af96cccSpedro 	LIST_REMOVE(up, u_le);
171170e22feSpedro 	mtx_leave(&ump->um_hashmtx);
172d9ac8608Spedro 
173d9ac8608Spedro 	return (0);
174d9ac8608Spedro }
175d9ac8608Spedro 
176d9ac8608Spedro int
177d9ac8608Spedro udf_allocv(struct mount *mp, struct vnode **vpp, struct proc *p)
178d9ac8608Spedro {
179d9ac8608Spedro 	int error;
180d9ac8608Spedro 	struct vnode *vp;
181d9ac8608Spedro 
182dc81e71aSthib 	error = getnewvnode(VT_UDF, mp, &udf_vops, &vp);
183d9ac8608Spedro 	if (error) {
184d9ac8608Spedro 		printf("udf_allocv: failed to allocate new vnode\n");
185d9ac8608Spedro 		return (error);
186d9ac8608Spedro 	}
187d9ac8608Spedro 
188d9ac8608Spedro 	*vpp = vp;
189d9ac8608Spedro 	return (0);
190d9ac8608Spedro }
191d9ac8608Spedro 
192d9ac8608Spedro /* Convert file entry permission (5 bits per owner/group/user) to a mode_t */
193d9ac8608Spedro static mode_t
1941af96cccSpedro udf_permtomode(struct unode *up)
195d9ac8608Spedro {
196d9ac8608Spedro 	uint32_t perm;
197d9ac8608Spedro 	uint16_t flags;
198d9ac8608Spedro 	mode_t mode;
199d9ac8608Spedro 
2001af96cccSpedro 	perm = letoh32(up->u_fentry->perm);
2011af96cccSpedro 	flags = letoh16(up->u_fentry->icbtag.flags);
202d9ac8608Spedro 
203d9ac8608Spedro 	mode = perm & UDF_FENTRY_PERM_USER_MASK;
204d9ac8608Spedro 	mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK) >> 2);
205d9ac8608Spedro 	mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4);
206d9ac8608Spedro 	mode |= ((flags & UDF_ICB_TAG_FLAGS_STICKY) << 4);
207d9ac8608Spedro 	mode |= ((flags & UDF_ICB_TAG_FLAGS_SETGID) << 6);
208d9ac8608Spedro 	mode |= ((flags & UDF_ICB_TAG_FLAGS_SETUID) << 8);
209d9ac8608Spedro 
210d9ac8608Spedro 	return (mode);
211d9ac8608Spedro }
212d9ac8608Spedro 
213d9ac8608Spedro int
214d9ac8608Spedro udf_access(void *v)
215d9ac8608Spedro {
21699bc9d31Sderaadt 	struct vop_access_args *ap = v;
217d9ac8608Spedro 	struct vnode *vp;
2181af96cccSpedro 	struct unode *up;
219d9ac8608Spedro 	mode_t a_mode, mode;
220d9ac8608Spedro 
221d9ac8608Spedro 	vp = ap->a_vp;
2221af96cccSpedro 	up = VTOU(vp);
223d9ac8608Spedro 	a_mode = ap->a_mode;
224d9ac8608Spedro 
225d9ac8608Spedro 	if (a_mode & VWRITE) {
226d9ac8608Spedro 		switch (vp->v_type) {
227d9ac8608Spedro 		case VDIR:
228d9ac8608Spedro 		case VLNK:
229d9ac8608Spedro 		case VREG:
230d9ac8608Spedro 			return (EROFS);
231d9ac8608Spedro 			/* NOTREACHED */
232d9ac8608Spedro 		default:
233d9ac8608Spedro 			break;
234d9ac8608Spedro 		}
235d9ac8608Spedro 	}
236d9ac8608Spedro 
2371af96cccSpedro 	mode = udf_permtomode(up);
238d9ac8608Spedro 
239141c07a8Smillert 	return (vaccess(vp->v_type, mode, up->u_fentry->uid, up->u_fentry->gid,
240141c07a8Smillert 	    a_mode, ap->a_cred));
241d9ac8608Spedro }
242d9ac8608Spedro 
243d9ac8608Spedro static int mon_lens[2][12] = {
244d9ac8608Spedro 	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
245d9ac8608Spedro 	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
246d9ac8608Spedro };
247d9ac8608Spedro 
248d9ac8608Spedro static int
249d9ac8608Spedro udf_isaleapyear(int year)
250d9ac8608Spedro {
251d9ac8608Spedro 	int i;
252d9ac8608Spedro 
253d9ac8608Spedro 	i = (year % 4) ? 0 : 1;
254d9ac8608Spedro 	i &= (year % 100) ? 1 : 0;
255d9ac8608Spedro 	i |= (year % 400) ? 0 : 1;
256d9ac8608Spedro 
257d9ac8608Spedro 	return (i);
258d9ac8608Spedro }
259d9ac8608Spedro 
260d9ac8608Spedro /*
261c184c659Spedro  * This is just a rough hack.  Daylight savings isn't calculated and tv_nsec
262d9ac8608Spedro  * is ignored.
263d9ac8608Spedro  * Timezone calculation compliments of Julian Elischer <julian@elischer.org>.
264d9ac8608Spedro  */
265d9ac8608Spedro static void
266d9ac8608Spedro udf_timetotimespec(struct timestamp *time, struct timespec *t)
267d9ac8608Spedro {
268d9ac8608Spedro 	int i, lpyear, daysinyear, year;
269d9ac8608Spedro 	union {
270d9ac8608Spedro 		uint16_t	u_tz_offset;
271d9ac8608Spedro 		int16_t		s_tz_offset;
272d9ac8608Spedro 	} tz;
273d9ac8608Spedro 
274d9ac8608Spedro 	/* DirectCD seems to like using bogus year values */
275d9ac8608Spedro 	year = letoh16(time->year);
276d9ac8608Spedro 	if (year < 1970) {
277d9ac8608Spedro 		t->tv_sec = 0;
278139ddf65Sguenther 		t->tv_nsec = 0;
279d9ac8608Spedro 		return;
280d9ac8608Spedro 	}
281d9ac8608Spedro 
282d9ac8608Spedro 	/* Calculate the time and day */
283139ddf65Sguenther 	t->tv_nsec = 1000 * time->usec + 100000 * time->hund_usec
284139ddf65Sguenther 	    + 10000000 * time->centisec;
285d9ac8608Spedro 	t->tv_sec = time->second;
286d9ac8608Spedro 	t->tv_sec += time->minute * 60;
287d9ac8608Spedro 	t->tv_sec += time->hour * 3600;
288d9ac8608Spedro 	t->tv_sec += time->day * 3600 * 24;
289d9ac8608Spedro 
2904a5c7b1eSpedro 	/* Calculate the month */
291d9ac8608Spedro 	lpyear = udf_isaleapyear(year);
292d9ac8608Spedro 	for (i = 1; i < time->month; i++)
293d9ac8608Spedro 		t->tv_sec += mon_lens[lpyear][i] * 3600 * 24;
294d9ac8608Spedro 
295d9ac8608Spedro 	/* Speed up the calculation */
296d9ac8608Spedro 	if (year > 1979)
297d9ac8608Spedro 		t->tv_sec += 315532800;
298d9ac8608Spedro 	if (year > 1989)
299d9ac8608Spedro 		t->tv_sec += 315619200;
300d9ac8608Spedro 	if (year > 1999)
301d9ac8608Spedro 		t->tv_sec += 315532800;
302d9ac8608Spedro 	for (i = 2000; i < year; i++) {
303d9ac8608Spedro 		daysinyear = udf_isaleapyear(i) + 365 ;
304d9ac8608Spedro 		t->tv_sec += daysinyear * 3600 * 24;
305d9ac8608Spedro 	}
306d9ac8608Spedro 
307d9ac8608Spedro 	/*
308d9ac8608Spedro 	 * Calculate the time zone.  The timezone is 12 bit signed 2's
309d9ac8608Spedro 	 * compliment, so we gotta do some extra magic to handle it right.
310d9ac8608Spedro 	 */
311d9ac8608Spedro 	tz.u_tz_offset = letoh16(time->type_tz);
312d9ac8608Spedro 	tz.u_tz_offset &= 0x0fff;
313d9ac8608Spedro 	if (tz.u_tz_offset & 0x0800)
314d9ac8608Spedro 		tz.u_tz_offset |= 0xf000;	/* extend the sign to 16 bits */
315d9ac8608Spedro 	if ((time->type_tz & 0x1000) && (tz.s_tz_offset != -2047))
316d9ac8608Spedro 		t->tv_sec -= tz.s_tz_offset * 60;
317d9ac8608Spedro 
318d9ac8608Spedro 	return;
319d9ac8608Spedro }
320d9ac8608Spedro 
321d9ac8608Spedro int
322d9ac8608Spedro udf_getattr(void *v)
323d9ac8608Spedro {
32499bc9d31Sderaadt 	struct vop_getattr_args *ap = v;
325d9ac8608Spedro 	struct vnode *vp;
3261af96cccSpedro 	struct unode *up;
327d9ac8608Spedro 	struct vattr *vap;
328b077103dSkrw 	struct extfile_entry *xfentry;
329d9ac8608Spedro 	struct file_entry *fentry;
330d9ac8608Spedro 	struct timespec ts;
331d9ac8608Spedro 
332d9ac8608Spedro 	ts.tv_sec = 0;
333d9ac8608Spedro 
334d9ac8608Spedro 	vp = ap->a_vp;
335d9ac8608Spedro 	vap = ap->a_vap;
3361af96cccSpedro 	up = VTOU(vp);
337b077103dSkrw 
338b077103dSkrw 	xfentry = up->u_fentry;
339b077103dSkrw 	fentry = (struct file_entry *)up->u_fentry;
340d9ac8608Spedro 
3411af96cccSpedro 	vap->va_fsid = up->u_dev;
3421af96cccSpedro 	vap->va_fileid = up->u_ino;
3431af96cccSpedro 	vap->va_mode = udf_permtomode(up);
344d9ac8608Spedro 	vap->va_nlink = letoh16(fentry->link_cnt);
345d9ac8608Spedro 	/*
346c184c659Spedro 	 * The spec says that -1 is valid for uid/gid and indicates an
347d9ac8608Spedro 	 * invalid uid/gid.  How should this be represented?
348d9ac8608Spedro 	 */
349d9ac8608Spedro 	vap->va_uid = (letoh32(fentry->uid) == -1) ? 0 : letoh32(fentry->uid);
350d9ac8608Spedro 	vap->va_gid = (letoh32(fentry->gid) == -1) ? 0 : letoh32(fentry->gid);
351c184c659Spedro 	vap->va_rdev = 0;
352d9ac8608Spedro 	if (vp->v_type & VDIR) {
353337976e5Spedro 		vap->va_nlink++; /* Count a reference to ourselves */
354d9ac8608Spedro 		/*
355d9ac8608Spedro 		 * Directories that are recorded within their ICB will show
356d9ac8608Spedro 		 * as having 0 blocks recorded.  Since tradition dictates
357d9ac8608Spedro 		 * that directories consume at least one logical block,
358d9ac8608Spedro 		 * make it appear so.
359d9ac8608Spedro 		 */
360b077103dSkrw 		vap->va_size = up->u_ump->um_bsize;
361b077103dSkrw 	} else
362b077103dSkrw 		vap->va_size = letoh64(fentry->inf_len);
363b077103dSkrw 	if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0) {
364b077103dSkrw 		udf_timetotimespec(&xfentry->atime, &vap->va_atime);
365b077103dSkrw 		udf_timetotimespec(&xfentry->mtime, &vap->va_mtime);
366b077103dSkrw 		if ((vp->v_type & VDIR) && xfentry->logblks_rec != 0)
367b077103dSkrw 			vap->va_size =
368b077103dSkrw 				    letoh64(xfentry->logblks_rec) * up->u_ump->um_bsize;
369b077103dSkrw 	} else {
370b077103dSkrw 		udf_timetotimespec(&fentry->atime, &vap->va_atime);
371b077103dSkrw 		udf_timetotimespec(&fentry->mtime, &vap->va_mtime);
372b077103dSkrw 		if ((vp->v_type & VDIR) && fentry->logblks_rec != 0)
373d9ac8608Spedro 			vap->va_size =
374884fd684Spedro 				    letoh64(fentry->logblks_rec) * up->u_ump->um_bsize;
375d9ac8608Spedro 	}
376b077103dSkrw 	vap->va_ctime = vap->va_mtime; /* Stored as an Extended Attribute */
377d9ac8608Spedro 	vap->va_flags = 0;
378d9ac8608Spedro 	vap->va_gen = 1;
379884fd684Spedro 	vap->va_blocksize = up->u_ump->um_bsize;
380d9ac8608Spedro 	vap->va_bytes = letoh64(fentry->inf_len);
381d9ac8608Spedro 	vap->va_type = vp->v_type;
382c184c659Spedro 	vap->va_filerev = 0;
383d9ac8608Spedro 
384d9ac8608Spedro 	return (0);
385d9ac8608Spedro }
386d9ac8608Spedro 
387d9ac8608Spedro int
388d9ac8608Spedro udf_open(void *v)
389d9ac8608Spedro {
390c184c659Spedro 	return (0); /* Nothing to be done at this point */
391d9ac8608Spedro }
392d9ac8608Spedro 
393d9ac8608Spedro int
394d9ac8608Spedro udf_close(void *v)
395d9ac8608Spedro {
396c184c659Spedro 	return (0); /* Nothing to be done at this point */
397d9ac8608Spedro }
398d9ac8608Spedro 
399d9ac8608Spedro /*
400d9ac8608Spedro  * File specific ioctls.
401d9ac8608Spedro  */
402d9ac8608Spedro int
403d9ac8608Spedro udf_ioctl(void *v)
404d9ac8608Spedro {
405d9ac8608Spedro 	return (ENOTTY);
406d9ac8608Spedro }
407d9ac8608Spedro 
408d9ac8608Spedro /*
409d9ac8608Spedro  * I'm not sure that this has much value in a read-only filesystem, but
410d9ac8608Spedro  * cd9660 has it too.
411d9ac8608Spedro  */
412bb394a57Sderaadt int
413bb394a57Sderaadt udf_pathconf(void *v)
414d9ac8608Spedro {
415bb394a57Sderaadt 	struct vop_pathconf_args *ap = v;
416a71fda8cSmatthew 	int error = 0;
417d9ac8608Spedro 
418d9ac8608Spedro 	switch (ap->a_name) {
419d9ac8608Spedro 	case _PC_LINK_MAX:
420d9ac8608Spedro 		*ap->a_retval = 65535;
421a71fda8cSmatthew 		break;
422d9ac8608Spedro 	case _PC_NAME_MAX:
423d9ac8608Spedro 		*ap->a_retval = NAME_MAX;
424a71fda8cSmatthew 		break;
425bb394a57Sderaadt 	case _PC_CHOWN_RESTRICTED:
426bb394a57Sderaadt 		*ap->a_retval = 1;
427bb394a57Sderaadt 		break;
428d9ac8608Spedro 	case _PC_NO_TRUNC:
429d9ac8608Spedro 		*ap->a_retval = 1;
430a71fda8cSmatthew 		break;
431bb394a57Sderaadt 	case _PC_TIMESTAMP_RESOLUTION:
432bb394a57Sderaadt 		*ap->a_retval = 1000;		/* 1 microsecond */
433bb394a57Sderaadt 		break;
434d9ac8608Spedro 	default:
435a71fda8cSmatthew 		error = EINVAL;
436a71fda8cSmatthew 		break;
437d9ac8608Spedro 	}
438a71fda8cSmatthew 
439a71fda8cSmatthew 	return (error);
440d9ac8608Spedro }
441d9ac8608Spedro 
442d9ac8608Spedro int
443d9ac8608Spedro udf_read(void *v)
444d9ac8608Spedro {
44599bc9d31Sderaadt 	struct vop_read_args *ap = v;
446d9ac8608Spedro 	struct vnode *vp = ap->a_vp;
447d9ac8608Spedro 	struct uio *uio = ap->a_uio;
4481af96cccSpedro 	struct unode *up = VTOU(vp);
449d9ac8608Spedro 	struct buf *bp;
450d9ac8608Spedro 	uint8_t *data;
451d9ac8608Spedro 	off_t fsize, offset;
452d9ac8608Spedro 	int error = 0;
453d9ac8608Spedro 	int size;
454d9ac8608Spedro 
455d9ac8608Spedro 	if (uio->uio_offset < 0)
456d9ac8608Spedro 		return (EINVAL);
457d9ac8608Spedro 
4581af96cccSpedro 	fsize = letoh64(up->u_fentry->inf_len);
459d9ac8608Spedro 
460d9ac8608Spedro 	while (uio->uio_offset < fsize && uio->uio_resid > 0) {
461d9ac8608Spedro 		offset = uio->uio_offset;
4625e04bb1bSstefan 		size = ulmin(uio->uio_resid, MAXBSIZE);
4635e04bb1bSstefan 		if (size > fsize - offset)
464d9ac8608Spedro 			size = fsize - offset;
4651af96cccSpedro 		error = udf_readatoffset(up, &size, offset, &bp, &data);
466d9ac8608Spedro 		if (error == 0)
4675e04bb1bSstefan 			error = uiomove(data, (size_t)size, uio);
468b077103dSkrw 		if (bp != NULL) {
469d9ac8608Spedro 			brelse(bp);
470b077103dSkrw 			bp = NULL;
471b077103dSkrw 		}
472d9ac8608Spedro 		if (error)
473d9ac8608Spedro 			break;
474479c151dSjsg 	}
475d9ac8608Spedro 
476d9ac8608Spedro 	return (error);
477d9ac8608Spedro }
478d9ac8608Spedro 
479d9ac8608Spedro /*
480b6647e39Smiod  * Translate the name from a CS0 dstring to a 16-bit Unicode String.
481b6647e39Smiod  * Hooks need to be placed in here to translate from Unicode to the encoding
482b6647e39Smiod  * that the kernel/user expects.  Return the length of the translated string.
483d9ac8608Spedro  */
4848885c958Spedro int
485170e22feSpedro udf_transname(char *cs0string, char *destname, int len, struct umount *ump)
486d9ac8608Spedro {
487d9ac8608Spedro 	unicode_t *transname;
488d9ac8608Spedro 	int i, unilen = 0, destlen;
489d9ac8608Spedro 
4905bec07aaSpedro 	if (len > MAXNAMLEN) {
491b6647e39Smiod #ifdef DIAGNOSTIC
4925bec07aaSpedro 		printf("udf_transname(): name too long\n");
493b6647e39Smiod #endif
4945bec07aaSpedro 		return (0);
4955bec07aaSpedro 	}
4965bec07aaSpedro 
497d9ac8608Spedro 	/* allocate a buffer big enough to hold an 8->16 bit expansion */
498d9ac8608Spedro 	transname = pool_get(&udf_trans_pool, PR_WAITOK);
499d9ac8608Spedro 
500b6647e39Smiod 	if ((unilen = udf_rawnametounicode(len, cs0string, transname)) == -1) {
501b6647e39Smiod #ifdef DIAGNOSTIC
502b6647e39Smiod 		printf("udf_transname(): Unicode translation failed\n");
503b6647e39Smiod #endif
504d9ac8608Spedro 		pool_put(&udf_trans_pool, transname);
505d9ac8608Spedro 		return (0);
506d9ac8608Spedro 	}
507d9ac8608Spedro 
50860418546Spedro 	/* Pack it back to 8-bit Unicode. */
509d9ac8608Spedro 	for (i = 0; i < unilen ; i++)
510d9ac8608Spedro 		if (transname[i] & 0xff00)
511b6647e39Smiod 			destname[i] = '?';	/* Fudge the 16bit chars */
512d9ac8608Spedro 		else
513d9ac8608Spedro 			destname[i] = transname[i] & 0xff;
514d9ac8608Spedro 
515d9ac8608Spedro 	pool_put(&udf_trans_pool, transname);
516b6647e39Smiod 
517b6647e39Smiod 	/* Don't forget to terminate the string. */
518d9ac8608Spedro 	destname[unilen] = 0;
519d9ac8608Spedro 	destlen = unilen;
520d9ac8608Spedro 
521d9ac8608Spedro 	return (destlen);
522d9ac8608Spedro }
523d9ac8608Spedro 
524d9ac8608Spedro /*
525d9ac8608Spedro  * Compare a CS0 dstring with a name passed in from the VFS layer.  Return
5264a5c7b1eSpedro  * 0 on a successful match, nonzero otherwise.  Unicode work may need to be
5274a5c7b1eSpedro  * done here also.
528d9ac8608Spedro  */
529d9ac8608Spedro static int
530170e22feSpedro udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen, struct umount *ump)
531d9ac8608Spedro {
532d9ac8608Spedro 	char *transname;
533d9ac8608Spedro 	int error = 0;
534d9ac8608Spedro 
535c184c659Spedro 	/* This is overkill, but not worth creating a new pool */
536d9ac8608Spedro 	transname = pool_get(&udf_trans_pool, PR_WAITOK);
537d9ac8608Spedro 
538170e22feSpedro 	cs0len = udf_transname(cs0string, transname, cs0len, ump);
539d9ac8608Spedro 
540d9ac8608Spedro 	/* Easy check.  If they aren't the same length, they aren't equal */
541d9ac8608Spedro 	if ((cs0len == 0) || (cs0len != cmplen))
542d9ac8608Spedro 		error = -1;
543d9ac8608Spedro 	else
544d9ac8608Spedro 		error = bcmp(transname, cmpname, cmplen);
545d9ac8608Spedro 
546d9ac8608Spedro 	pool_put(&udf_trans_pool, transname);
547d9ac8608Spedro 
548d9ac8608Spedro 	return (error);
549d9ac8608Spedro }
550d9ac8608Spedro 
551d9ac8608Spedro struct udf_uiodir {
552d9ac8608Spedro 	struct dirent *dirent;
553d9ac8608Spedro 	int eofflag;
554d9ac8608Spedro };
555d9ac8608Spedro 
556d9ac8608Spedro static int
55791a535ffSguenther udf_uiodir(struct udf_uiodir *uiodir, struct uio *uio, long off)
558d9ac8608Spedro {
5595e04bb1bSstefan 	size_t de_size = DIRENT_SIZE(uiodir->dirent);
560d9ac8608Spedro 
561d9ac8608Spedro 	if (uio->uio_resid < de_size) {
562d9ac8608Spedro 		uiodir->eofflag = 0;
563d9ac8608Spedro 		return (-1);
564d9ac8608Spedro 	}
56591a535ffSguenther 	uiodir->dirent->d_off = off;
56691a535ffSguenther 	uiodir->dirent->d_reclen = de_size;
567d9ac8608Spedro 
56846f7109aSclaudio 	if (memchr(uiodir->dirent->d_name, '/',
56946f7109aSclaudio 	    uiodir->dirent->d_namlen) != NULL) {
57046f7109aSclaudio 		/* illegal file name */
57146f7109aSclaudio 		return (EINVAL);
57246f7109aSclaudio 	}
57346f7109aSclaudio 
5745e04bb1bSstefan 	return (uiomove(uiodir->dirent, de_size, uio));
575d9ac8608Spedro }
576d9ac8608Spedro 
577d9ac8608Spedro static struct udf_dirstream *
578170e22feSpedro udf_opendir(struct unode *up, int offset, int fsize, struct umount *ump)
579d9ac8608Spedro {
580d9ac8608Spedro 	struct udf_dirstream *ds;
581d9ac8608Spedro 
582920b9ef6Smk 	ds = pool_get(&udf_ds_pool, PR_WAITOK | PR_ZERO);
583d9ac8608Spedro 
5841af96cccSpedro 	ds->node = up;
585d9ac8608Spedro 	ds->offset = offset;
586170e22feSpedro 	ds->ump = ump;
587d9ac8608Spedro 	ds->fsize = fsize;
588d9ac8608Spedro 
589d9ac8608Spedro 	return (ds);
590d9ac8608Spedro }
591d9ac8608Spedro 
592d9ac8608Spedro static struct fileid_desc *
593d9ac8608Spedro udf_getfid(struct udf_dirstream *ds)
594d9ac8608Spedro {
595d9ac8608Spedro 	struct fileid_desc *fid;
596d9ac8608Spedro 	int error, frag_size = 0, total_fid_size;
597d9ac8608Spedro 
598d9ac8608Spedro 	/* End of directory? */
599d9ac8608Spedro 	if (ds->offset + ds->off >= ds->fsize) {
600d9ac8608Spedro 		ds->error = 0;
601d9ac8608Spedro 		return (NULL);
602d9ac8608Spedro 	}
603d9ac8608Spedro 
604d9ac8608Spedro 	/* Grab the first extent of the directory */
605d9ac8608Spedro 	if (ds->off == 0) {
606d9ac8608Spedro 		ds->size = 0;
607d9ac8608Spedro 		error = udf_readatoffset(ds->node, &ds->size, ds->offset,
608d9ac8608Spedro 		    &ds->bp, &ds->data);
609d9ac8608Spedro 		if (error) {
610d9ac8608Spedro 			ds->error = error;
611b077103dSkrw 			if (ds->bp != NULL) {
612d9ac8608Spedro 				brelse(ds->bp);
613b077103dSkrw 				ds->bp = NULL;
614b077103dSkrw 			}
615d9ac8608Spedro 			return (NULL);
616d9ac8608Spedro 		}
617d9ac8608Spedro 	}
618d9ac8608Spedro 
619d9ac8608Spedro 	/*
620d9ac8608Spedro 	 * Clean up from a previous fragmented FID.
621c184c659Spedro 	 * Is this the right place for this?
622d9ac8608Spedro 	 */
623d9ac8608Spedro 	if (ds->fid_fragment && ds->buf != NULL) {
624d9ac8608Spedro 		ds->fid_fragment = 0;
6250e5ae731Stedu 		free(ds->buf, M_UDFFID, 0);
626d9ac8608Spedro 	}
627d9ac8608Spedro 
628d9ac8608Spedro 	fid = (struct fileid_desc*)&ds->data[ds->off];
629d9ac8608Spedro 
630d9ac8608Spedro 	/*
631d9ac8608Spedro 	 * Check to see if the fid is fragmented. The first test
632d9ac8608Spedro 	 * ensures that we don't wander off the end of the buffer
633d9ac8608Spedro 	 * looking for the l_iu and l_fi fields.
634d9ac8608Spedro 	 */
635d9ac8608Spedro 	if (ds->off + UDF_FID_SIZE > ds->size ||
636d9ac8608Spedro 	    ds->off + letoh16(fid->l_iu) + fid->l_fi + UDF_FID_SIZE > ds->size){
637d9ac8608Spedro 
638d9ac8608Spedro 		/* Copy what we have of the fid into a buffer */
639d9ac8608Spedro 		frag_size = ds->size - ds->off;
640170e22feSpedro 		if (frag_size >= ds->ump->um_bsize) {
641d9ac8608Spedro 			printf("udf: invalid FID fragment\n");
642d9ac8608Spedro 			ds->error = EINVAL;
643d9ac8608Spedro 			return (NULL);
644d9ac8608Spedro 		}
645d9ac8608Spedro 
646d9ac8608Spedro 		/*
647d9ac8608Spedro 		 * File ID descriptors can only be at most one
648d9ac8608Spedro 		 * logical sector in size.
649d9ac8608Spedro 		 */
650bc9397c2Skrw 		ds->buf = malloc(ds->ump->um_bsize, M_UDFFID, M_WAITOK|M_ZERO);
651d9ac8608Spedro 		bcopy(fid, ds->buf, frag_size);
652d9ac8608Spedro 
653d9ac8608Spedro 		/* Reduce all of the casting magic */
654d9ac8608Spedro 		fid = (struct fileid_desc*)ds->buf;
655d9ac8608Spedro 
656b077103dSkrw 		if (ds->bp != NULL) {
657d9ac8608Spedro 			brelse(ds->bp);
658b077103dSkrw 			ds->bp = NULL;
659b077103dSkrw 		}
660d9ac8608Spedro 
661d9ac8608Spedro 		/* Fetch the next allocation */
662d9ac8608Spedro 		ds->offset += ds->size;
663d9ac8608Spedro 		ds->size = 0;
664d9ac8608Spedro 		error = udf_readatoffset(ds->node, &ds->size, ds->offset,
665d9ac8608Spedro 		    &ds->bp, &ds->data);
666d9ac8608Spedro 		if (error) {
667d9ac8608Spedro 			ds->error = error;
668b077103dSkrw 			if (ds->bp != NULL) {
669b077103dSkrw 				brelse(ds->bp);
670b077103dSkrw 				ds->bp = NULL;
671b077103dSkrw 			}
672d9ac8608Spedro 			return (NULL);
673d9ac8608Spedro 		}
674d9ac8608Spedro 
675d9ac8608Spedro 		/*
676d9ac8608Spedro 		 * If the fragment was so small that we didn't get
677d9ac8608Spedro 		 * the l_iu and l_fi fields, copy those in.
678d9ac8608Spedro 		 */
679d9ac8608Spedro 		if (frag_size < UDF_FID_SIZE)
680d9ac8608Spedro 			bcopy(ds->data, &ds->buf[frag_size],
681d9ac8608Spedro 			    UDF_FID_SIZE - frag_size);
682d9ac8608Spedro 
683d9ac8608Spedro 		/*
684d9ac8608Spedro 		 * Now that we have enough of the fid to work with,
685d9ac8608Spedro 		 * copy in the rest of the fid from the new
686d9ac8608Spedro 		 * allocation.
687d9ac8608Spedro 		 */
688d9ac8608Spedro 		total_fid_size = UDF_FID_SIZE + letoh16(fid->l_iu) + fid->l_fi;
689170e22feSpedro 		if (total_fid_size > ds->ump->um_bsize) {
690d9ac8608Spedro 			printf("udf: invalid FID\n");
691d9ac8608Spedro 			ds->error = EIO;
692d9ac8608Spedro 			return (NULL);
693d9ac8608Spedro 		}
694d9ac8608Spedro 		bcopy(ds->data, &ds->buf[frag_size],
695d9ac8608Spedro 		    total_fid_size - frag_size);
696d9ac8608Spedro 
697d9ac8608Spedro 		ds->fid_fragment = 1;
698d9ac8608Spedro 	} else {
699d9ac8608Spedro 		total_fid_size = letoh16(fid->l_iu) + fid->l_fi + UDF_FID_SIZE;
700d9ac8608Spedro 	}
701d9ac8608Spedro 
702d9ac8608Spedro 	/*
703d9ac8608Spedro 	 * Update the offset. Align on a 4 byte boundary because the
704d9ac8608Spedro 	 * UDF spec says so.
705d9ac8608Spedro 	 */
706d9ac8608Spedro 	if (!ds->fid_fragment) {
707d9ac8608Spedro 		ds->off += (total_fid_size + 3) & ~0x03;
708d9ac8608Spedro 	} else {
709d9ac8608Spedro 		ds->off = (total_fid_size - frag_size + 3) & ~0x03;
710d9ac8608Spedro 	}
7116cb982a7Sguenther 	ds->this_off = ds->offset + ds->off;
712d9ac8608Spedro 
713d9ac8608Spedro 	return (fid);
714d9ac8608Spedro }
715d9ac8608Spedro 
716d9ac8608Spedro static void
717d9ac8608Spedro udf_closedir(struct udf_dirstream *ds)
718d9ac8608Spedro {
719d9ac8608Spedro 
720b077103dSkrw 	if (ds->bp != NULL) {
721d9ac8608Spedro 		brelse(ds->bp);
722b077103dSkrw 		ds->bp = NULL;
723b077103dSkrw 	}
724d9ac8608Spedro 
725d9ac8608Spedro 	if (ds->fid_fragment && ds->buf != NULL)
7260e5ae731Stedu 		free(ds->buf, M_UDFFID, 0);
727d9ac8608Spedro 
728d9ac8608Spedro 	pool_put(&udf_ds_pool, ds);
729d9ac8608Spedro }
730d9ac8608Spedro 
7316cb982a7Sguenther #define SELF_OFFSET	1
7326cb982a7Sguenther #define PARENT_OFFSET	2
7336cb982a7Sguenther 
734d9ac8608Spedro int
735d9ac8608Spedro udf_readdir(void *v)
736d9ac8608Spedro {
73799bc9d31Sderaadt 	struct vop_readdir_args *ap = v;
738d9ac8608Spedro 	struct vnode *vp;
739d9ac8608Spedro 	struct uio *uio;
740d9ac8608Spedro 	struct dirent dir;
7411af96cccSpedro 	struct unode *up;
742170e22feSpedro 	struct umount *ump;
743d9ac8608Spedro 	struct fileid_desc *fid;
744d9ac8608Spedro 	struct udf_uiodir uiodir;
745d9ac8608Spedro 	struct udf_dirstream *ds;
74696c276e9Sguenther 	off_t last_off;
7476cb982a7Sguenther 	enum { MODE_NORMAL, MODE_SELF, MODE_PARENT } mode;
748d9ac8608Spedro 	int error = 0;
749d9ac8608Spedro 
750d9ac8608Spedro 	vp = ap->a_vp;
751d9ac8608Spedro 	uio = ap->a_uio;
7521af96cccSpedro 	up = VTOU(vp);
753170e22feSpedro 	ump = up->u_ump;
754d9ac8608Spedro 	uiodir.eofflag = 1;
75591a535ffSguenther 	uiodir.dirent = &dir;
756ebee68f3Sguenther 	memset(&dir, 0, sizeof(dir));
757d9ac8608Spedro 
758d9ac8608Spedro 	/*
7596cb982a7Sguenther 	 * if asked to start at SELF_OFFSET or PARENT_OFFSET, search
7606cb982a7Sguenther 	 * for the parent ref
7616cb982a7Sguenther 	 */
7626cb982a7Sguenther 	if (uio->uio_offset == SELF_OFFSET) {
7636cb982a7Sguenther 		mode = MODE_SELF;
7646cb982a7Sguenther 		uio->uio_offset = 0;
7656cb982a7Sguenther 	} else if (uio->uio_offset == PARENT_OFFSET) {
7666cb982a7Sguenther 		mode = MODE_PARENT;
7676cb982a7Sguenther 		uio->uio_offset = 0;
7686cb982a7Sguenther 	} else
7696cb982a7Sguenther 		mode = MODE_NORMAL;
7706cb982a7Sguenther 
7716cb982a7Sguenther 	/*
772d9ac8608Spedro 	 * Iterate through the file id descriptors.  Give the parent dir
773d9ac8608Spedro 	 * entry special attention.
774d9ac8608Spedro 	 */
775b077103dSkrw 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
776b077103dSkrw 		up->u_ump->um_start += up->u_ump->um_meta_start;
777b077103dSkrw 		up->u_ump->um_len = up->u_ump->um_meta_len;
778b077103dSkrw 	}
7791af96cccSpedro 	ds = udf_opendir(up, uio->uio_offset,
7801af96cccSpedro 	    letoh64(up->u_fentry->inf_len), up->u_ump);
781d9ac8608Spedro 
78296c276e9Sguenther 	last_off = ds->offset + ds->off;
783d9ac8608Spedro 	while ((fid = udf_getfid(ds)) != NULL) {
784d9ac8608Spedro 
785c184c659Spedro 		/* Should we return an error on a bad fid? */
786d9ac8608Spedro 		if (udf_checktag(&fid->tag, TAGID_FID)) {
787b077103dSkrw 			printf("Invalid FID tag (%d)\n", fid->tag.id);
788d9ac8608Spedro 			error = EIO;
789d9ac8608Spedro 			break;
790d9ac8608Spedro 		}
791d9ac8608Spedro 
792d9ac8608Spedro 		/* Is this a deleted file? */
793d9ac8608Spedro 		if (fid->file_char & UDF_FILE_CHAR_DEL)
794d9ac8608Spedro 			continue;
795d9ac8608Spedro 
796d9ac8608Spedro 		if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
797d9ac8608Spedro 			/* Do up the '.' and '..' entries.  Dummy values are
79891a535ffSguenther 			 * used for the offset since the offset here is
799d9ac8608Spedro 			 * usually zero, and NFS doesn't like that value
800d9ac8608Spedro 			 */
8016cb982a7Sguenther 			if (mode == MODE_NORMAL) {
8021af96cccSpedro 				dir.d_fileno = up->u_ino;
803d9ac8608Spedro 				dir.d_type = DT_DIR;
804d9ac8608Spedro 				dir.d_name[0] = '.';
805d9ac8608Spedro 				dir.d_name[1] = '\0';
806d9ac8608Spedro 				dir.d_namlen = 1;
8076cb982a7Sguenther 				error = udf_uiodir(&uiodir, uio, SELF_OFFSET);
808d9ac8608Spedro 				if (error)
809d9ac8608Spedro 					break;
8106cb982a7Sguenther 			}
8116cb982a7Sguenther 			if (mode != MODE_PARENT) {
812d9ac8608Spedro 				dir.d_fileno = udf_getid(&fid->icb);
813d9ac8608Spedro 				dir.d_type = DT_DIR;
814d9ac8608Spedro 				dir.d_name[0] = '.';
815d9ac8608Spedro 				dir.d_name[1] = '.';
816d9ac8608Spedro 				dir.d_name[2] = '\0';
817d9ac8608Spedro 				dir.d_namlen = 2;
8186cb982a7Sguenther 				error = udf_uiodir(&uiodir, uio, PARENT_OFFSET);
8196cb982a7Sguenther 			}
8206cb982a7Sguenther 			mode = MODE_NORMAL;
8216cb982a7Sguenther 		} else if (mode != MODE_NORMAL) {
8226cb982a7Sguenther 			continue;
823d9ac8608Spedro 		} else {
824d9ac8608Spedro 			dir.d_namlen = udf_transname(&fid->data[fid->l_iu],
825170e22feSpedro 			    &dir.d_name[0], fid->l_fi, ump);
826d9ac8608Spedro 			dir.d_fileno = udf_getid(&fid->icb);
827d9ac8608Spedro 			dir.d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ?
828d9ac8608Spedro 			    DT_DIR : DT_UNKNOWN;
82991a535ffSguenther 			error = udf_uiodir(&uiodir, uio, ds->this_off);
830d9ac8608Spedro 		}
831d9ac8608Spedro 		if (error) {
83296c276e9Sguenther 			/*
83396c276e9Sguenther 			 * udf_uiodir() indicates there isn't space for
83496c276e9Sguenther 			 * another entry by returning -1
83596c276e9Sguenther 			 */
83696c276e9Sguenther 			if (error == -1)
83796c276e9Sguenther 				error = 0;
838d9ac8608Spedro 			break;
839d9ac8608Spedro 		}
84096c276e9Sguenther 		last_off = ds->this_off;
841d9ac8608Spedro 	}
842d9ac8608Spedro 
843d9ac8608Spedro 	/* tell the calling layer whether we need to be called again */
844d9ac8608Spedro 	*ap->a_eofflag = uiodir.eofflag;
84596c276e9Sguenther 	uio->uio_offset = last_off;
846d9ac8608Spedro 
847d9ac8608Spedro 	if (!error)
848d9ac8608Spedro 		error = ds->error;
849d9ac8608Spedro 
850d9ac8608Spedro 	udf_closedir(ds);
851b077103dSkrw 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
852b077103dSkrw 		up->u_ump->um_start = up->u_ump->um_realstart;
853b077103dSkrw 		up->u_ump->um_len = up->u_ump->um_reallen;
854b077103dSkrw 	}
855d9ac8608Spedro 
856d9ac8608Spedro 	return (error);
857d9ac8608Spedro }
858d9ac8608Spedro 
859d9ac8608Spedro /* Are there any implementations out there that do soft-links? */
860d9ac8608Spedro int
861d9ac8608Spedro udf_readlink(void *v)
862d9ac8608Spedro {
863d9ac8608Spedro 	return (EOPNOTSUPP);
864d9ac8608Spedro }
865d9ac8608Spedro 
866d9ac8608Spedro int
867d9ac8608Spedro udf_strategy(void *v)
868d9ac8608Spedro {
86999bc9d31Sderaadt 	struct vop_strategy_args *ap = v;
870d9ac8608Spedro 	struct buf *bp;
871d9ac8608Spedro 	struct vnode *vp;
8721af96cccSpedro 	struct unode *up;
873d9ac8608Spedro 	int maxsize, s, error;
874d9ac8608Spedro 
875d9ac8608Spedro 	bp = ap->a_bp;
876d9ac8608Spedro 	vp = bp->b_vp;
8771af96cccSpedro 	up = VTOU(vp);
878d9ac8608Spedro 
879d9ac8608Spedro 	/* cd9660 has this test reversed, but it seems more logical this way */
880d9ac8608Spedro 	if (bp->b_blkno != bp->b_lblkno) {
881d9ac8608Spedro 		/*
882d9ac8608Spedro 		 * Files that are embedded in the fentry don't translate well
883d9ac8608Spedro 		 * to a block number.  Reject.
884d9ac8608Spedro 		 */
885884fd684Spedro 		if (udf_bmap_internal(up, bp->b_lblkno * up->u_ump->um_bsize,
886d9ac8608Spedro 		    &bp->b_lblkno, &maxsize)) {
887d9ac8608Spedro 			clrbuf(bp);
888d9ac8608Spedro 			bp->b_blkno = -1;
889d9ac8608Spedro 		}
890d9ac8608Spedro 	} else {
891d9ac8608Spedro 		error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL);
892d9ac8608Spedro 		if (error) {
893d9ac8608Spedro 			bp->b_error = error;
894d9ac8608Spedro 			bp->b_flags |= B_ERROR;
895d9ac8608Spedro 			s = splbio();
896d9ac8608Spedro 			biodone(bp);
897d9ac8608Spedro 			splx(s);
898d9ac8608Spedro 			return (error);
899d9ac8608Spedro 		}
900d9ac8608Spedro 
901d9ac8608Spedro 		if ((long)bp->b_blkno == -1)
902d9ac8608Spedro 			clrbuf(bp);
903d9ac8608Spedro 	}
904d9ac8608Spedro 
905d9ac8608Spedro 	if ((long)bp->b_blkno == -1) {
906d9ac8608Spedro 		s = splbio();
907d9ac8608Spedro 		biodone(bp);
908d9ac8608Spedro 		splx(s);
909d9ac8608Spedro 	} else {
910d9ac8608Spedro 		bp->b_dev = vp->v_rdev;
911f1993be3Svisa 		VOP_STRATEGY(up->u_devvp, bp);
912d9ac8608Spedro 	}
913d9ac8608Spedro 
914d9ac8608Spedro 	return (0);
915d9ac8608Spedro }
916d9ac8608Spedro 
917d9ac8608Spedro int
918d9ac8608Spedro udf_lock(void *v)
919d9ac8608Spedro {
92099bc9d31Sderaadt 	struct vop_lock_args *ap = v;
921d9ac8608Spedro 	struct vnode *vp = ap->a_vp;
922d9ac8608Spedro 
92326b8ec94Snatano 	return rrw_enter(&VTOU(vp)->u_lock, ap->a_flags & LK_RWFLAGS);
924d9ac8608Spedro }
925d9ac8608Spedro 
926d9ac8608Spedro int
927d9ac8608Spedro udf_unlock(void *v)
928d9ac8608Spedro {
92999bc9d31Sderaadt 	struct vop_unlock_args *ap = v;
930d9ac8608Spedro 	struct vnode *vp = ap->a_vp;
931d9ac8608Spedro 
93226b8ec94Snatano 	rrw_exit(&VTOU(vp)->u_lock);
93326b8ec94Snatano 	return 0;
934d9ac8608Spedro }
935d9ac8608Spedro 
936d9ac8608Spedro int
937d9ac8608Spedro udf_islocked(void *v)
938d9ac8608Spedro {
93999bc9d31Sderaadt 	struct vop_islocked_args *ap = v;
940d9ac8608Spedro 
94126b8ec94Snatano 	return rrw_status(&VTOU(ap->a_vp)->u_lock);
942d9ac8608Spedro }
943d9ac8608Spedro 
944d9ac8608Spedro int
945d9ac8608Spedro udf_print(void *v)
946d9ac8608Spedro {
947*5a0ec814Smiod #if defined(DEBUG) || defined(DIAGNOSTIC) || defined(VFSLCKDEBUG)
94899bc9d31Sderaadt 	struct vop_print_args *ap = v;
949d9ac8608Spedro 	struct vnode *vp = ap->a_vp;
9501af96cccSpedro 	struct unode *up = VTOU(vp);
951d9ac8608Spedro 
952d9ac8608Spedro 	/*
953d9ac8608Spedro 	 * Complete the information given by vprint().
954d9ac8608Spedro 	 */
9551af96cccSpedro 	printf("tag VT_UDF, hash id %u\n", up->u_ino);
956f70ef8d5Sderaadt #ifdef DIAGNOSTIC
957d9ac8608Spedro 	printf("\n");
958f70ef8d5Sderaadt #endif
959*5a0ec814Smiod #endif
960d9ac8608Spedro 	return (0);
961d9ac8608Spedro }
962d9ac8608Spedro 
963d9ac8608Spedro int
964d9ac8608Spedro udf_bmap(void *v)
965d9ac8608Spedro {
96699bc9d31Sderaadt 	struct vop_bmap_args *ap = v;
9671af96cccSpedro 	struct unode *up;
968d9ac8608Spedro 	uint32_t max_size;
9691abdbfdeSderaadt 	daddr_t lsector;
970d9ac8608Spedro 	int error;
971d9ac8608Spedro 
9721af96cccSpedro 	up = VTOU(ap->a_vp);
973d9ac8608Spedro 
974d9ac8608Spedro 	if (ap->a_vpp != NULL)
9751af96cccSpedro 		*ap->a_vpp = up->u_devvp;
976d9ac8608Spedro 	if (ap->a_bnp == NULL)
977d9ac8608Spedro 		return (0);
978d9ac8608Spedro 
979884fd684Spedro 	error = udf_bmap_internal(up, ap->a_bn * up->u_ump->um_bsize,
980797916ccSpedro 	    &lsector, &max_size);
981d9ac8608Spedro 	if (error)
982d9ac8608Spedro 		return (error);
983d9ac8608Spedro 
984d9ac8608Spedro 	/* Translate logical to physical sector number */
985884fd684Spedro 	*ap->a_bnp = lsector << (up->u_ump->um_bshift - DEV_BSHIFT);
986d9ac8608Spedro 
987d9ac8608Spedro 	/* Punt on read-ahead for now */
988d9ac8608Spedro 	if (ap->a_runp)
989d9ac8608Spedro 		*ap->a_runp = 0;
990d9ac8608Spedro 
991d9ac8608Spedro 	return (0);
992d9ac8608Spedro }
993d9ac8608Spedro 
994d9ac8608Spedro /*
995d9ac8608Spedro  * The all powerful VOP_LOOKUP().
996d9ac8608Spedro  */
997d9ac8608Spedro int
998d9ac8608Spedro udf_lookup(void *v)
999d9ac8608Spedro {
100099bc9d31Sderaadt 	struct vop_lookup_args *ap = v;
1001d9ac8608Spedro 	struct vnode *dvp;
1002d9ac8608Spedro 	struct vnode *tdp = NULL;
1003d9ac8608Spedro 	struct vnode **vpp = ap->a_vpp;
10041af96cccSpedro 	struct unode *up;
1005170e22feSpedro 	struct umount *ump;
1006d9ac8608Spedro 	struct fileid_desc *fid = NULL;
1007d9ac8608Spedro 	struct udf_dirstream *ds;
1008d9ac8608Spedro 	struct proc *p;
1009d9ac8608Spedro 	u_long nameiop;
1010d9ac8608Spedro 	u_long flags;
1011d9ac8608Spedro 	char *nameptr;
1012d9ac8608Spedro 	long namelen;
10130cad8b22Sguenther 	udfino_t id = 0;
1014d9ac8608Spedro 	int offset, error = 0;
1015d9ac8608Spedro 	int numdirpasses, fsize;
1016d9ac8608Spedro 
1017d9ac8608Spedro 	extern struct nchstats nchstats;
1018d9ac8608Spedro 
1019d9ac8608Spedro 	dvp = ap->a_dvp;
10201af96cccSpedro 	up = VTOU(dvp);
1021170e22feSpedro 	ump = up->u_ump;
1022d9ac8608Spedro 	nameiop = ap->a_cnp->cn_nameiop;
1023d9ac8608Spedro 	flags = ap->a_cnp->cn_flags;
1024d9ac8608Spedro 	nameptr = ap->a_cnp->cn_nameptr;
1025d9ac8608Spedro 	namelen = ap->a_cnp->cn_namelen;
10261af96cccSpedro 	fsize = letoh64(up->u_fentry->inf_len);
1027d9ac8608Spedro 	p = ap->a_cnp->cn_proc;
102869d99b4dSpedro 	*vpp = NULL;
1029d9ac8608Spedro 
1030d9ac8608Spedro 	/*
1031d9ac8608Spedro 	 * Make sure the process can scan the requested directory.
1032d9ac8608Spedro 	 */
1033d9ac8608Spedro 	error = VOP_ACCESS(dvp, VEXEC, ap->a_cnp->cn_cred, p);
1034d9ac8608Spedro 	if (error)
1035d9ac8608Spedro 		return (error);
1036d9ac8608Spedro 
1037d9ac8608Spedro 	/*
1038d9ac8608Spedro 	 * Check if the (directory, name) tuple has been already cached.
1039d9ac8608Spedro 	 */
1040d9ac8608Spedro 	error = cache_lookup(dvp, vpp, ap->a_cnp);
1041d9ac8608Spedro 	if (error >= 0)
1042d9ac8608Spedro 		return (error);
1043d9ac8608Spedro 	else
1044d9ac8608Spedro 		error = 0;
1045d9ac8608Spedro 
1046d9ac8608Spedro 	/*
1047d9ac8608Spedro 	 * If dvp is what's being looked up, then return it.
1048d9ac8608Spedro 	 */
1049d9ac8608Spedro 	if (ap->a_cnp->cn_namelen == 1 && ap->a_cnp->cn_nameptr[0] == '.') {
1050627b2c48Sthib 		vref(dvp);
1051d9ac8608Spedro 		*vpp = dvp;
1052d9ac8608Spedro 		return (0);
1053d9ac8608Spedro 	}
1054d9ac8608Spedro 
1055d9ac8608Spedro 	/*
1056d9ac8608Spedro 	 * If this is a LOOKUP and we've already partially searched through
1057d9ac8608Spedro 	 * the directory, pick up where we left off and flag that the
1058d9ac8608Spedro 	 * directory may need to be searched twice.  For a full description,
1059d9ac8608Spedro 	 * see /sys/isofs/cd9660/cd9660_lookup.c:cd9660_lookup()
1060d9ac8608Spedro 	 */
10611af96cccSpedro 	if (nameiop != LOOKUP || up->u_diroff == 0 || up->u_diroff > fsize) {
1062d9ac8608Spedro 		offset = 0;
1063d9ac8608Spedro 		numdirpasses = 1;
1064d9ac8608Spedro 	} else {
10651af96cccSpedro 		offset = up->u_diroff;
1066d9ac8608Spedro 		numdirpasses = 2;
1067d9ac8608Spedro 		nchstats.ncs_2passes++;
1068d9ac8608Spedro 	}
1069d9ac8608Spedro 
1070b077103dSkrw 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1071b077103dSkrw 		up->u_ump->um_start += up->u_ump->um_meta_start;
1072b077103dSkrw 		up->u_ump->um_len = up->u_ump->um_meta_len;
1073b077103dSkrw 	}
1074d9ac8608Spedro lookloop:
1075170e22feSpedro 	ds = udf_opendir(up, offset, fsize, ump);
1076d9ac8608Spedro 
1077d9ac8608Spedro 	while ((fid = udf_getfid(ds)) != NULL) {
1078d9ac8608Spedro 		/* Check for a valid FID tag. */
1079d9ac8608Spedro 		if (udf_checktag(&fid->tag, TAGID_FID)) {
1080d9ac8608Spedro 			printf("udf_lookup: Invalid tag\n");
1081d9ac8608Spedro 			error = EIO;
1082d9ac8608Spedro 			break;
1083d9ac8608Spedro 		}
1084d9ac8608Spedro 
1085d9ac8608Spedro 		/* Is this a deleted file? */
1086d9ac8608Spedro 		if (fid->file_char & UDF_FILE_CHAR_DEL)
1087d9ac8608Spedro 			continue;
1088d9ac8608Spedro 
1089d9ac8608Spedro 		if ((fid->l_fi == 0) && (fid->file_char & UDF_FILE_CHAR_PAR)) {
1090d9ac8608Spedro 			if (flags & ISDOTDOT) {
1091d9ac8608Spedro 				id = udf_getid(&fid->icb);
1092d9ac8608Spedro 				break;
1093d9ac8608Spedro 			}
1094d9ac8608Spedro 		} else {
1095d9ac8608Spedro 			if (!(udf_cmpname(&fid->data[fid->l_iu],
1096170e22feSpedro 			    nameptr, fid->l_fi, namelen, ump))) {
1097d9ac8608Spedro 				id = udf_getid(&fid->icb);
1098d9ac8608Spedro 				break;
1099d9ac8608Spedro 			}
1100d9ac8608Spedro 		}
1101d9ac8608Spedro 	}
1102d9ac8608Spedro 
1103d9ac8608Spedro 	if (!error)
1104d9ac8608Spedro 		error = ds->error;
1105d9ac8608Spedro 
1106d9ac8608Spedro 	if (error) {
1107d9ac8608Spedro 		udf_closedir(ds);
1108b077103dSkrw 		if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1109b077103dSkrw 			up->u_ump->um_start = up->u_ump->um_realstart;
1110b077103dSkrw 			up->u_ump->um_len = up->u_ump->um_reallen;
1111b077103dSkrw 		}
1112d9ac8608Spedro 		return (error);
1113d9ac8608Spedro 	}
1114d9ac8608Spedro 
1115d9ac8608Spedro 	/* Did we have a match? */
1116d9ac8608Spedro 	if (id) {
1117170e22feSpedro 		error = udf_vget(ump->um_mountp, id, &tdp);
1118d9ac8608Spedro 		if (!error) {
1119d9ac8608Spedro 			/*
1120d9ac8608Spedro 			 * Remember where this entry was if it's the final
1121d9ac8608Spedro 			 * component.
1122d9ac8608Spedro 			 */
1123d9ac8608Spedro 			if ((flags & ISLASTCN) && nameiop == LOOKUP)
11241af96cccSpedro 				up->u_diroff = ds->offset + ds->off;
1125d9ac8608Spedro 			if (numdirpasses == 2)
1126d9ac8608Spedro 				nchstats.ncs_pass2++;
1127d9ac8608Spedro 			if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) {
1128d9ac8608Spedro 				ap->a_cnp->cn_flags |= PDIRUNLOCK;
112936bb23f1Svisa 				VOP_UNLOCK(dvp);
1130d9ac8608Spedro 			}
1131d9ac8608Spedro 
1132d9ac8608Spedro 			*vpp = tdp;
1133d9ac8608Spedro 		}
1134d9ac8608Spedro 	} else {
1135d9ac8608Spedro 		/* Name wasn't found on this pass.  Do another pass? */
1136d9ac8608Spedro 		if (numdirpasses == 2) {
1137d9ac8608Spedro 			numdirpasses--;
1138d9ac8608Spedro 			offset = 0;
1139d9ac8608Spedro 			udf_closedir(ds);
1140d9ac8608Spedro 			goto lookloop;
1141d9ac8608Spedro 		}
1142d9ac8608Spedro 
1143d9ac8608Spedro 		if ((flags & ISLASTCN) &&
1144d9ac8608Spedro 		    (nameiop == CREATE || nameiop == RENAME)) {
1145d9ac8608Spedro 			error = EROFS;
1146d9ac8608Spedro 		} else {
1147d9ac8608Spedro 			error = ENOENT;
1148d9ac8608Spedro 		}
1149d9ac8608Spedro 	}
1150d9ac8608Spedro 
1151d9ac8608Spedro 	/*
1152d9ac8608Spedro 	 * Cache the result of this lookup.
1153d9ac8608Spedro 	 */
1154d9ac8608Spedro 	if (flags & MAKEENTRY)
1155d9ac8608Spedro 		cache_enter(dvp, *vpp, ap->a_cnp);
1156d9ac8608Spedro 
1157d9ac8608Spedro 	udf_closedir(ds);
1158b077103dSkrw 	if (ISSET(up->u_ump->um_flags, UDF_MNT_USES_META)) {
1159b077103dSkrw 		up->u_ump->um_start = up->u_ump->um_realstart;
1160b077103dSkrw 		up->u_ump->um_len = up->u_ump->um_reallen;
1161b077103dSkrw 	}
1162d9ac8608Spedro 
1163d9ac8608Spedro 	return (error);
1164d9ac8608Spedro }
1165d9ac8608Spedro 
1166d9ac8608Spedro int
1167d9ac8608Spedro udf_inactive(void *v)
1168d9ac8608Spedro {
116999bc9d31Sderaadt 	struct vop_inactive_args *ap = v;
1170d9ac8608Spedro 	struct vnode *vp = ap->a_vp;
1171d9ac8608Spedro 
1172d9ac8608Spedro 	/*
1173d9ac8608Spedro 	 * No need to sync anything, so just unlock the vnode and return.
1174d9ac8608Spedro 	 */
117536bb23f1Svisa 	VOP_UNLOCK(vp);
1176d9ac8608Spedro 
1177d9ac8608Spedro 	return (0);
1178d9ac8608Spedro }
1179d9ac8608Spedro 
1180d9ac8608Spedro int
1181d9ac8608Spedro udf_reclaim(void *v)
1182d9ac8608Spedro {
118399bc9d31Sderaadt 	struct vop_reclaim_args *ap = v;
1184d9ac8608Spedro 	struct vnode *vp;
11851af96cccSpedro 	struct unode *up;
1186d9ac8608Spedro 
1187d9ac8608Spedro 	vp = ap->a_vp;
11881af96cccSpedro 	up = VTOU(vp);
1189d9ac8608Spedro 
11901af96cccSpedro 	if (up != NULL) {
11911af96cccSpedro 		udf_hashrem(up);
11921af96cccSpedro 		if (up->u_devvp) {
11931af96cccSpedro 			vrele(up->u_devvp);
11941af96cccSpedro 			up->u_devvp = 0;
1195d9ac8608Spedro 		}
1196d9ac8608Spedro 
11971af96cccSpedro 		if (up->u_fentry != NULL)
11980e5ae731Stedu 			free(up->u_fentry, M_UDFFENTRY, 0);
11995921d099Spedro 
12001af96cccSpedro 		pool_put(&unode_pool, up);
1201d9ac8608Spedro 		vp->v_data = NULL;
1202d9ac8608Spedro 	}
1203d9ac8608Spedro 
1204d9ac8608Spedro 	return (0);
1205d9ac8608Spedro }
1206d9ac8608Spedro 
1207d9ac8608Spedro /*
1208d9ac8608Spedro  * Read the block and then set the data pointer to correspond with the
1209d9ac8608Spedro  * offset passed in.  Only read in at most 'size' bytes, and then set 'size'
1210d9ac8608Spedro  * to the number of bytes pointed to.  If 'size' is zero, try to read in a
1211d9ac8608Spedro  * whole extent.
1212d9ac8608Spedro  *
1213d9ac8608Spedro  * Note that *bp may be assigned error or not.
1214d9ac8608Spedro  *
1215d9ac8608Spedro  */
1216d9ac8608Spedro int
12171af96cccSpedro udf_readatoffset(struct unode *up, int *size, off_t offset,
1218d9ac8608Spedro     struct buf **bp, uint8_t **data)
1219d9ac8608Spedro {
1220170e22feSpedro 	struct umount *ump;
1221b077103dSkrw 	struct extfile_entry *xfentry = NULL;
1222d9ac8608Spedro 	struct file_entry *fentry = NULL;
1223d9ac8608Spedro 	struct buf *bp1;
1224d9ac8608Spedro 	uint32_t max_size;
12251abdbfdeSderaadt 	daddr_t sector;
1226d9ac8608Spedro 	int error;
1227d9ac8608Spedro 
1228170e22feSpedro 	ump = up->u_ump;
1229d9ac8608Spedro 
1230d9ac8608Spedro 	*bp = NULL;
12311af96cccSpedro 	error = udf_bmap_internal(up, offset, &sector, &max_size);
1232d9ac8608Spedro 	if (error == UDF_INVALID_BMAP) {
1233d9ac8608Spedro 		/*
1234d9ac8608Spedro 		 * This error means that the file *data* is stored in the
1235d9ac8608Spedro 		 * allocation descriptor field of the file entry.
1236d9ac8608Spedro 		 */
1237b077103dSkrw 		if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0) {
1238b077103dSkrw 			xfentry = up->u_fentry;
1239b077103dSkrw 			*data = &xfentry->data[letoh32(xfentry->l_ea)];
1240b077103dSkrw 			*size = letoh32(xfentry->l_ad);
1241b077103dSkrw 		} else {
1242b077103dSkrw 			fentry = (struct file_entry *)up->u_fentry;
1243d9ac8608Spedro 			*data = &fentry->data[letoh32(fentry->l_ea)];
1244d9ac8608Spedro 			*size = letoh32(fentry->l_ad);
1245b077103dSkrw 		}
1246d9ac8608Spedro 		return (0);
1247d9ac8608Spedro 	} else if (error != 0) {
1248d9ac8608Spedro 		return (error);
1249d9ac8608Spedro 	}
1250d9ac8608Spedro 
1251d9ac8608Spedro 	/* Adjust the size so that it is within range */
1252d9ac8608Spedro 	if (*size == 0 || *size > max_size)
1253d9ac8608Spedro 		*size = max_size;
1254d9ac8608Spedro 	*size = min(*size, MAXBSIZE);
1255d9ac8608Spedro 
1256170e22feSpedro 	if ((error = udf_readlblks(ump, sector, *size, bp))) {
1257d9ac8608Spedro 		printf("warning: udf_readlblks returned error %d\n", error);
1258d9ac8608Spedro 		/* note: *bp may be non-NULL */
1259d9ac8608Spedro 		return (error);
1260d9ac8608Spedro 	}
1261d9ac8608Spedro 
1262d9ac8608Spedro 	bp1 = *bp;
1263170e22feSpedro 	*data = (uint8_t *)&bp1->b_data[offset % ump->um_bsize];
1264d9ac8608Spedro 	return (0);
1265d9ac8608Spedro }
1266d9ac8608Spedro 
1267d9ac8608Spedro /*
1268d9ac8608Spedro  * Translate a file offset into a logical block and then into a physical
1269d9ac8608Spedro  * block.
1270d9ac8608Spedro  */
1271d9ac8608Spedro int
12721abdbfdeSderaadt udf_bmap_internal(struct unode *up, off_t offset, daddr_t *sector,
1273d9ac8608Spedro     uint32_t *max_size)
1274d9ac8608Spedro {
1275170e22feSpedro 	struct umount *ump;
1276b077103dSkrw 	struct extfile_entry *xfentry;
1277d9ac8608Spedro 	struct file_entry *fentry;
1278d9ac8608Spedro 	void *icb;
1279d9ac8608Spedro 	struct icb_tag *tag;
1280d9ac8608Spedro 	uint32_t icblen = 0;
12811abdbfdeSderaadt 	daddr_t lsector;
1282d9ac8608Spedro 	int ad_offset, ad_num = 0;
1283b077103dSkrw 	int i, p_offset, l_ea, l_ad;
1284d9ac8608Spedro 
1285170e22feSpedro 	ump = up->u_ump;
1286b077103dSkrw 	xfentry = up->u_fentry;
1287b077103dSkrw 	fentry = (struct file_entry *)up->u_fentry;
1288d9ac8608Spedro 	tag = &fentry->icbtag;
1289b077103dSkrw 	if (udf_checktag(&xfentry->tag, TAGID_EXTFENTRY) == 0) {
1290b077103dSkrw 		l_ea = letoh32(xfentry->l_ea);
1291b077103dSkrw 		l_ad = letoh32(xfentry->l_ad);
1292b077103dSkrw 	} else {
1293b077103dSkrw 		l_ea = letoh32(fentry->l_ea);
1294b077103dSkrw 		l_ad = letoh32(fentry->l_ad);
1295b077103dSkrw 	}
1296d9ac8608Spedro 
1297d9ac8608Spedro 	switch (letoh16(tag->strat_type)) {
1298d9ac8608Spedro 	case 4:
1299d9ac8608Spedro 		break;
1300d9ac8608Spedro 
1301d9ac8608Spedro 	case 4096:
1302d9ac8608Spedro 		printf("Cannot deal with strategy4096 yet!\n");
1303d9ac8608Spedro 		return (ENODEV);
1304d9ac8608Spedro 
1305d9ac8608Spedro 	default:
1306d9ac8608Spedro 		printf("Unknown strategy type %d\n", tag->strat_type);
1307d9ac8608Spedro 		return (ENODEV);
1308d9ac8608Spedro 	}
1309d9ac8608Spedro 
1310d9ac8608Spedro 	switch (letoh16(tag->flags) & 0x7) {
1311d9ac8608Spedro 	case 0:
1312d9ac8608Spedro 		/*
1313d9ac8608Spedro 		 * The allocation descriptor field is filled with short_ad's.
1314d9ac8608Spedro 		 * If the offset is beyond the current extent, look for the
1315d9ac8608Spedro 		 * next extent.
1316d9ac8608Spedro 		 */
1317d9ac8608Spedro 		do {
1318d9ac8608Spedro 			offset -= icblen;
1319d9ac8608Spedro 			ad_offset = sizeof(struct short_ad) * ad_num;
1320b077103dSkrw 			if (ad_offset > l_ad) {
1321b077103dSkrw 				printf("SFile offset out of bounds (%d > %d)\n",
1322b077103dSkrw 				    ad_offset, l_ad);
1323d9ac8608Spedro 				return (EINVAL);
1324d9ac8608Spedro 			}
1325b077103dSkrw 
1326b077103dSkrw 			if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0)
1327b077103dSkrw 				icb = GETICB(short_ad, xfentry, l_ea + ad_offset);
1328b077103dSkrw 			else
1329b077103dSkrw 				icb = GETICB(short_ad, fentry, l_ea + ad_offset);
1330b077103dSkrw 
1331d9ac8608Spedro 			icblen = GETICBLEN(short_ad, icb);
1332d9ac8608Spedro 			ad_num++;
1333d9ac8608Spedro 		} while(offset >= icblen);
1334d9ac8608Spedro 
1335170e22feSpedro 		lsector = (offset  >> ump->um_bshift) +
1336de3bd4b9Skrw 		    letoh32(((struct short_ad *)(icb))->lb_num);
1337d9ac8608Spedro 
1338d9ac8608Spedro 		*max_size = GETICBLEN(short_ad, icb);
1339d9ac8608Spedro 
1340d9ac8608Spedro 		break;
1341d9ac8608Spedro 	case 1:
1342d9ac8608Spedro 		/*
1343d9ac8608Spedro 		 * The allocation descriptor field is filled with long_ad's
1344d9ac8608Spedro 		 * If the offset is beyond the current extent, look for the
1345d9ac8608Spedro 		 * next extent.
1346d9ac8608Spedro 		 */
1347d9ac8608Spedro 		do {
1348d9ac8608Spedro 			offset -= icblen;
1349d9ac8608Spedro 			ad_offset = sizeof(struct long_ad) * ad_num;
1350b077103dSkrw 			if (ad_offset > l_ad) {
1351b077103dSkrw 				printf("LFile offset out of bounds (%d > %d)\n",
1352b077103dSkrw 				    ad_offset, l_ad);
1353d9ac8608Spedro 				return (EINVAL);
1354d9ac8608Spedro 			}
1355b077103dSkrw 			if (udf_checktag(&up->u_fentry->tag, TAGID_EXTFENTRY) == 0)
1356b077103dSkrw 				icb = GETICB(long_ad, xfentry, l_ea + ad_offset);
1357b077103dSkrw 			else
1358b077103dSkrw 				icb = GETICB(long_ad, fentry, l_ea + ad_offset);
1359d9ac8608Spedro 			icblen = GETICBLEN(long_ad, icb);
1360d9ac8608Spedro 			ad_num++;
1361d9ac8608Spedro 		} while(offset >= icblen);
1362d9ac8608Spedro 
1363170e22feSpedro 		lsector = (offset >> ump->um_bshift) +
1364d9ac8608Spedro 		    letoh32(((struct long_ad *)(icb))->loc.lb_num);
1365d9ac8608Spedro 
1366d9ac8608Spedro 		*max_size = GETICBLEN(long_ad, icb);
1367d9ac8608Spedro 
1368d9ac8608Spedro 		break;
1369d9ac8608Spedro 	case 3:
1370d9ac8608Spedro 		/*
1371d9ac8608Spedro 		 * This type means that the file *data* is stored in the
1372d9ac8608Spedro 		 * allocation descriptor field of the file entry.
1373d9ac8608Spedro 		 */
1374d9ac8608Spedro 		*max_size = 0;
1375170e22feSpedro 		*sector = up->u_ino + ump->um_start;
1376d9ac8608Spedro 
1377d9ac8608Spedro 		return (UDF_INVALID_BMAP);
1378d9ac8608Spedro 	case 2:
1379d9ac8608Spedro 		/* DirectCD does not use extended_ad's */
1380d9ac8608Spedro 	default:
1381d9ac8608Spedro 		printf("Unsupported allocation descriptor %d\n",
1382d9ac8608Spedro 		       tag->flags & 0x7);
1383d9ac8608Spedro 		return (ENODEV);
1384d9ac8608Spedro 	}
1385d9ac8608Spedro 
1386170e22feSpedro 	*sector = lsector + ump->um_start;
1387d9ac8608Spedro 
1388d9ac8608Spedro 	/*
1389d9ac8608Spedro 	 * Check the sparing table.  Each entry represents the beginning of
1390d9ac8608Spedro 	 * a packet.
1391d9ac8608Spedro 	 */
1392170e22feSpedro 	if (ump->um_stbl != NULL) {
1393170e22feSpedro 		for (i = 0; i< ump->um_stbl_len; i++) {
1394d9ac8608Spedro 			p_offset =
1395170e22feSpedro 			    lsector - letoh32(ump->um_stbl->entries[i].org);
1396170e22feSpedro 			if ((p_offset < ump->um_psecs) && (p_offset >= 0)) {
1397d9ac8608Spedro 				*sector =
1398170e22feSpedro 				   letoh32(ump->um_stbl->entries[i].map) +
1399d9ac8608Spedro 				    p_offset;
1400d9ac8608Spedro 				break;
1401d9ac8608Spedro 			}
1402d9ac8608Spedro 		}
1403d9ac8608Spedro 	}
1404d9ac8608Spedro 
1405d9ac8608Spedro 	return (0);
1406d9ac8608Spedro }
1407