1/*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/stat.h>
25#include <sys/param.h>
26#include <sys/time.h>
27
28#include <err.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <fstab.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36
37#include <sys/types.h>
38#include <sys/ioctl.h>
39#include <sys/disk.h>
40
41#define E_OPENDEV -1
42#define E_READ -5
43
44/*
45 * We don't have a (non-C++) standard header for UDF (yet?), so
46 * let's define the basic structures and constants we'll be using.
47 */
48
49typedef struct UDFVolumeSequenceDescriptor {
50	unsigned char	type;
51	unsigned char	id[5];
52	unsigned char	version;
53	unsigned char	data[2041];
54} udfVSD;
55
56#define UDFSTART	(32*1024)	/* First 32k is unused */
57
58void usage(void);
59char *rawname(char *name);
60char *unrawname(char *name);
61int CheckUDF(int, int);
62char *blockcheck(char *origname);
63
64char *progname;
65
66/*
67 * prefer to use raw device. TODO: suppose block device is valid but
68 * the corresponding raw device is not valid, then we fail. this is
69 * probably no the desired behavior.
70 */
71
72int
73main(int argc, char **argv)
74{
75	char *devname = NULL;
76	int fd, retval;
77
78	retval = 0;
79	fd = -1;
80
81	if ((progname = strrchr(*argv, '/')))
82		++progname;
83	else
84		progname = *argv;
85
86	if (argc != 2) {
87		usage();
88	} else {
89		devname = blockcheck(argv[1]);
90		if (devname != NULL) {
91			if ((fd = open(devname, O_RDONLY, 0)) < 0) {
92				retval = E_OPENDEV;
93			} else {
94				int bsize;
95				if (ioctl(fd, DKIOCGETBLOCKSIZE, (char*)&bsize) == -1) {
96#ifdef DEBUG
97					warn("DKIOCGETBLOCKSIZE ioctl failed for %s", devname);
98#endif
99					bsize = 2048;	/* A standard default size */
100				}
101				retval = CheckUDF(fd, bsize) == 1;
102				if (retval == 0 && bsize != 2048) {
103					retval = CheckUDF(fd, 2048) == 1;
104				}
105			}
106		}
107	}
108
109	return retval;
110}
111
112static int
113IsVSD(udfVSD *vsd) {
114	int retval = memcmp(vsd->id, "BEA01", 5)==0 ||
115		memcmp(vsd->id, "BOOT2", 5)==0 ||
116		memcmp(vsd->id, "NSR02", 5)==0 ||
117		memcmp(vsd->id, "NSR03", 5)==0 ||
118		memcmp(vsd->id, "TEA01", 5)==0 ||
119		memcmp(vsd->id, "CDW02", 5)==0 ||
120		memcmp(vsd->id, "CD001", 5)==0;
121#ifdef DEBUG
122	fprintf(stderr, "IsVSD:  Returning %d\n", retval);
123#endif
124	return retval;
125}
126
127/*
128 * This is inspired by the udf25 kext code.
129 * It concludes that a device has a UDF filesystem
130 * if:
131 * 1)  It has a Volume Sequence Descriptor;
132 * 2)  That VSD has a "BEA01" in it;
133 * 3)  That VSD has an "NSR02" or "NSR03" in it before the terminating one.
134 *
135 * It may be necessary to check the AVDP(s), as well.
136 */
137
138int
139CheckUDF(int fd, int blockSize) {
140	ssize_t err;
141	char buf[blockSize];
142	off_t curr, max;
143	char found = 0;
144
145	curr = UDFSTART;
146	max = curr + (512 * blockSize);
147	if (lseek(fd, curr, SEEK_SET) == -1) {
148		warn("Cannot seek to %llu", curr);
149		return -1;
150	}
151
152	while (curr < max) {
153		udfVSD *vsd;
154		err = read(fd, buf, sizeof(buf));
155		if (err != sizeof(buf)) {
156			if (err == -1) {
157				warn("Cannot read %zu bytes", sizeof(buf));
158			} else {
159				warn("Cannot read %zd bytes, only read %zd", sizeof(buf), err);
160			}
161			return -1;
162		}
163		vsd = (udfVSD*)buf;
164		if (!IsVSD(vsd)) {
165			break;
166		}
167		if (vsd->type == 0 &&
168			memcmp(vsd->id, "BEA01", 5) == 0 &&
169			vsd->version == 1) {
170			found = 1;
171			break;
172		}
173		curr += blockSize;
174	}
175	if (found == 0)
176		return 0;
177
178	found = 0;
179
180	while (curr < max) {
181		udfVSD *vsd;
182		err = read(fd, buf, sizeof(buf));
183		if (err != sizeof(buf)) {
184			if (err == -1) {
185				warn("Cannot read %zu bytes", sizeof(buf));
186			} else {
187				warn("Cannot read %zu bytes, only read %zd", sizeof(buf), err);
188			}
189			return -1;
190		}
191		vsd = (udfVSD*)buf;
192		if (!IsVSD(vsd)) {
193			break;
194		}
195		if (vsd->type == 0 &&
196			memcmp(vsd->id, "TEA01", 5) == 0 &&
197			vsd->version == 1) {
198			/* we're at the end */
199			break;
200		} else if (memcmp(vsd->id, "NSR02", 5) == 0 ||
201				memcmp(vsd->id, "NSR03", 5) == 0) {
202			found = 1;
203			break;
204		}
205		curr += blockSize;
206	}
207
208	return found;
209}
210
211void
212usage(void)
213{
214	fprintf(stdout, "usage: %s device\n", progname);
215	return;
216}
217
218/* copied from diskdev_cmds/fsck_hfs/utilities.c */
219char *
220rawname(char *name)
221{
222	static char     rawbuf[32];
223	char           *dp;
224
225	if ((dp = strrchr(name, '/')) == 0)
226		return (0);
227	*dp = 0;
228	(void) strcpy(rawbuf, name);
229	*dp = '/';
230	(void) strcat(rawbuf, "/r");
231	(void) strcat(rawbuf, &dp[1]);
232
233	return (rawbuf);
234}
235
236/* copied from diskdev_cmds/fsck_hfs/utilities.c */
237char *
238unrawname(char *name)
239{
240	char           *dp;
241	struct stat     stb;
242
243	if ((dp = strrchr(name, '/')) == 0)
244		return (name);
245	if (stat(name, &stb) < 0)
246		return (name);
247	if ((stb.st_mode & S_IFMT) != S_IFCHR)
248		return (name);
249	if (dp[1] != 'r')
250		return (name);
251	(void) strcpy(&dp[1], &dp[2]);
252
253	return (name);
254}
255
256/*
257 * copied from diskdev_cmds/fsck_hfs/utilities.c, and modified:
258 * 1) remove "hotroot"
259 * 2) if error, return NULL
260 * 3) if not a char device, return NULL (effectively, this is treated
261 *    as error even if accessing the block device might have been OK)
262 */
263char *
264blockcheck(char *origname)
265{
266	struct stat     stblock, stchar;
267	char           *newname, *raw;
268	int             retried = 0;
269
270	newname = origname;
271retry:
272	if (stat(newname, &stblock) < 0) {
273		perror(newname);
274		fprintf(stderr, "Can't stat %s\n", newname);
275		return NULL;
276	}
277	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
278		raw = rawname(newname);
279		if (stat(raw, &stchar) < 0) {
280			perror(raw);
281			fprintf(stderr, "Can't stat %s\n", raw);
282			return NULL;
283		}
284		if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
285			return (raw);
286		} else {
287			fprintf(stderr, "%s is not a character device\n", raw);
288			return NULL;
289		}
290	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
291		newname = unrawname(newname);
292		retried++;
293		goto retry;
294	}
295#ifdef DEBUG
296	else if ((stblock.st_mode & S_IFMT) == S_IFREG) {
297		return strdup(origname);
298	}
299#endif
300	/* not a block or character device */
301	return NULL;
302}
303
304
305