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 <stdio.h>
25#include <stdlib.h>
26#include <sys/disk.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <unistd.h>
30#include <string.h>
31#include <sys/stat.h>
32
33/* copied from diskdev_cmds/fsck_msdos/dosfs.h */
34#define DOSBOOTBLOCKSIZE 512
35#define MAX_SECTOR_SIZE 4096
36
37#define E_OPENDEV -1
38#define E_READ -5
39
40void usage(void);
41char *rawname(char *name);
42char *unrawname(char *name);
43int checkVolHdr(const unsigned char *volhdr);
44char *blockcheck(char *origname);
45
46char *progname;
47
48/*
49 * prefer to use raw device. TODO: suppose block device is valid but
50 * the corresponding raw device is not valid, then we fail. this is
51 * probably no the desired behavior.
52 */
53
54int
55main(int argc, char **argv)
56{
57	unsigned char volhdr[MAX_SECTOR_SIZE] = {0};
58	int fd, retval;
59	char *devname;
60
61	fd = -1;
62	retval = 0;
63
64	if ((progname = strrchr(*argv, '/')))
65		++progname;
66	else
67		progname = *argv;
68
69	if (argc != 2) {
70		usage();
71	} else {
72		devname = blockcheck(argv[1]);
73
74		if (devname != NULL) {
75			if ((fd = open(devname, O_RDONLY, 0)) < 0) {
76				retval = E_OPENDEV;
77			} else if (read(fd, volhdr, MAX_SECTOR_SIZE) != MAX_SECTOR_SIZE) {
78				retval = E_READ;
79			} else {
80				retval = checkVolHdr(volhdr);
81			}
82
83			if (-1 != fd) {
84				close(fd);
85				fd = -1;
86			}
87		}
88	}
89
90	return retval;
91}
92
93void
94usage(void)
95{
96	fprintf(stdout, "usage: %s device\n", progname);
97	return;
98}
99
100/* copied from diskdev_cmds/fsck_hfs/utilities.c */
101char *
102rawname(char *name)
103{
104	static char     rawbuf[32];
105	char           *dp;
106
107	if ((dp = strrchr(name, '/')) == 0)
108		return (0);
109	*dp = 0;
110	(void) strcpy(rawbuf, name);
111	*dp = '/';
112	(void) strcat(rawbuf, "/r");
113	(void) strcat(rawbuf, &dp[1]);
114
115	return (rawbuf);
116}
117
118/* copied from diskdev_cmds/fsck_hfs/utilities.c */
119char *
120unrawname(char *name)
121{
122	char           *dp;
123	struct stat     stb;
124
125	if ((dp = strrchr(name, '/')) == 0)
126		return (name);
127	if (stat(name, &stb) < 0)
128		return (name);
129	if ((stb.st_mode & S_IFMT) != S_IFCHR)
130		return (name);
131	if (dp[1] != 'r')
132		return (name);
133	(void) strcpy(&dp[1], &dp[2]);
134
135	return (name);
136}
137
138/*
139 * copied from diskdev_cmds/fsck_hfs/utilities.c, and modified:
140 * 1) remove "hotroot"
141 * 2) if error, return NULL
142 * 3) if not a char device, return NULL (effectively, this is treated
143 *    as error even if accessing the block device might have been OK)
144 */
145char *
146blockcheck(char *origname)
147{
148	struct stat stblock, stchar;
149	char *newname, *raw;
150	int retried;
151
152	retried = 0;
153	newname = origname;
154retry:
155	if (stat(newname, &stblock) < 0) {
156		perror(newname);
157		fprintf(stderr, "Can't stat %s\n", newname);
158		return NULL;
159	}
160	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
161		raw = rawname(newname);
162		if (stat(raw, &stchar) < 0) {
163			perror(raw);
164			fprintf(stderr, "Can't stat %s\n", raw);
165			return NULL;
166		}
167		if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
168			return (raw);
169		} else {
170			fprintf(stderr, "%s is not a character device\n", raw);
171			return NULL;
172		}
173	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
174		newname = unrawname(newname);
175		retried++;
176		goto retry;
177	}
178	/* not a block or character device */
179	return NULL;
180}
181
182/*
183 * (sanity) check the volume header in volhdr
184 *
185 * return 1 if volhdr is a FAT volhdr, 0 otherwise
186 */
187int
188checkVolHdr(const unsigned char *volhdr)
189{
190	/* NTFS volumes have an OEMid of NTFS followed by four spaces. */
191	const char *ntfs_oemid = "NTFS    ";
192	int retval;
193
194	retval = 1;
195
196	/* copied from diskdev_cmds/fsck_msdos/boot.c */
197
198	/*
199	 * [2699033]
200	 *
201	 * The first three bytes are an Intel x86 jump instruction.  It should
202	 * be one of the following forms: 0xE9 0x?? 0x?? 0xEC 0x?? 0x90 where
203	 * 0x?? means any byte value is OK.
204	 */
205	if (volhdr[0] != 0xE9 && (volhdr[0] != 0xEB || volhdr[2] != 0x90)) {
206		retval = 0;
207	}
208	if (!memcmp(ntfs_oemid, volhdr + 3, 8))
209		retval = 0;
210	return retval;
211}
212