1189251Ssam/*-
2189251Ssam * SPDX-License-Identifier: BSD-2-Clause
3189251Ssam *
4189251Ssam * Copyright (c) 2001, 2002 Scott Long <scottl@freebsd.org>
5252726Srpaulo * All rights reserved.
6252726Srpaulo *
7189251Ssam * Redistribution and use in source and binary forms, with or without
8189251Ssam * modification, are permitted provided that the following conditions
9189251Ssam * are met:
10189251Ssam * 1. Redistributions of source code must retain the above copyright
11189251Ssam *    notice, this list of conditions and the following disclaimer.
12189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
13189251Ssam *    notice, this list of conditions and the following disclaimer in the
14189251Ssam *    documentation and/or other materials provided with the distribution.
15189251Ssam *
16189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19189251Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26189251Ssam * SUCH DAMAGE.
27189251Ssam */
28189251Ssam
29189251Ssam/* udf_vfsops.c */
30189251Ssam/* Implement the VFS side of things */
31189251Ssam
32189251Ssam/*
33189251Ssam * Ok, here's how it goes.  The UDF specs are pretty clear on how each data
34189251Ssam * structure is made up, but not very clear on how they relate to each other.
35189251Ssam * Here is the skinny... This demostrates a filesystem with one file in the
36189251Ssam * root directory.  Subdirectories are treated just as normal files, but they
37189251Ssam * have File Id Descriptors of their children as their file data.  As for the
38189251Ssam * Anchor Volume Descriptor Pointer, it can exist in two of the following three
39189251Ssam * places: sector 256, sector n (the max sector of the disk), or sector
40189251Ssam * n - 256.  It's a pretty good bet that one will exist at sector 256 though.
41189251Ssam * One caveat is unclosed CD media.  For that, sector 256 cannot be written,
42189251Ssam * so the Anchor Volume Descriptor Pointer can exist at sector 512 until the
43189251Ssam * media is closed.
44189251Ssam *
45189251Ssam *  Sector:
46189251Ssam *     256:
47189251Ssam *       n: Anchor Volume Descriptor Pointer
48189251Ssam * n - 256:	|
49189251Ssam *		|
50189251Ssam *		|-->Main Volume Descriptor Sequence
51189251Ssam *			|	|
52189251Ssam *			|	|
53189251Ssam *			|	|-->Logical Volume Descriptor
54189251Ssam *			|			  |
55189251Ssam *			|-->Partition Descriptor  |
56189251Ssam *				|		  |
57189251Ssam *				|		  |
58189251Ssam *				|-->Fileset Descriptor
59189251Ssam *					|
60189251Ssam *					|
61189251Ssam *					|-->Root Dir File Entry
62189251Ssam *						|
63189251Ssam *						|
64189251Ssam *						|-->File data:
65189251Ssam *						    File Id Descriptor
66189251Ssam *							|
67189251Ssam *							|
68189251Ssam *							|-->File Entry
69189251Ssam *								|
70189251Ssam *								|
71189251Ssam *								|-->File data
72189251Ssam */
73189251Ssam#include <sys/types.h>
74189251Ssam#include <sys/param.h>
75189251Ssam#include <sys/systm.h>
76189251Ssam#include <sys/uio.h>
77189251Ssam#include <sys/bio.h>
78189251Ssam#include <sys/buf.h>
79189251Ssam#include <sys/conf.h>
80189251Ssam#include <sys/dirent.h>
81189251Ssam#include <sys/fcntl.h>
82189251Ssam#include <sys/iconv.h>
83189251Ssam#include <sys/kernel.h>
84189251Ssam#include <sys/malloc.h>
85189251Ssam#include <sys/mount.h>
86189251Ssam#include <sys/namei.h>
87189251Ssam#include <sys/priv.h>
88189251Ssam#include <sys/proc.h>
89189251Ssam#include <sys/queue.h>
90189251Ssam#include <sys/vnode.h>
91189251Ssam#include <sys/endian.h>
92189251Ssam
93189251Ssam#include <geom/geom.h>
94189251Ssam#include <geom/geom_vfs.h>
95189251Ssam
96189251Ssam#include <vm/uma.h>
97189251Ssam
98189251Ssam#include <fs/udf/ecma167-udf.h>
99189251Ssam#include <fs/udf/osta.h>
100189251Ssam#include <fs/udf/udf.h>
101189251Ssam#include <fs/udf/udf_mount.h>
102189251Ssam
103189251Ssamstatic MALLOC_DEFINE(M_UDFMOUNT, "udf_mount", "UDF mount structure");
104189251SsamMALLOC_DEFINE(M_UDFFENTRY, "udf_fentry", "UDF file entry structure");
105189251Ssam
106189251Ssamstruct iconv_functions *udf_iconv = NULL;
107189251Ssam
108189251Ssam/* Zones */
109189251Ssamuma_zone_t udf_zone_trans = NULL;
110189251Ssamuma_zone_t udf_zone_node = NULL;
111189251Ssamuma_zone_t udf_zone_ds = NULL;
112189251Ssam
113189251Ssamstatic vfs_init_t      udf_init;
114189251Ssamstatic vfs_uninit_t    udf_uninit;
115189251Ssamstatic vfs_mount_t     udf_mount;
116189251Ssamstatic vfs_root_t      udf_root;
117189251Ssamstatic vfs_statfs_t    udf_statfs;
118189251Ssamstatic vfs_unmount_t   udf_unmount;
119189251Ssamstatic vfs_fhtovp_t	udf_fhtovp;
120189251Ssam
121189251Ssamstatic int udf_find_partmaps(struct udf_mnt *, struct logvol_desc *);
122189251Ssam
123189251Ssamstatic struct vfsops udf_vfsops = {
124189251Ssam	.vfs_fhtovp =		udf_fhtovp,
125189251Ssam	.vfs_init =		udf_init,
126189251Ssam	.vfs_mount =		udf_mount,
127189251Ssam	.vfs_root =		udf_root,
128189251Ssam	.vfs_statfs =		udf_statfs,
129189251Ssam	.vfs_uninit =		udf_uninit,
130189251Ssam	.vfs_unmount =		udf_unmount,
131189251Ssam	.vfs_vget =		udf_vget,
132189251Ssam};
133189251SsamVFS_SET(udf_vfsops, udf, VFCF_READONLY);
134189251Ssam
135189251SsamMODULE_VERSION(udf, 1);
136189251Ssam
137189251Ssamstatic int udf_mountfs(struct vnode *, struct mount *);
138189251Ssam
139189251Ssamstatic int
140189251Ssamudf_init(struct vfsconf *foo)
141189251Ssam{
142189251Ssam
143189251Ssam	udf_zone_trans = uma_zcreate("UDF translation buffer, zone", MAXNAMLEN *
144189251Ssam	    sizeof(unicode_t), NULL, NULL, NULL, NULL, 0, 0);
145189251Ssam
146189251Ssam	udf_zone_node = uma_zcreate("UDF Node zone", sizeof(struct udf_node),
147189251Ssam	    NULL, NULL, NULL, NULL, 0, 0);
148189251Ssam
149189251Ssam	udf_zone_ds = uma_zcreate("UDF Dirstream zone",
150189251Ssam	    sizeof(struct udf_dirstream), NULL, NULL, NULL, NULL, 0, 0);
151189251Ssam
152189251Ssam	return 0;
153189251Ssam}
154189251Ssam
155189251Ssamstatic int
156189251Ssamudf_uninit(struct vfsconf *foo)
157189251Ssam{
158189251Ssam
159189251Ssam	if (udf_zone_trans != NULL) {
160189251Ssam		uma_zdestroy(udf_zone_trans);
161189251Ssam		udf_zone_trans = NULL;
162189251Ssam	}
163189251Ssam
164189251Ssam	if (udf_zone_node != NULL) {
165189251Ssam		uma_zdestroy(udf_zone_node);
166189251Ssam		udf_zone_node = NULL;
167189251Ssam	}
168189251Ssam
169189251Ssam	if (udf_zone_ds != NULL) {
170189251Ssam		uma_zdestroy(udf_zone_ds);
171189251Ssam		udf_zone_ds = NULL;
172189251Ssam	}
173189251Ssam
174189251Ssam	return (0);
175189251Ssam}
176189251Ssam
177189251Ssamstatic int
178189251Ssamudf_mount(struct mount *mp)
179189251Ssam{
180189251Ssam	struct vnode *devvp;	/* vnode of the mount device */
181189251Ssam	struct thread *td;
182189251Ssam	struct udf_mnt *imp = NULL;
183189251Ssam	struct vfsoptlist *opts;
184189251Ssam	char *fspec, *cs_disk, *cs_local;
185189251Ssam	int error, len, *udf_flags;
186189251Ssam	struct nameidata nd, *ndp = &nd;
187189251Ssam
188189251Ssam	td = curthread;
189189251Ssam	opts = mp->mnt_optnew;
190189251Ssam
191189251Ssam	/*
192189251Ssam	 * Unconditionally mount as read-only.
193189251Ssam	 */
194189251Ssam	MNT_ILOCK(mp);
195189251Ssam	mp->mnt_flag |= MNT_RDONLY;
196189251Ssam	MNT_IUNLOCK(mp);
197189251Ssam
198189251Ssam	/*
199189251Ssam	 * No root filesystem support.  Probably not a big deal, since the
200189251Ssam	 * bootloader doesn't understand UDF.
201189251Ssam	 */
202189251Ssam	if (mp->mnt_flag & MNT_ROOTFS)
203189251Ssam		return (ENOTSUP);
204189251Ssam
205189251Ssam	fspec = NULL;
206189251Ssam	error = vfs_getopt(opts, "from", (void **)&fspec, &len);
207189251Ssam	if (!error && fspec[len - 1] != '\0')
208189251Ssam		return (EINVAL);
209189251Ssam
210189251Ssam	if (mp->mnt_flag & MNT_UPDATE) {
211189251Ssam		return (0);
212189251Ssam	}
213189251Ssam
214189251Ssam	/* Check that the mount device exists */
215189251Ssam	if (fspec == NULL)
216189251Ssam		return (EINVAL);
217189251Ssam	NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspec);
218189251Ssam	if ((error = namei(ndp)))
219189251Ssam		return (error);
220189251Ssam	NDFREE_PNBUF(ndp);
221189251Ssam	devvp = ndp->ni_vp;
222189251Ssam
223189251Ssam	if (!vn_isdisk_error(devvp, &error)) {
224189251Ssam		vput(devvp);
225189251Ssam		return (error);
226189251Ssam	}
227189251Ssam
228189251Ssam	/* Check the access rights on the mount device */
229189251Ssam	error = VOP_ACCESS(devvp, VREAD, td->td_ucred, td);
230189251Ssam	if (error)
231189251Ssam		error = priv_check(td, PRIV_VFS_MOUNT_PERM);
232189251Ssam	if (error) {
233189251Ssam		vput(devvp);
234189251Ssam		return (error);
235189251Ssam	}
236189251Ssam
237189251Ssam	if ((error = udf_mountfs(devvp, mp))) {
238189251Ssam		vrele(devvp);
239189251Ssam		return (error);
240189251Ssam	}
241189251Ssam
242189251Ssam	imp = VFSTOUDFFS(mp);
243189251Ssam
244189251Ssam	udf_flags = NULL;
245189251Ssam	error = vfs_getopt(opts, "flags", (void **)&udf_flags, &len);
246189251Ssam	if (error || len != sizeof(int))
247189251Ssam		return (EINVAL);
248189251Ssam	imp->im_flags = *udf_flags;
249189251Ssam
250189251Ssam	if (imp->im_flags & UDFMNT_KICONV && udf_iconv) {
251189251Ssam		cs_disk = NULL;
252189251Ssam		error = vfs_getopt(opts, "cs_disk", (void **)&cs_disk, &len);
253189251Ssam		if (!error && cs_disk[len - 1] != '\0')
254189251Ssam			return (EINVAL);
255189251Ssam		cs_local = NULL;
256189251Ssam		error = vfs_getopt(opts, "cs_local", (void **)&cs_local, &len);
257189251Ssam		if (!error && cs_local[len - 1] != '\0')
258189251Ssam			return (EINVAL);
259189251Ssam		udf_iconv->open(cs_local, cs_disk, &imp->im_d2l);
260189251Ssam#if 0
261189251Ssam		udf_iconv->open(cs_disk, cs_local, &imp->im_l2d);
262189251Ssam#endif
263189251Ssam	}
264189251Ssam
265189251Ssam	vfs_mountedfrom(mp, fspec);
266189251Ssam	return 0;
267189251Ssam};
268189251Ssam
269189251Ssam/*
270189251Ssam * Check the descriptor tag for both the correct id and correct checksum.
271189251Ssam * Return zero if all is good, EINVAL if not.
272189251Ssam */
273189251Ssamint
274189251Ssamudf_checktag(struct desc_tag *tag, uint16_t id)
275189251Ssam{
276189251Ssam	uint8_t *itag;
277189251Ssam	uint8_t i, cksum = 0;
278189251Ssam
279189251Ssam	itag = (uint8_t *)tag;
280189251Ssam
281189251Ssam	if (le16toh(tag->id) != id)
282189251Ssam		return (EINVAL);
283189251Ssam
284189251Ssam	for (i = 0; i < 16; i++)
285189251Ssam		cksum = cksum + itag[i];
286189251Ssam	cksum = cksum - itag[4];
287189251Ssam
288189251Ssam	if (cksum == tag->cksum)
289189251Ssam		return (0);
290189251Ssam
291189251Ssam	return (EINVAL);
292189251Ssam}
293189251Ssam
294189251Ssamstatic int
295189251Ssamudf_mountfs(struct vnode *devvp, struct mount *mp)
296189251Ssam{
297189251Ssam	struct buf *bp = NULL;
298189251Ssam	struct cdev *dev;
299189251Ssam	struct anchor_vdp avdp;
300189251Ssam	struct udf_mnt *udfmp = NULL;
301189251Ssam	struct part_desc *pd;
302189251Ssam	struct logvol_desc *lvd;
303189251Ssam	struct fileset_desc *fsd;
304189251Ssam	struct file_entry *root_fentry;
305189251Ssam	uint32_t sector, size, mvds_start, mvds_end;
306189251Ssam	uint32_t logical_secsize;
307189251Ssam	uint32_t fsd_offset = 0;
308189251Ssam	uint16_t part_num = 0, fsd_part = 0;
309189251Ssam	int error = EINVAL;
310189251Ssam	int logvol_found = 0, part_found = 0, fsd_found = 0;
311189251Ssam	int bsize;
312189251Ssam	struct g_consumer *cp;
313189251Ssam	struct bufobj *bo;
314189251Ssam
315189251Ssam	dev = devvp->v_rdev;
316189251Ssam	dev_ref(dev);
317189251Ssam	g_topology_lock();
318189251Ssam	error = g_vfs_open(devvp, &cp, "udf", 0);
319189251Ssam	g_topology_unlock();
320189251Ssam	VOP_UNLOCK(devvp);
321189251Ssam	if (error)
322189251Ssam		goto bail;
323189251Ssam
324189251Ssam	bo = &devvp->v_bufobj;
325189251Ssam
326189251Ssam	if (devvp->v_rdev->si_iosize_max != 0)
327189251Ssam		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
328189251Ssam	if (mp->mnt_iosize_max > maxphys)
329189251Ssam		mp->mnt_iosize_max = maxphys;
330189251Ssam
331189251Ssam	/* XXX: should be M_WAITOK */
332189251Ssam	udfmp = malloc(sizeof(struct udf_mnt), M_UDFMOUNT,
333189251Ssam	    M_NOWAIT | M_ZERO);
334189251Ssam	if (udfmp == NULL) {
335189251Ssam		printf("Cannot allocate UDF mount struct\n");
336189251Ssam		error = ENOMEM;
337189251Ssam		goto bail;
338189251Ssam	}
339189251Ssam
340189251Ssam	mp->mnt_data = udfmp;
341189251Ssam	mp->mnt_stat.f_fsid.val[0] = dev2udev(devvp->v_rdev);
342189251Ssam	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
343189251Ssam	MNT_ILOCK(mp);
344189251Ssam	mp->mnt_flag |= MNT_LOCAL;
345189251Ssam	mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED;
346189251Ssam	MNT_IUNLOCK(mp);
347189251Ssam	udfmp->im_mountp = mp;
348189251Ssam	udfmp->im_dev = dev;
349189251Ssam	udfmp->im_devvp = devvp;
350189251Ssam	udfmp->im_d2l = NULL;
351189251Ssam	udfmp->im_cp = cp;
352189251Ssam	udfmp->im_bo = bo;
353189251Ssam
354189251Ssam#if 0
355189251Ssam	udfmp->im_l2d = NULL;
356189251Ssam#endif
357189251Ssam	/*
358189251Ssam	 * The UDF specification defines a logical sectorsize of 2048
359189251Ssam	 * for DVD media.
360189251Ssam	 */
361189251Ssam	logical_secsize = 2048;
362189251Ssam
363189251Ssam	if (((logical_secsize % cp->provider->sectorsize) != 0) ||
364189251Ssam	    (logical_secsize < cp->provider->sectorsize)) {
365189251Ssam		error = EINVAL;
366189251Ssam		goto bail;
367189251Ssam	}
368189251Ssam
369189251Ssam	bsize = cp->provider->sectorsize;
370189251Ssam
371189251Ssam	/*
372189251Ssam	 * Get the Anchor Volume Descriptor Pointer from sector 256.
373189251Ssam	 * XXX Should also check sector n - 256, n, and 512.
374189251Ssam	 */
375189251Ssam	sector = 256;
376189251Ssam	if ((error = bread(devvp, sector * btodb(logical_secsize), bsize,
377189251Ssam			   NOCRED, &bp)) != 0)
378189251Ssam		goto bail;
379189251Ssam	if ((error = udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR)))
380189251Ssam		goto bail;
381189251Ssam
382189251Ssam	bcopy(bp->b_data, &avdp, sizeof(struct anchor_vdp));
383189251Ssam	brelse(bp);
384189251Ssam	bp = NULL;
385189251Ssam
386189251Ssam	/*
387189251Ssam	 * Extract the Partition Descriptor and Logical Volume Descriptor
388189251Ssam	 * from the Volume Descriptor Sequence.
389189251Ssam	 * XXX Should we care about the partition type right now?
390189251Ssam	 * XXX What about multiple partitions?
391189251Ssam	 */
392189251Ssam	mvds_start = le32toh(avdp.main_vds_ex.loc);
393189251Ssam	mvds_end = mvds_start + (le32toh(avdp.main_vds_ex.len) - 1) / bsize;
394189251Ssam	for (sector = mvds_start; sector < mvds_end; sector++) {
395189251Ssam		if ((error = bread(devvp, sector * btodb(logical_secsize),
396189251Ssam				   bsize, NOCRED, &bp)) != 0) {
397189251Ssam			printf("Can't read sector %d of VDS\n", sector);
398189251Ssam			goto bail;
399189251Ssam		}
400189251Ssam		lvd = (struct logvol_desc *)bp->b_data;
401189251Ssam		if (!udf_checktag(&lvd->tag, TAGID_LOGVOL)) {
402189251Ssam			udfmp->bsize = le32toh(lvd->lb_size);
403189251Ssam			if (udfmp->bsize < 0 || udfmp->bsize > maxbcachebuf) {
404189251Ssam				printf("lvd block size %d\n", udfmp->bsize);
405189251Ssam				error = EINVAL;
406189251Ssam				goto bail;
407189251Ssam			}
408189251Ssam			udfmp->bmask = udfmp->bsize - 1;
409189251Ssam			udfmp->bshift = ffs(udfmp->bsize) - 1;
410189251Ssam			fsd_part = le16toh(lvd->_lvd_use.fsd_loc.loc.part_num);
411189251Ssam			fsd_offset = le32toh(lvd->_lvd_use.fsd_loc.loc.lb_num);
412189251Ssam			if (udf_find_partmaps(udfmp, lvd))
413189251Ssam				break;
414189251Ssam			logvol_found = 1;
415189251Ssam		}
416189251Ssam		pd = (struct part_desc *)bp->b_data;
417189251Ssam		if (!udf_checktag(&pd->tag, TAGID_PARTITION)) {
418189251Ssam			part_found = 1;
419189251Ssam			part_num = le16toh(pd->part_num);
420189251Ssam			udfmp->part_len = le32toh(pd->part_len);
421189251Ssam			udfmp->part_start = le32toh(pd->start_loc);
422189251Ssam		}
423189251Ssam
424189251Ssam		brelse(bp);
425252726Srpaulo		bp = NULL;
426252726Srpaulo		if ((part_found) && (logvol_found))
427252726Srpaulo			break;
428252726Srpaulo	}
429252726Srpaulo
430252726Srpaulo	if (!part_found || !logvol_found) {
431189251Ssam		error = EINVAL;
432189251Ssam		goto bail;
433189251Ssam	}
434189251Ssam
435189251Ssam	if (fsd_part != part_num) {
436189251Ssam		printf("FSD does not lie within the partition!\n");
437189251Ssam		error = EINVAL;
438189251Ssam		goto bail;
439189251Ssam	}
440189251Ssam
441189251Ssam	/*
442189251Ssam	 * Grab the Fileset Descriptor
443189251Ssam	 * Thanks to Chuck McCrobie <mccrobie@cablespeed.com> for pointing
444189251Ssam	 * me in the right direction here.
445189251Ssam	 */
446189251Ssam	sector = udfmp->part_start + fsd_offset;
447189251Ssam	if ((error = RDSECTOR(devvp, sector, udfmp->bsize, &bp)) != 0) {
448189251Ssam		printf("Cannot read sector %d of FSD\n", sector);
449189251Ssam		goto bail;
450189251Ssam	}
451189251Ssam	fsd = (struct fileset_desc *)bp->b_data;
452189251Ssam	if (!udf_checktag(&fsd->tag, TAGID_FSD)) {
453189251Ssam		fsd_found = 1;
454189251Ssam		bcopy(&fsd->rootdir_icb, &udfmp->root_icb,
455189251Ssam		    sizeof(struct long_ad));
456189251Ssam	}
457189251Ssam
458189251Ssam	brelse(bp);
459189251Ssam	bp = NULL;
460189251Ssam
461189251Ssam	if (!fsd_found) {
462189251Ssam		printf("Couldn't find the fsd\n");
463189251Ssam		error = EINVAL;
464189251Ssam		goto bail;
465189251Ssam	}
466189251Ssam
467189251Ssam	/*
468189251Ssam	 * Find the file entry for the root directory.
469189251Ssam	 */
470189251Ssam	sector = le32toh(udfmp->root_icb.loc.lb_num) + udfmp->part_start;
471189251Ssam	size = le32toh(udfmp->root_icb.len);
472189251Ssam	if (size < UDF_FENTRY_SIZE) {
473189251Ssam		printf("Invalid root directory file entry length %u\n",
474189251Ssam		    size);
475189251Ssam		goto bail;
476189251Ssam	}
477189251Ssam	if ((error = udf_readdevblks(udfmp, sector, size, &bp)) != 0) {
478189251Ssam		printf("Cannot read sector %d\n", sector);
479189251Ssam		goto bail;
480189251Ssam	}
481214734Srpaulo
482189251Ssam	root_fentry = (struct file_entry *)bp->b_data;
483214734Srpaulo	if ((error = udf_checktag(&root_fentry->tag, TAGID_FENTRY))) {
484214734Srpaulo		printf("Invalid root file entry!\n");
485189251Ssam		goto bail;
486189251Ssam	}
487189251Ssam
488189251Ssam	brelse(bp);
489189251Ssam	bp = NULL;
490189251Ssam
491189251Ssam	return 0;
492189251Ssam
493189251Ssambail:
494189251Ssam	if (udfmp != NULL)
495189251Ssam		free(udfmp, M_UDFMOUNT);
496189251Ssam	if (bp != NULL)
497189251Ssam		brelse(bp);
498252726Srpaulo	if (cp != NULL) {
499189251Ssam		g_topology_lock();
500189251Ssam		g_vfs_close(cp);
501189251Ssam		g_topology_unlock();
502214734Srpaulo	}
503189251Ssam	dev_rel(dev);
504214734Srpaulo	return error;
505214734Srpaulo};
506189251Ssam
507189251Ssamstatic int
508214734Srpauloudf_unmount(struct mount *mp, int mntflags)
509214734Srpaulo{
510214734Srpaulo	struct udf_mnt *udfmp;
511189251Ssam	int error, flags = 0;
512189251Ssam
513189251Ssam	udfmp = VFSTOUDFFS(mp);
514189251Ssam
515214734Srpaulo	if (mntflags & MNT_FORCE)
516214734Srpaulo		flags |= FORCECLOSE;
517189251Ssam
518189251Ssam	if ((error = vflush(mp, 0, flags, curthread)))
519189251Ssam		return (error);
520214734Srpaulo
521214734Srpaulo	if (udfmp->im_flags & UDFMNT_KICONV && udf_iconv) {
522189251Ssam		if (udfmp->im_d2l)
523189251Ssam			udf_iconv->close(udfmp->im_d2l);
524189251Ssam#if 0
525214734Srpaulo		if (udfmp->im_l2d)
526214734Srpaulo			udf_iconv->close(udfmp->im_l2d);
527189251Ssam#endif
528189251Ssam	}
529189251Ssam
530189251Ssam	g_topology_lock();
531189251Ssam	g_vfs_close(udfmp->im_cp);
532189251Ssam	g_topology_unlock();
533189251Ssam	vrele(udfmp->im_devvp);
534189251Ssam	dev_rel(udfmp->im_dev);
535189251Ssam
536189251Ssam	if (udfmp->s_table != NULL)
537189251Ssam		free(udfmp->s_table, M_UDFMOUNT);
538189251Ssam
539189251Ssam	free(udfmp, M_UDFMOUNT);
540189251Ssam
541189251Ssam	mp->mnt_data = NULL;
542189251Ssam	return (0);
543189251Ssam}
544189251Ssam
545189251Ssamstatic int
546189251Ssamudf_root(struct mount *mp, int flags, struct vnode **vpp)
547189251Ssam{
548189251Ssam	struct udf_mnt *udfmp;
549189251Ssam	ino_t id;
550189251Ssam
551189251Ssam	udfmp = VFSTOUDFFS(mp);
552189251Ssam
553189251Ssam	id = udf_getid(&udfmp->root_icb);
554189251Ssam
555189251Ssam	return (udf_vget(mp, id, flags, vpp));
556189251Ssam}
557189251Ssam
558189251Ssamstatic int
559189251Ssamudf_statfs(struct mount *mp, struct statfs *sbp)
560189251Ssam{
561189251Ssam	struct udf_mnt *udfmp;
562189251Ssam
563189251Ssam	udfmp = VFSTOUDFFS(mp);
564189251Ssam
565189251Ssam	sbp->f_bsize = udfmp->bsize;
566189251Ssam	sbp->f_iosize = udfmp->bsize;
567189251Ssam	sbp->f_blocks = udfmp->part_len;
568189251Ssam	sbp->f_bfree = 0;
569189251Ssam	sbp->f_bavail = 0;
570189251Ssam	sbp->f_files = 0;
571189251Ssam	sbp->f_ffree = 0;
572189251Ssam	return 0;
573189251Ssam}
574189251Ssam
575189251Ssamint
576189251Ssamudf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
577189251Ssam{
578189251Ssam	struct buf *bp;
579189251Ssam	struct vnode *devvp;
580189251Ssam	struct udf_mnt *udfmp;
581189251Ssam	struct thread *td;
582189251Ssam	struct vnode *vp;
583189251Ssam	struct udf_node *unode;
584189251Ssam	struct file_entry *fe;
585189251Ssam	uint32_t lea, lad;
586189251Ssam	int error, sector, size;
587189251Ssam
588189251Ssam	error = vfs_hash_get(mp, ino, flags, curthread, vpp, NULL, NULL);
589189251Ssam	if (error || *vpp != NULL)
590189251Ssam		return (error);
591189251Ssam
592189251Ssam	/*
593189251Ssam	 * We must promote to an exclusive lock for vnode creation.  This
594189251Ssam	 * can happen if lookup is passed LOCKSHARED.
595189251Ssam 	 */
596189251Ssam	if ((flags & LK_TYPE_MASK) == LK_SHARED) {
597189251Ssam		flags &= ~LK_TYPE_MASK;
598189251Ssam		flags |= LK_EXCLUSIVE;
599189251Ssam	}
600189251Ssam
601189251Ssam	/*
602189251Ssam	 * We do not lock vnode creation as it is believed to be too
603189251Ssam	 * expensive for such rare case as simultaneous creation of vnode
604189251Ssam	 * for same ino by different processes. We just allow them to race
605189251Ssam	 * and check later to decide who wins. Let the race begin!
606189251Ssam	 */
607189251Ssam
608189251Ssam	td = curthread;
609189251Ssam	udfmp = VFSTOUDFFS(mp);
610189251Ssam
611189251Ssam	unode = uma_zalloc(udf_zone_node, M_WAITOK | M_ZERO);
612189251Ssam
613189251Ssam	if ((error = udf_allocv(mp, &vp, td))) {
614189251Ssam		printf("Error from udf_allocv\n");
615189251Ssam		uma_zfree(udf_zone_node, unode);
616189251Ssam		return (error);
617189251Ssam	}
618189251Ssam
619189251Ssam	unode->i_vnode = vp;
620189251Ssam	unode->hash_id = ino;
621189251Ssam	unode->udfmp = udfmp;
622189251Ssam	vp->v_data = unode;
623189251Ssam
624189251Ssam	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
625189251Ssam	error = insmntque(vp, mp);
626189251Ssam	if (error != 0) {
627189251Ssam		uma_zfree(udf_zone_node, unode);
628189251Ssam		return (error);
629189251Ssam	}
630189251Ssam	error = vfs_hash_insert(vp, ino, flags, td, vpp, NULL, NULL);
631189251Ssam	if (error || *vpp != NULL)
632189251Ssam		return (error);
633189251Ssam
634189251Ssam	/*
635189251Ssam	 * Copy in the file entry.  Per the spec, the size can only be 1 block.
636189251Ssam	 */
637189251Ssam	sector = ino + udfmp->part_start;
638189251Ssam	devvp = udfmp->im_devvp;
639189251Ssam	if ((error = RDSECTOR(devvp, sector, udfmp->bsize, &bp)) != 0) {
640189251Ssam		printf("Cannot read sector %d\n", sector);
641189251Ssam		goto error;
642189251Ssam	}
643189251Ssam
644189251Ssam	/*
645189251Ssam	 * File entry length validation.
646189251Ssam	 */
647189251Ssam	fe = (struct file_entry *)bp->b_data;
648189251Ssam	if (udf_checktag(&fe->tag, TAGID_FENTRY)) {
649189251Ssam		printf("Invalid file entry!\n");
650189251Ssam		error = ENOMEM;
651189251Ssam		goto error;
652189251Ssam	}
653189251Ssam	lea = le32toh(fe->l_ea);
654189251Ssam	lad = le32toh(fe->l_ad);
655189251Ssam	if (lea > udfmp->bsize || lad > udfmp->bsize) {
656189251Ssam		printf("Invalid EA and AD lengths %u, %u\n", lea, lad);
657189251Ssam		error = EIO;
658189251Ssam		goto error;
659189251Ssam	}
660189251Ssam	size = UDF_FENTRY_SIZE + lea + lad;
661189251Ssam	if (size > udfmp->bsize) {
662189251Ssam		printf("Invalid file entry size %u\n", size);
663189251Ssam		error = EIO;
664189251Ssam		goto error;
665189251Ssam	}
666189251Ssam
667189251Ssam	unode->fentry = malloc(size, M_UDFFENTRY, M_NOWAIT | M_ZERO);
668189251Ssam	if (unode->fentry == NULL) {
669189251Ssam		printf("Cannot allocate file entry block\n");
670189251Ssam		error = ENOMEM;
671189251Ssam		goto error;
672189251Ssam	}
673189251Ssam
674189251Ssam	bcopy(bp->b_data, unode->fentry, size);
675189251Ssam
676189251Ssam	brelse(bp);
677189251Ssam	bp = NULL;
678189251Ssam
679189251Ssam	switch (unode->fentry->icbtag.file_type) {
680189251Ssam	default:
681189251Ssam		vp->v_type = VBAD;
682189251Ssam		break;
683189251Ssam	case 4:
684189251Ssam		vp->v_type = VDIR;
685189251Ssam		break;
686189251Ssam	case 5:
687189251Ssam		vp->v_type = VREG;
688189251Ssam		break;
689189251Ssam	case 6:
690189251Ssam		vp->v_type = VBLK;
691189251Ssam		break;
692189251Ssam	case 7:
693189251Ssam		vp->v_type = VCHR;
694189251Ssam		break;
695189251Ssam	case 9:
696189251Ssam		vp->v_type = VFIFO;
697189251Ssam		vp->v_op = &udf_fifoops;
698189251Ssam		break;
699189251Ssam	case 10:
700189251Ssam		vp->v_type = VSOCK;
701189251Ssam		break;
702189251Ssam	case 12:
703189251Ssam		vp->v_type = VLNK;
704189251Ssam		break;
705189251Ssam	}
706189251Ssam
707189251Ssam	if (vp->v_type != VFIFO)
708189251Ssam		VN_LOCK_ASHARE(vp);
709189251Ssam
710189251Ssam	if (ino == udf_getid(&udfmp->root_icb))
711189251Ssam		vp->v_vflag |= VV_ROOT;
712189251Ssam
713189251Ssam	vn_set_state(vp, VSTATE_CONSTRUCTED);
714189251Ssam	*vpp = vp;
715189251Ssam
716189251Ssam	return (0);
717189251Ssam
718189251Ssamerror:
719189251Ssam	vgone(vp);
720189251Ssam	vput(vp);
721189251Ssam	brelse(bp);
722189251Ssam	*vpp = NULL;
723189251Ssam	return (error);
724189251Ssam}
725189251Ssam
726189251Ssamstatic int
727189251Ssamudf_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
728189251Ssam{
729189251Ssam	struct ifid *ifhp;
730189251Ssam	struct vnode *nvp;
731189251Ssam	struct udf_node *np;
732189251Ssam	off_t fsize;
733189251Ssam	int error;
734189251Ssam
735189251Ssam	ifhp = (struct ifid *)fhp;
736189251Ssam
737189251Ssam	if ((error = VFS_VGET(mp, ifhp->ifid_ino, LK_EXCLUSIVE, &nvp)) != 0) {
738189251Ssam		*vpp = NULLVP;
739189251Ssam		return (error);
740189251Ssam	}
741189251Ssam
742189251Ssam	np = VTON(nvp);
743189251Ssam	fsize = le64toh(np->fentry->inf_len);
744189251Ssam
745189251Ssam	*vpp = nvp;
746189251Ssam	vnode_create_vobject(*vpp, fsize, curthread);
747189251Ssam	return (0);
748189251Ssam}
749189251Ssam
750189251Ssamstatic int
751189251Ssamudf_find_partmaps(struct udf_mnt *udfmp, struct logvol_desc *lvd)
752189251Ssam{
753189251Ssam	struct part_map_spare *pms;
754189251Ssam	struct regid *pmap_id;
755189251Ssam	struct buf *bp;
756189251Ssam	unsigned char regid_id[UDF_REGID_ID_SIZE + 1];
757189251Ssam	int i, k, ptype, psize, error;
758189251Ssam	uint8_t *pmap = (uint8_t *) &lvd->maps[0];
759189251Ssam
760189251Ssam	for (i = 0; i < le32toh(lvd->n_pm); i++) {
761189251Ssam		ptype = pmap[0];
762189251Ssam		psize = pmap[1];
763189251Ssam		if (((ptype != 1) && (ptype != 2)) ||
764189251Ssam		    ((psize != UDF_PMAP_TYPE1_SIZE) &&
765189251Ssam		     (psize != UDF_PMAP_TYPE2_SIZE))) {
766189251Ssam			printf("Invalid partition map found\n");
767189251Ssam			return (1);
768189251Ssam		}
769189251Ssam
770189251Ssam		if (ptype == 1) {
771189251Ssam			/* Type 1 map.  We don't care */
772189251Ssam			pmap += UDF_PMAP_TYPE1_SIZE;
773189251Ssam			continue;
774189251Ssam		}
775189251Ssam
776189251Ssam		/* Type 2 map.  Gotta find out the details */
777189251Ssam		pmap_id = (struct regid *)&pmap[4];
778189251Ssam		bzero(&regid_id[0], UDF_REGID_ID_SIZE);
779189251Ssam		bcopy(&pmap_id->id[0], &regid_id[0], UDF_REGID_ID_SIZE);
780189251Ssam
781189251Ssam		if (bcmp(&regid_id[0], "*UDF Sparable Partition",
782189251Ssam		    UDF_REGID_ID_SIZE)) {
783189251Ssam			printf("Unsupported partition map: %s\n", &regid_id[0]);
784189251Ssam			return (1);
785189251Ssam		}
786189251Ssam
787189251Ssam		pms = (struct part_map_spare *)pmap;
788189251Ssam		pmap += UDF_PMAP_TYPE2_SIZE;
789189251Ssam		udfmp->s_table = malloc(le32toh(pms->st_size),
790189251Ssam		    M_UDFMOUNT, M_NOWAIT | M_ZERO);
791189251Ssam		if (udfmp->s_table == NULL)
792189251Ssam			return (ENOMEM);
793189251Ssam
794189251Ssam		/* Calculate the number of sectors per packet. */
795189251Ssam		/* XXX Logical or physical? */
796189251Ssam		udfmp->p_sectors = le16toh(pms->packet_len) / udfmp->bsize;
797189251Ssam
798189251Ssam		/*
799189251Ssam		 * XXX If reading the first Sparing Table fails, should look
800189251Ssam		 * for another table.
801189251Ssam		 */
802189251Ssam		if ((error = udf_readdevblks(udfmp, le32toh(pms->st_loc[0]),
803189251Ssam					   le32toh(pms->st_size), &bp)) != 0) {
804189251Ssam			if (bp != NULL)
805189251Ssam				brelse(bp);
806189251Ssam			printf("Failed to read Sparing Table at sector %d\n",
807189251Ssam			    le32toh(pms->st_loc[0]));
808189251Ssam			free(udfmp->s_table, M_UDFMOUNT);
809189251Ssam			return (error);
810189251Ssam		}
811189251Ssam		bcopy(bp->b_data, udfmp->s_table, le32toh(pms->st_size));
812189251Ssam		brelse(bp);
813189251Ssam
814189251Ssam		if (udf_checktag(&udfmp->s_table->tag, 0)) {
815189251Ssam			printf("Invalid sparing table found\n");
816189251Ssam			free(udfmp->s_table, M_UDFMOUNT);
817189251Ssam			return (EINVAL);
818189251Ssam		}
819189251Ssam
820189251Ssam		/* See how many valid entries there are here.  The list is
821189251Ssam		 * supposed to be sorted. 0xfffffff0 and higher are not valid
822189251Ssam		 */
823189251Ssam		for (k = 0; k < le16toh(udfmp->s_table->rt_l); k++) {
824189251Ssam			udfmp->s_table_entries = k;
825189251Ssam			if (le32toh(udfmp->s_table->entries[k].org) >=
826189251Ssam			    0xfffffff0)
827189251Ssam				break;
828189251Ssam		}
829189251Ssam	}
830189251Ssam
831189251Ssam	return (0);
832189251Ssam}
833189251Ssam