xref: /openbsd-src/sys/isofs/udf/udf_subr.c (revision 3b9d585e0ce86bc6eed0b4493f3c73d741778f91)
1*3b9d585eSjsg /*	$OpenBSD: udf_subr.c,v 1.27 2024/04/13 23:44:11 jsg Exp $	*/
2b6647e39Smiod 
3b6647e39Smiod /*
4b6647e39Smiod  * Copyright (c) 2006, Miodrag Vallat
545aa27dfSpedro  * Copyright (c) 2006, Pedro Martelletto
6b6647e39Smiod  *
7b6647e39Smiod  * Redistribution and use in source and binary forms, with or without
8b6647e39Smiod  * modification, are permitted provided that the following conditions
9b6647e39Smiod  * are met:
10b6647e39Smiod  * 1. Redistributions of source code must retain the above copyright
11b6647e39Smiod  *    notice, this list of conditions and the following disclaimer.
12b6647e39Smiod  * 2. Redistributions in binary form must reproduce the above copyright
13b6647e39Smiod  *    notice, this list of conditions and the following disclaimer in the
14b6647e39Smiod  *    documentation and/or other materials provided with the distribution.
15b6647e39Smiod  *
16b6647e39Smiod  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17b6647e39Smiod  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18b6647e39Smiod  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19b6647e39Smiod  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20b6647e39Smiod  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21b6647e39Smiod  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22b6647e39Smiod  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23b6647e39Smiod  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24b6647e39Smiod  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25b6647e39Smiod  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26b6647e39Smiod  * POSSIBILITY OF SUCH DAMAGE.
27b6647e39Smiod  */
28b6647e39Smiod 
29b6647e39Smiod #include <sys/param.h>
30b6647e39Smiod #include <sys/systm.h>
3145aa27dfSpedro #include <sys/buf.h>
32b6647e39Smiod #include <sys/kernel.h>
33b97bc9b1Spedro #include <sys/malloc.h>
34b6647e39Smiod #include <sys/mutex.h>
35b6647e39Smiod #include <sys/stat.h>
36b6647e39Smiod #include <sys/mount.h>
37b6647e39Smiod #include <sys/vnode.h>
38fde894e5Stedu #include <sys/lock.h>
39b6647e39Smiod #include <sys/dirent.h>
4045aa27dfSpedro #include <sys/disklabel.h>
41b6647e39Smiod 
429dc9bb81Sdlg #include <crypto/siphash.h>
439dc9bb81Sdlg 
44b6647e39Smiod #include <isofs/udf/ecma167-udf.h>
45b6647e39Smiod #include <isofs/udf/udf.h>
46b6647e39Smiod #include <isofs/udf/udf_extern.h>
47b6647e39Smiod 
48884fd684Spedro int udf_vat_read(struct umount *, uint32_t *);
498eb7f8cbSpedro 
50b6647e39Smiod /*
51b6647e39Smiod  * Convert a CS0 dstring to a 16-bit Unicode string.
52b6647e39Smiod  * Returns the length of the Unicode string, in unicode characters (not
53b6647e39Smiod  * bytes!), or -1 if an error arises.
54b6647e39Smiod  * Note that the transname destination buffer is expected to be large
55b6647e39Smiod  * enough to hold the result, and will not be terminated in any way.
56b6647e39Smiod  */
57b6647e39Smiod int
udf_rawnametounicode(u_int len,char * cs0string,unicode_t * transname)58b6647e39Smiod udf_rawnametounicode(u_int len, char *cs0string, unicode_t *transname)
59b6647e39Smiod {
60b6647e39Smiod 	unicode_t *origname = transname;
61b6647e39Smiod 
62b6647e39Smiod 	if (len-- == 0)
63b6647e39Smiod 		return (-1);
64b6647e39Smiod 
65b6647e39Smiod 	switch (*cs0string++) {
66b6647e39Smiod 	case 8:		/* bytes string */
67b6647e39Smiod 		while (len-- != 0)
68b6647e39Smiod 			*transname++ = (unicode_t)*cs0string++;
69b6647e39Smiod 		break;
70b6647e39Smiod 	case 16:	/* 16 bit unicode string */
71b6647e39Smiod 		if (len & 1)
72b6647e39Smiod 			return (-1);
73b6647e39Smiod 		len >>= 1;
74b6647e39Smiod 		while (len-- != 0) {
75b6647e39Smiod 			unicode_t tmpchar;
76b6647e39Smiod 
77b6647e39Smiod 			tmpchar = (unicode_t)*cs0string++;
78b6647e39Smiod 			tmpchar = (tmpchar << 8) | (unicode_t)*cs0string++;
79b6647e39Smiod 			*transname++ = tmpchar;
80b6647e39Smiod 		}
81b6647e39Smiod 		break;
82b6647e39Smiod 	default:
83b6647e39Smiod 		return (-1);
84b6647e39Smiod 	}
85b6647e39Smiod 
86b6647e39Smiod 	return (transname - origname);
87b6647e39Smiod }
8845aa27dfSpedro 
8945aa27dfSpedro /*
9045aa27dfSpedro  * Do a lazy probe on the underlying media to check if it's a UDF volume, in
9145aa27dfSpedro  * which case we fake a disk label for it.
9245aa27dfSpedro  */
9345aa27dfSpedro int
udf_disklabelspoof(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp)9445aa27dfSpedro udf_disklabelspoof(dev_t dev, void (*strat)(struct buf *),
9545aa27dfSpedro     struct disklabel *lp)
9645aa27dfSpedro {
9745aa27dfSpedro 	char vid[32];
9845aa27dfSpedro 	int i, bsize = 2048, error = EINVAL;
9945aa27dfSpedro 	uint32_t sector = 256, mvds_start, mvds_end;
10045aa27dfSpedro 	struct buf *bp;
10145aa27dfSpedro 	struct anchor_vdp avdp;
10245aa27dfSpedro 	struct pri_vol_desc *pvd;
10345aa27dfSpedro 
10445aa27dfSpedro 	/*
10545aa27dfSpedro 	 * Get a buffer to work with.
10645aa27dfSpedro 	 */
10745aa27dfSpedro 	bp = geteblk(bsize);
10845aa27dfSpedro 	bp->b_dev = dev;
10945aa27dfSpedro 
11045aa27dfSpedro 	/*
11145aa27dfSpedro 	 * Look for an Anchor Volume Descriptor at sector 256.
11245aa27dfSpedro 	 */
11345aa27dfSpedro 	bp->b_blkno = sector * btodb(bsize);
11445aa27dfSpedro 	bp->b_bcount = bsize;
1150189133dSkrw 	CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
116e08c5f56Skrw 	SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
11745aa27dfSpedro 	bp->b_resid = bp->b_blkno / lp->d_secpercyl;
11845aa27dfSpedro 
11945aa27dfSpedro 	(*strat)(bp);
12045aa27dfSpedro 	if (biowait(bp))
12145aa27dfSpedro 		goto out;
12245aa27dfSpedro 
12345aa27dfSpedro 	if (udf_checktag((struct desc_tag *)bp->b_data, TAGID_ANCHOR))
12445aa27dfSpedro 		goto out;
12545aa27dfSpedro 
12645aa27dfSpedro 	bcopy(bp->b_data, &avdp, sizeof(avdp));
12745aa27dfSpedro 	mvds_start = letoh32(avdp.main_vds_ex.loc);
12845aa27dfSpedro 	mvds_end = mvds_start + (letoh32(avdp.main_vds_ex.len) - 1) / bsize;
12945aa27dfSpedro 
13045aa27dfSpedro 	/*
13145aa27dfSpedro 	 * Then try to find a reference to a Primary Volume Descriptor.
13245aa27dfSpedro 	 */
13345aa27dfSpedro 	for (sector = mvds_start; sector < mvds_end; sector++) {
13445aa27dfSpedro 		bp->b_blkno = sector * btodb(bsize);
13545aa27dfSpedro 		bp->b_bcount = bsize;
1360189133dSkrw 		CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
137e08c5f56Skrw 		SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
13845aa27dfSpedro 		bp->b_resid = bp->b_blkno / lp->d_secpercyl;
13945aa27dfSpedro 
14045aa27dfSpedro 		(*strat)(bp);
14145aa27dfSpedro 		if (biowait(bp))
14245aa27dfSpedro 			goto out;
14345aa27dfSpedro 
14445aa27dfSpedro 		pvd = (struct pri_vol_desc *)bp->b_data;
14545aa27dfSpedro 		if (!udf_checktag(&pvd->tag, TAGID_PRI_VOL))
14645aa27dfSpedro 			break;
14745aa27dfSpedro 	}
14845aa27dfSpedro 
14945aa27dfSpedro 	/*
15045aa27dfSpedro 	 * If we couldn't find a reference, bail out.
15145aa27dfSpedro 	 */
15245aa27dfSpedro 	if (sector == mvds_end)
15345aa27dfSpedro 		goto out;
15445aa27dfSpedro 
15545aa27dfSpedro 	/*
156198e4265Spedro 	 * Okay, it's a UDF volume. Spoof a disk label for it.
15745aa27dfSpedro 	 */
1588518eb4aSpedro 	if (udf_transname(pvd->vol_id, vid, sizeof(pvd->vol_id) - 1, NULL))
15945aa27dfSpedro 		strlcpy(lp->d_typename, vid, sizeof(lp->d_typename));
16045aa27dfSpedro 
16145aa27dfSpedro 	for (i = 0; i < MAXPARTITIONS; i++) {
162e16633b4Sderaadt 		DL_SETPSIZE(&lp->d_partitions[i], 0);
163e16633b4Sderaadt 		DL_SETPOFFSET(&lp->d_partitions[i], 0);
16445aa27dfSpedro 	}
16545aa27dfSpedro 
16645aa27dfSpedro 	/*
16745aa27dfSpedro 	 * Fake two partitions, 'a' and 'c'.
16845aa27dfSpedro 	 */
169e16633b4Sderaadt 	DL_SETPSIZE(&lp->d_partitions[0], DL_GETDSIZE(lp));
17045aa27dfSpedro 	lp->d_partitions[0].p_fstype = FS_UDF;
171e16633b4Sderaadt 	DL_SETPSIZE(&lp->d_partitions[RAW_PART], DL_GETDSIZE(lp));
17245aa27dfSpedro 	lp->d_partitions[RAW_PART].p_fstype = FS_UDF;
173e96b4495Skrw 	lp->d_npartitions = MAXPARTITIONS;
174d9864f85Sderaadt 	lp->d_version = 1;
17545aa27dfSpedro 
17645aa27dfSpedro 	lp->d_magic = DISKMAGIC;
17745aa27dfSpedro 	lp->d_magic2 = DISKMAGIC;
17845aa27dfSpedro 	lp->d_checksum = dkcksum(lp);
17945aa27dfSpedro 
18045aa27dfSpedro 	error = 0;
18145aa27dfSpedro out:
18245aa27dfSpedro 	bp->b_flags |= B_INVAL;
18345aa27dfSpedro 	brelse(bp);
18445aa27dfSpedro 
18545aa27dfSpedro 	return (error);
18645aa27dfSpedro }
1878eb7f8cbSpedro 
1888eb7f8cbSpedro /* Get a vnode for the Virtual Allocation Table (VAT) */
1898eb7f8cbSpedro int
udf_vat_get(struct umount * ump,uint32_t lb)190e1a8e762Spedro udf_vat_get(struct umount *ump, uint32_t lb)
1918eb7f8cbSpedro {
1928eb7f8cbSpedro 	struct vnode *vp;
1931af96cccSpedro 	struct unode *up;
1948eb7f8cbSpedro 	int error;
1958eb7f8cbSpedro 
196e1a8e762Spedro 	error = udf_vget(ump->um_mountp, lb - ump->um_start - 3, &vp);
1978eb7f8cbSpedro 	if (error)
1988eb7f8cbSpedro 		return (error);
1998eb7f8cbSpedro 
2001af96cccSpedro 	up = VTOU(vp);
2011af96cccSpedro 	up->u_vatlen = (letoh64(up->u_fentry->inf_len) - 36) >> 2;
2028eb7f8cbSpedro 
203b97bc9b1Spedro 	ump->um_vat = malloc(sizeof(struct unode), M_UDFMOUNT, M_WAITOK);
204b97bc9b1Spedro 	*ump->um_vat = *up;
205b97bc9b1Spedro 
206884fd684Spedro 	ump->um_flags &= ~UDF_MNT_FIND_VAT;
207884fd684Spedro 	ump->um_flags |=  UDF_MNT_USES_VAT;
2088eb7f8cbSpedro 
2098eb7f8cbSpedro 	vput(vp);
2108eb7f8cbSpedro 
2118eb7f8cbSpedro 	return (0);
2128eb7f8cbSpedro }
2138eb7f8cbSpedro 
2148eb7f8cbSpedro /* Look up a sector in the VAT */
2158eb7f8cbSpedro int
udf_vat_map(struct umount * ump,uint32_t * sector)216884fd684Spedro udf_vat_map(struct umount *ump, uint32_t *sector)
2178eb7f8cbSpedro {
2188eb7f8cbSpedro 	/* If there's no VAT, then it's easy */
219884fd684Spedro 	if (!(ump->um_flags & UDF_MNT_USES_VAT)) {
220884fd684Spedro 		*sector += ump->um_start;
2218eb7f8cbSpedro 		return (0);
2228eb7f8cbSpedro 	}
2238eb7f8cbSpedro 
2248eb7f8cbSpedro 	/* Sanity check the given sector */
225b97bc9b1Spedro 	if (*sector >= ump->um_vat->u_vatlen)
2268eb7f8cbSpedro 		return (EINVAL);
2278eb7f8cbSpedro 
2288eb7f8cbSpedro 	return (udf_vat_read(ump, sector));
2298eb7f8cbSpedro }
2308eb7f8cbSpedro 
2318eb7f8cbSpedro /* Read from the VAT */
2328eb7f8cbSpedro int
udf_vat_read(struct umount * ump,uint32_t * sector)233884fd684Spedro udf_vat_read(struct umount *ump, uint32_t *sector)
2348eb7f8cbSpedro {
2358eb7f8cbSpedro 	struct buf *bp;
2368eb7f8cbSpedro 	uint8_t *data;
2378eb7f8cbSpedro 	int error, size;
2388eb7f8cbSpedro 
2398eb7f8cbSpedro 	size = 4;
2408eb7f8cbSpedro 
2418eb7f8cbSpedro 	/*
2428eb7f8cbSpedro 	 * Note that we rely on the buffer cache to keep frequently accessed
2438eb7f8cbSpedro 	 * buffers around to avoid reading them from the disk all the time.
2448eb7f8cbSpedro 	 */
245b97bc9b1Spedro 	error = udf_readatoffset(ump->um_vat, &size, *sector << 2, &bp, &data);
2468eb7f8cbSpedro 	if (error) {
2478eb7f8cbSpedro 		if (bp != NULL)
2488eb7f8cbSpedro 			brelse(bp);
2498eb7f8cbSpedro 
2508eb7f8cbSpedro 		return (error);
2518eb7f8cbSpedro 	}
2528eb7f8cbSpedro 
2538eb7f8cbSpedro 	/* Make sure we read at least a whole entry */
2548eb7f8cbSpedro 	if (size < 4) {
2558eb7f8cbSpedro 		if (bp != NULL)
2568eb7f8cbSpedro 			brelse(bp);
2578eb7f8cbSpedro 
2588eb7f8cbSpedro 		return (EINVAL);
2598eb7f8cbSpedro 	}
2608eb7f8cbSpedro 
2618eb7f8cbSpedro 	/* Map the sector */
262884fd684Spedro 	*sector = letoh32(*(uint32_t *)data) + ump->um_start;
2638eb7f8cbSpedro 
2648eb7f8cbSpedro 	brelse(bp);
2658eb7f8cbSpedro 
2668eb7f8cbSpedro 	return (0);
2678eb7f8cbSpedro }
268