1/*
2 * Copyright (c) 2004 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 <string.h>
26#include <stdlib.h>
27#include <limits.h>
28#include <errno.h>
29#include <err.h>
30#include <fcntl.h>
31#include <pwd.h>
32#include <grp.h>
33#include <unistd.h>
34#include <zlib.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <sys/disk.h>
38
39#include <CoreFoundation/CoreFoundation.h>
40#include <Kernel/libkern/OSByteOrder.h>
41
42#include <IOKit/storage/IOAppleLabelScheme.h>
43
44#include "util.h"
45
46#define MIN(a, b) \
47	({ typeof (a) _x = (a); typeof (b) _y = (b); \
48		_x < _y ? _x : _y; })
49
50/*
51 * When setting properties, a value can be forced to be
52 * a string by putting it in quotes.  e.g., foo="123"
53 * This function copies the string, and removes the quotes.
54 * If the string does not begin with a double-quote, it is
55 * simply duplicated and the duplicate returned; if it does
56 * begin with a double-quote, it MUST end with a double-quote,
57 * or NULL is returned.
58 * The string that is returned must be free()'d by the
59 * caller.
60 */
61static char *
62RemoveQuotes(const char *s) {
63	char *retval;
64	int len;
65
66	if (*s != '"')
67		return strdup(s);
68
69	retval = strdup(s + 1);
70	len = strlen(retval);
71	if (len < 2 || retval[len - 1] != '"') {
72		warnx("Quoted string `%s' is mal-formed", s);
73		return NULL;
74	}
75	retval[len - 1] = 0;
76	return retval;
77}
78
79/*
80 * Do some sanity checks on the given device name
81 * (e.g., device-name=fred).  Right now, we check to
82 * see if it has a '/' in it, or if the device already
83 * exists in /dev.  Having a '/' is an error; already
84 * existing is a warning.
85 * In case of error, we return -1; in case of a warning,
86 * we print it out to stderr, but still return success (0).
87 */
88static int
89CheckDeviceName(const char *name) {
90	int len = strlen(name);
91	char devname[len + 6];
92	struct stat sbuf;
93
94	if (strchr(name, '/') != NULL) {
95		warnx("Device name `%s' is invalid:  cannot contain a '/'", name);
96		return -1;
97	}
98
99	snprintf(devname, sizeof(devname), "/dev/%s", name);
100	if (stat(devname, &sbuf) != -1) {
101		warnx("Device name `%s' already exists in /dev", name);
102	}
103
104	// Any other checks to do?
105	return 0;
106}
107
108/*
109 * Do a basic sanity check on the user name ("user-uid=...").
110 * If it's a string, and the user exists, set *uidp to it;
111 * if it's a number, set *uidp to it;
112 * we return a positive number in those case.
113 * Otherwise, print out an appropriate warning, and return -1.
114 */
115static int
116CheckUserName(const char *name, int asString, uid_t *uidp) {
117	uid_t uid;
118	struct passwd *pwd;
119
120	if (!asString) {
121		char *endptr;
122		uid = strtoul(name, &endptr, 10);
123		if (*name != 0 && *endptr == 0) {
124			// It was a digit
125			pwd = getpwuid(uid);
126			if (pwd == NULL) {
127				warnx("UID %d does not exist on this system", uid);
128				return -1;
129			}
130			*uidp = uid;
131			return 1;
132		}
133	}
134	// Otherwise, treat it as a string
135	pwd = getpwnam(name);
136	if (pwd == NULL) {
137		warnx("User-name `%s' does not exist on this system", name);
138		return -1;
139	}
140
141	*uidp = pwd->pw_uid;
142	return 1;
143}
144
145/*
146 * Same thing as above, but for the group ("owner-group=...").
147 */
148static int
149CheckGroupName(const char *name, int asString, gid_t *gidp) {
150	gid_t uid;
151	struct group *grp;
152
153	if (!asString) {
154		char *endptr;
155		uid = strtoul(name, &endptr, 10);
156		if (*name != 0 && *endptr == 0) {
157			// It was a digit
158			grp = getgrgid(uid);
159			if (grp == NULL) {
160				warnx("GID %d does not exist on this system", uid);
161				return -1;
162			}
163			*gidp = uid;
164			return 1;
165		}
166	}
167	// Otherwise, treat it as a string
168	grp = getgrnam(name);
169	if (grp == NULL) {
170		warnx("Group-name `%s' does not exist on this system", name);
171		return -1;
172	}
173
174	*gidp = grp->gr_gid;
175	return 1;
176}
177
178/*
179 * As above, but check the device mode ("owner-mode=...").
180 * Right now, it must be an integer, but it could conceivably
181 * be made into a string later (e.g., "owner-mode=rw-rw---",
182 * perhaps?).  It returns 0 on success, and -1 on failure.
183 */
184static int
185CheckDeviceMode(const char *mode, int asString) {
186	unsigned long m;
187	char *endptr;
188
189	if (asString) {
190		warnx("Device mode must be an integer");
191		return -1;
192	}
193
194	m = strtoul(mode, &endptr, 0);
195
196	if (*mode != 0 && *endptr == 0) {
197		return 0;
198	}
199	warnx("Device mode `%s' is invalid", mode);
200	return -1;
201}
202
203/*
204 * A routine to get the applelabel structure.  This will be at
205 * the beginning of the device.  On error, it returns NULL; on
206 * success, it returns a pointer to allocated memory.  It also
207 * swaps the values of the structure around to host order, so
208 * the caller does not need to.
209 */
210static struct applelabel *
211getDeviceLabel(const char *dev) {
212	int fd;
213	struct applelabel *retval = NULL;
214
215	fd = open(dev, O_RDONLY);
216	if (fd == -1) {
217		warn("getDeviceLabel:  unable to open device %s for reading", dev);
218		return NULL;
219	}
220	retval = malloc(sizeof(*retval));
221	if (retval == NULL) {
222		warnx("getDeviceLabel: unable to allocate memory for device label");
223		goto done;
224	}
225	if (read(fd, retval, sizeof(*retval)) != sizeof(*retval)) {
226		warnx("getDeviceLabel: unable to read a full label for device %s", dev);
227		free(retval);
228		retval = NULL;
229		goto done;
230	}
231	retval->al_magic	= OSSwapBigToHostInt16(retval->al_magic);
232	retval->al_type		= OSSwapBigToHostInt16(retval->al_type);
233	retval->al_flags	= OSSwapBigToHostInt32(retval->al_flags);
234	retval->al_offset	= OSSwapBigToHostInt64(retval->al_offset);
235	retval->al_size		= OSSwapBigToHostInt32(retval->al_size);
236	retval->al_checksum	= OSSwapBigToHostInt32(retval->al_checksum);
237
238done:
239	close(fd);
240	return retval;
241}
242
243/*
244 * The opposite of the above, this sets the device label.
245 * It returns -1 on failure.  It assumes the various fields
246 * are in host-order, and swaps them around as appropriate.
247 */
248static int
249setDeviceLabel(const char *dev, const struct applelabel *lbl) {
250	int fd;
251	int retval = -1;
252	struct applelabel tmp;
253
254	if (lbl == NULL)
255		return retval;
256
257	fd = open(dev, O_WRONLY);
258	if (fd == -1) {
259		warn("setDeviceLabel: unable to open device %s", dev);
260		return retval;
261	}
262
263	tmp = *lbl;
264	tmp.al_magic	= OSSwapHostToBigInt16(tmp.al_magic);
265	tmp.al_type	= OSSwapHostToBigInt16(tmp.al_type);
266	tmp.al_flags	= OSSwapHostToBigInt32(tmp.al_flags);
267	tmp.al_offset	= OSSwapHostToBigInt64(tmp.al_offset);
268	tmp.al_size	= OSSwapHostToBigInt32(tmp.al_size);
269	tmp.al_checksum	= OSSwapHostToBigInt32(tmp.al_checksum);
270
271	if (write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
272		warn("setDeviceLabel: unable to write a full label to device %s", dev);
273		goto done;
274	}
275	retval = 0;
276done:
277	close(fd);
278	return retval;
279}
280
281/*
282 * Apply the crc32 checksum function to the metadata.
283 * It needs to know the size so that it can fill out the
284 * pad area as needed.  It returns 0 on error, although
285 * that's not great.
286 */
287static uint32_t
288ChecksumData(CFDictionaryRef dict, int32_t size) {
289	uint32_t retval = 0;
290	unsigned char *bytes = NULL;
291	int len;
292	CFDataRef data = nil;
293
294	bytes = malloc(size);
295	if (bytes == NULL) {
296		warnx("ChecksumData:  cannot allocate %d bytes of data\n", size);
297		return 0;
298	}
299	memset(bytes, 0, size);
300
301	data = CFPropertyListCreateXMLData(nil, (CFPropertyListRef)dict);
302	if (data == nil) {
303		warnx("ChecksumData:  cannot create data from dictionary");
304		goto done;
305	}
306
307	len = CFDataGetLength(data);
308	memcpy(bytes, (void*)CFDataGetBytePtr(data), len);
309
310	if (gDebug && gVerbose) {
311		fprintf(stderr, "ChecksumData:  calling crc32(0, %p, %d)\n", bytes, size);
312	}
313
314	retval = crc32(0, bytes, size);
315
316done:
317	if (data)
318		CFRelease(data);
319	free(bytes);
320
321	return retval;
322}
323
324/*
325 * Given a C string property (e.g., "owner-uid=fred" or "owner-mode"),
326 * turn it into a CFStringRef key, and optionally CFStringRef or CFNumberRef value.
327 * Return 0 on error, 1 on success.
328 * If the input string has an '=' in it, and defines a value, and the input
329 * pointer valuePtr is not NULL, then it will set valuePtr to a CFTypeRef
330 * (either a CFStringRef or a CFNumberRef).  If the value is being set, and
331 * is one of a few known properties, it will also do some basic sanity checking
332 * on the values (see the static functions above).
333 */
334int
335parseProperty(const char *term, CFStringRef *namePtr, CFTypeRef *valuePtr) {
336	CFStringRef k = nil;
337	CFTypeRef v = nil;
338	char *tag;
339	char *value = strdup(term);
340	char *endptr;
341	unsigned int tInt = -1;
342	char forceInt = 0;
343	char forceString = 0;
344
345	tag = strsep(&value, "=");
346
347	if (gDebug > 1)
348		fprintf(stderr, "parseProperty: property `%s' = value `%s'\n",
349			tag, value);
350	k = CFStringCreateWithCString(nil, tag, kCFStringEncodingASCII);
351	if (value) {
352		if (*value == '"') {	// force string
353			endptr = RemoveQuotes(value);
354			free(tag);
355			tag = value = endptr;
356			forceString = 1;
357		}
358
359		if (!strcmp(tag, "dev-name")) {
360			forceString = 1;	// just in case
361			if (CheckDeviceName(value) == -1) {
362				goto bad;
363			}
364		}
365		if (!strcmp(tag, "owner-uid")) {
366			if (CheckUserName(value, forceString, &tInt) == -1) {
367				// An illegal name
368				goto bad;
369			}
370		}
371		if (!strcmp(tag, "owner-gid")) {
372			if (CheckGroupName(value, forceString, &tInt) == -1) {
373				// An illegal group
374				goto bad;
375			}
376		}
377		if (!strcmp(tag, "owner-mode")) {
378			if (CheckDeviceMode(value, forceString) == -1) {
379				// An illegal mode
380				goto bad;
381			}
382		}
383
384		if (tInt == -1) {
385			tInt = strtol(value, &endptr, 0);
386		} else {
387			forceInt = 1;
388		}
389		if (!forceInt && (forceString || (endptr == value ||
390			(*endptr != 0 && *value != 0)))) {
391			if (gDebug)
392				fprintf(stderr, "Property `%s' has value `%s'\n",
393					tag, value);
394			v = CFStringCreateWithCString(nil, value, kCFStringEncodingASCII);
395		} else {
396			if (gDebug)
397				fprintf(stderr, "Property `%s' has int value %d\n",
398					tag, tInt);
399			v = CFNumberCreate(nil, kCFNumberSInt32Type, &tInt);
400		}
401	}
402	if (k) {
403		if (namePtr) {
404			*namePtr = k;
405		} else {
406			CFRelease(k);
407		}
408	}
409	if (v) {
410		if (valuePtr) {
411			*valuePtr = v;
412		} else {
413			CFRelease(v);
414		}
415	}
416	free(tag);
417
418	return 1;
419
420bad:
421	if (v)
422		CFRelease(v);
423	if (k)
424		CFRelease(k);
425	if (tag)
426		free(tag);
427	return 0;
428}
429
430/*
431 * Read the metadata from the device.  It returns NULL on error,
432 * and a CFDictionaryRef on success.  It opens up the device, gets
433 * the applelabel from it, and then uses that to read in the data.
434 */
435CFDictionaryRef
436ReadMetadata(const char *dev) {
437	int fd;
438	struct stat sbuf;
439	int64_t off;
440	struct applelabel *lbl = NULL;
441	CFDictionaryRef retval = nil;
442	CFStringRef errStr;
443	CFDataRef data = nil;
444	void *buf = NULL;
445	int len;
446
447	if ((fd = open(dev, O_RDONLY)) == -1) {
448		warn("cannot open device %s", dev);
449		return nil;
450	}
451	lbl = getDeviceLabel(dev);
452	if (lbl == NULL) {
453		warnx("ReadMetadata:  cannot get label for device %s", dev);
454		goto done;
455	}
456	off = lbl->al_offset;
457	len = lbl->al_size;
458
459	fstat(fd, &sbuf);
460
461	if (lseek(fd, off, SEEK_SET) == -1) {
462		warn("ReadMetadata:  cannot seek to metadata offset for device %s", dev);
463		goto done;
464	}
465
466	if (gDebug) {
467		fprintf(stderr, "For device %s, metadata len (max) = %d\n", dev, len);
468	}
469	buf = malloc(len);
470	if (buf == NULL) {
471		warnx("cannot allocate %d bytes to read metadata for device %s", len, dev);
472		goto done;
473	}
474	read(fd, buf, len);
475
476	data = CFDataCreate(nil, buf, len);
477	if (data == nil) {
478		warnx("cannot create CFData instance of XML");
479		goto done;
480	}
481	retval = (CFPropertyListRef)CFPropertyListCreateFromXMLData(nil,
482		data,
483		kCFPropertyListImmutable, &errStr);
484
485	if (retval == NULL) {
486		int l = CFStringGetLength(errStr);
487		char buf[l * 2];
488		if (CFStringGetCString(errStr, buf, sizeof(buf), kCFStringEncodingASCII)) {
489			warnx("cannot create property list: %s", buf);
490		}
491		CFRelease(errStr);
492	}
493done:
494	if (buf)
495		free(buf);
496	if (data)
497		CFRelease(data);
498	close(fd);
499	if (lbl)
500		free(lbl);
501
502	return retval;
503}
504
505/*
506 * Used to create the initial metadata area.  It sets up an
507 * applelabel structure at the beginning of the file, computes the
508 * initial checksum, and writes out the metadata.  It returns -1
509 * on failure.
510 */
511int
512InitialMetadata(const char *dev, CFDictionaryRef dict, uint64_t size) {
513	int fd;
514	int retval = -1;
515	uint32_t bs;
516	uint64_t dSize;
517	struct applelabel lbl = { { 0 } };
518
519	if (gDebug && gVerbose) {
520		fprintf(stderr, "InitialMetadata(%s, dict, %qu)\n", dev, size);
521	}
522
523	fd = open(dev, O_RDWR);
524	if (fd == -1) {
525		warn("InitialMetadata:  cannot open device file %s", dev);
526		return -1;
527	}
528
529	bs = GetBlockSize(dev);
530	if (bs == 0) {
531		warnx("InitialMetadata:  cannot get block size for device %s", dev);
532		goto done;
533	}
534
535	dSize = GetDiskSize(dev);
536	if (dSize == 0) {
537		warnx("InitialMetadata:  cannot get disk size for device %s", dev);
538		goto done;
539	}
540
541	if (dSize <= size) {
542		warnx("InitialMetadata:  Disk device size (%qu) is not large enough "
543			"for metadata size (%qu) for device %s",
544			dSize, size, dev);
545		goto done;
546	}
547
548	lbl.al_magic	= AL_MAGIC;
549	lbl.al_type	= AL_TYPE_DEFAULT;
550	lbl.al_flags	= AL_FLAG_DEFAULT;
551	lbl.al_offset	= bs;	// start at block #1
552	lbl.al_size	= (uint32_t)size - bs;
553	lbl.al_checksum	= ChecksumData(dict, lbl.al_size);
554
555	if (gDebug) {
556		fprintf(stderr, "lbl = {\n");
557		fprintf(stderr, "\tal_magic = 0x%x\n", lbl.al_magic);
558		fprintf(stderr, "\tal_type = 0x%x\n", lbl.al_type);
559		fprintf(stderr, "\tal_flags = 0x%x\n", lbl.al_flags);
560		fprintf(stderr, "\tal_offset = %qu\n", lbl.al_offset);
561		fprintf(stderr, "\tal_size = %d\n", lbl.al_size);
562		fprintf(stderr, "\tal_checksum = 0x%x\n};\n", lbl.al_checksum);
563	}
564
565	if (setDeviceLabel(dev, &lbl) == -1) {
566		warnx("InitialMetadata:  cannot write header for device %s", dev);
567		goto done;
568	}
569
570	if (WriteMetadata(dev, dict) == -1) {
571		warnx("InitialMetadata:  cannot write metadata");
572		goto done;
573	}
574
575	retval = 0;
576done:
577	close(fd);
578	return retval;
579}
580
581/*
582 * Write out the metadata to the file.  It also recomputes
583 * the checksum.  It returns -1 on error.
584 */
585int
586WriteMetadata(const char *dev, CFDictionaryRef dict) {
587	int fd;
588	int64_t off;
589	int len;
590	CFDataRef data = nil;
591	int retval = -1;
592	void *bytes;
593	uint32_t cksum = 0;
594	struct applelabel *lbl;
595	uint32_t mSize;
596
597	lbl = getDeviceLabel(dev);
598	if (lbl == NULL) {
599		warnx("cannot get label for device %s", dev);
600		return -1;
601	}
602
603	mSize = GetMetadataSize(dev);
604	bytes = malloc(mSize);
605	memset(bytes, 0, mSize);
606
607	if (bytes == NULL) {
608		warnx("WriteMetadata: cannot allocate %u bytes\n", mSize);
609		return -1;
610	}
611
612	fd = open(dev, O_RDWR);
613	if (fd == -1) {
614		warn("cannot open device %s", dev);
615		return -1;
616	}
617	if (GetDiskSize(dev) < lbl->al_size) {
618		warnx("device %s is too small for metadata size", dev);
619		return -1;
620	}
621
622	off = lbl->al_offset;
623
624	if (lseek(fd, off, SEEK_SET) == -1) {
625		warn("WriteMetadata: cannot seek to offset %qu for device %s", off, dev);
626		goto done;
627	}
628
629	data = CFPropertyListCreateXMLData(nil, (CFPropertyListRef)dict);
630	if (data == nil) {
631		warnx("cannot create CFData from dictionary");
632		goto done;
633	}
634	len = CFDataGetLength(data);
635	memcpy(bytes, CFDataGetBytePtr(data), len);
636
637	if (write(fd, bytes, mSize) != mSize) {
638		warn("cannot write %d bytes to metadata area", mSize);
639		goto done;
640	}
641
642	cksum = ChecksumData(dict, mSize);
643
644	lbl->al_checksum = cksum;
645	if (setDeviceLabel(dev, lbl) == -1) {
646		warnx("unable to update label for device %s", dev);
647		goto done;
648	}
649
650	retval = 1;
651done:
652	close(fd);
653	if (data)
654		CFRelease(data);
655	return retval;
656}
657
658/*
659 * Return the checksum of the metadata area *from the disk*.
660 * It returns 0 on error (not great, I know).
661 */
662uint32_t
663ChecksumMetadata(const char *dev) {
664	int fd = -1;
665	struct stat sbuf;
666	int64_t off;
667	int32_t cksum = 0;
668	unsigned char *buf = NULL;
669	int len;
670	struct applelabel *lbl = NULL;
671
672	if ((fd = open(dev, O_RDONLY)) == -1) {
673		warn("cannot open device %s", dev);
674		return 0;
675	}
676
677	lbl = getDeviceLabel(dev);
678	if (lbl == NULL) {
679		warnx("ChecksumMetadata:  cannot get label for device %s", dev);
680		goto done;
681	}
682	fstat(fd, &sbuf);
683	off = lbl->al_offset;
684	len = lbl->al_size;
685
686	if (lseek(fd, off, SEEK_SET) == -1) {
687		warn("ChecksumMetadata:  cannot seek to %qu for device %s", off, dev);
688		goto done;
689	}
690
691	if (gDebug) {
692		fprintf(stderr, "ChecksumMetadata: For device %s, metadata len (max) = %d\n", dev, len);
693	}
694	buf = malloc(len);
695	if (buf == NULL) {
696		warnx("ChecksumMetadata: cannot allocate %d bytes to read metadata for device %s", len, dev);
697		goto done;
698	}
699	read(fd, buf, len);
700
701	if (gDebug && gVerbose) {
702		fprintf(stderr, "ChecksumMetadata:  calling crc32(0, %p, %d)\n", buf, len);
703	}
704
705	cksum = crc32(0, buf, len);
706
707	if (gDebug) {
708		fprintf(stderr, "ChecksumMetadata: For device %s, checksum = %u\n", dev, cksum);
709	}
710
711done:
712	if (buf)
713		free(buf);
714	close(fd);
715	if (lbl)
716		free(lbl);
717
718	return cksum;
719}
720
721/*
722 * Update the applelabel's value of the checksum to the indicated
723 * value.  It returns -1 on error.
724 */
725int
726UpdateChecksum(const char *dev, uint32_t sum) {
727	struct applelabel *lbl;
728
729	lbl = getDeviceLabel(dev);
730	if (lbl == NULL) {
731		warnx("UpdateChecksum:  cannot get label for device %s", dev);
732		return -1;
733	}
734
735	lbl->al_checksum = sum;
736	if (setDeviceLabel(dev, lbl) == -1) {
737		warnx("UpdateChecksum:  cannot update label for device %s", dev);
738		return -1;
739	}
740	free(lbl);
741
742	return 0;
743}
744
745/*
746 * A simpler version to get teh checksum, this returns
747 * the value that is in the applelabel structure.
748 */
749uint32_t
750GetChecksum(const char *dev) {
751	struct applelabel *lbl;
752	uint32_t retval = 0;
753
754	lbl = getDeviceLabel(dev);
755	if (lbl == NULL) {
756		warnx("GetChecksum:  cannot get label for device %s", dev);
757		return retval;
758	}
759	retval = lbl->al_checksum;
760	free(lbl);
761	return retval;
762}
763
764/*
765 * Compare the applelabel structure's copy of the checksum against
766 * a computed value of the checksum.  Return the difference -- it should
767 * be 0, obviously, on success!
768 */
769int
770VerifyChecksum(const char *dev) {
771	int32_t written, data;
772
773	if ((data = ChecksumMetadata(dev)) < 1) {
774		warnx("VerifyChecksum:  Metadata checksum is 0x%x", data);
775	}
776	if ((written = GetChecksum(dev)) < 1) {
777		warnx("VerifyChecksum:  Metadata written checksum is 0x%x", written);
778	}
779	return data - written;
780}
781
782/*
783 * Return the size of the metadata area, as defined by
784 * the applelabel structure.
785 */
786uint32_t
787GetMetadataSize(const char *dev) {
788	struct applelabel *lbl = NULL;
789	uint32_t retval;
790	lbl = getDeviceLabel(dev);
791	if (lbl == NULL) {
792		warnx("GetMetadataSize:  cannot get label for device %s", dev);
793		return 0;
794	}
795
796	retval = lbl->al_size;
797	free(lbl);
798	return retval;
799}
800
801/*
802 * Get the device label (if possible), and see if it's
803 * got the right magic value.  If so, return 1; otherwise,
804 * return 0;
805 */
806int
807IsAppleLabel(const char *dev) {
808	struct applelabel *lbl = NULL;
809	int retval = 0;
810
811	lbl = getDeviceLabel(dev);
812	if (lbl == NULL) {
813		warn("IsAppleLabel:  cannot get label for device %s", dev);
814		return 0;
815	}
816
817	retval = lbl->al_magic == AL_MAGIC;
818	free(lbl);
819	return retval;
820}
821
822/*
823 * Return the blocksize for the requested device.
824 * If the device is not a disk, we return an error (0),
825 * UNLESS debugging is turned on (gDebug, -D command line option).
826 * In that case, if we can't get teh block size for the "device,"
827 * we assume 4k.
828 */
829uint32_t
830GetBlockSize(const char *dev) {
831	uint32_t retval = 0;
832	int fd;
833
834	fd = open(dev, O_RDONLY);
835	if (fd == -1) {
836		warn("BlockSize:  cannot open %s", dev);
837		return 0;
838	}
839	if (ioctl(fd, DKIOCGETBLOCKSIZE, &retval) == -1) {
840		if (gDebug) {
841			retval = 4 * 1024;	// a default blocksize
842		} else {
843			retval = 0;
844			warn("BlockSize:  cannot get blocksize for device %s", dev);
845		}
846	}
847	close(fd);
848	return retval;
849}
850
851/*
852 * Similar to above, but it returns the size (in bytes)
853 * of the device.  If debugging is enabled, and it is
854 * a file, it just returns the size of the file.
855 */
856uint64_t
857GetDiskSize(const char *dev) {
858	int bs;
859	uint64_t bc;
860	int fd;
861	uint64_t retval = 0;
862
863	fd = open(dev, O_RDONLY);
864	if (fd == -1) {
865		warn("GetDiskSize:  cannot open %s", dev);
866		return 0;
867	}
868	bs = GetBlockSize(dev);
869	if (bs == 0) {
870		return 0;
871	}
872
873	if (ioctl(fd, DKIOCGETBLOCKCOUNT, &bc) == -1) {
874		if (gDebug) {
875			struct stat sbuf;
876			if (fstat(fd, &sbuf) == -1) {
877				warn("GetDiskSize:  cannot fstat %s", dev);
878			} else {
879				retval = sbuf.st_size;
880			}
881		} else {
882			warn("GetDiskSize:  cannot get block count for device %s", dev);
883		}
884	} else {
885		retval = bs * bc;
886	}
887	close(fd);
888	return retval;
889}
890
891