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#define HFS_VOLHDR_OFFSET 1024	/* technote 1150 */
34#define HFS_VOLHDR_SIZE 512	/* technote 1150 */
35
36#define E_OPENDEV -1
37#define E_READ -5
38
39void usage(void);
40char *rawname(char *name);
41char *unrawname(char *name);
42int checkVolHdr(const unsigned char *volhdr);
43char *blockcheck(char *origname);
44
45char *progname;
46
47/*
48 * perhaps check the alternate volume header as well
49
50 * prefer to use raw device. TODO: suppose block device is valid but
51 * the corresponding raw device is not valid, then we fail. this is
52 * probably no the desired behavior.
53 */
54
55int
56main(int argc, char **argv)
57{
58	unsigned char volhdr[HFS_VOLHDR_SIZE] = {0};
59	int fd, retval;
60	char *devname;
61
62	fd = -1;
63	retval = 0;
64
65	if ((progname = strrchr(*argv, '/')))
66		++progname;
67	else
68		progname = *argv;
69
70	if (argc != 2) {
71		usage();
72	} else {
73		devname = blockcheck(argv[1]);
74
75		if (devname != NULL) {
76			if ((fd = open(devname, O_RDONLY, 0)) < 0) {
77				retval = E_OPENDEV;
78			} else if (pread(fd, volhdr, HFS_VOLHDR_SIZE, HFS_VOLHDR_OFFSET) != HFS_VOLHDR_SIZE) {
79				retval = E_READ;
80			} else {
81				retval = checkVolHdr(volhdr);
82			}
83
84			if (-1 != fd) {
85				close(fd);
86				fd = -1;
87			}
88		}
89	}
90
91	return retval;
92}
93
94void
95usage(void)
96{
97	fprintf(stdout, "usage: %s device\n", progname);
98	return;
99}
100
101/* copied from diskdev_cmds/fsck_hfs/utilities.c */
102char *
103rawname(char *name)
104{
105	static char     rawbuf[32];
106	char           *dp;
107
108	if ((dp = strrchr(name, '/')) == 0)
109		return (0);
110	*dp = 0;
111	(void) strcpy(rawbuf, name);
112	*dp = '/';
113	(void) strcat(rawbuf, "/r");
114	(void) strcat(rawbuf, &dp[1]);
115
116	return (rawbuf);
117}
118
119/* copied from diskdev_cmds/fsck_hfs/utilities.c */
120char *
121unrawname(char *name)
122{
123	char           *dp;
124	struct stat     stb;
125
126	if ((dp = strrchr(name, '/')) == 0)
127		return (name);
128	if (stat(name, &stb) < 0)
129		return (name);
130	if ((stb.st_mode & S_IFMT) != S_IFCHR)
131		return (name);
132	if (dp[1] != 'r')
133		return (name);
134	(void) strcpy(&dp[1], &dp[2]);
135
136	return (name);
137}
138
139/*
140 * copied from diskdev_cmds/fsck_hfs/utilities.c, and modified:
141 * 1) remove "hotroot"
142 * 2) if error, return NULL
143 * 3) if not a char device, return NULL (effectively, this is treated
144 *    as error even if accessing the block device might have been OK)
145 */
146char *
147blockcheck(char *origname)
148{
149	struct stat stblock, stchar;
150	char *newname, *raw;
151	int retried;
152
153	retried = 0;
154	newname = origname;
155retry:
156	if (stat(newname, &stblock) < 0) {
157		perror(newname);
158		fprintf(stderr, "Can't stat %s\n", newname);
159		return NULL;
160	}
161	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
162		raw = rawname(newname);
163		if (stat(raw, &stchar) < 0) {
164			perror(raw);
165			fprintf(stderr, "Can't stat %s\n", raw);
166			return NULL;
167		}
168		if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
169			return (raw);
170		} else {
171			fprintf(stderr, "%s is not a character device\n", raw);
172			return NULL;
173		}
174	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
175		newname = unrawname(newname);
176		retried++;
177		goto retry;
178	}
179	/* not a block or character device */
180	return NULL;
181}
182
183/*
184 * (sanity) check the volume header in volhdr
185 *
186 * return 1 if volhdr is an HFS volhdr, 0 otherwise
187 */
188int
189checkVolHdr(const unsigned char *volhdr)
190{
191	int retval;
192
193	retval = 0;
194
195	if (strncmp((const char *)volhdr, "H+", 2) == 0) {
196		/* technote 1150: H+ is version 4 */
197		retval = (volhdr[3] == 4);
198	} else if (strncmp((const char *)volhdr, "HX", 2) == 0) {
199		/* technote 1150: HX is version 5 */
200		retval = (volhdr[3] == 5);
201	}
202	return retval;
203}
204