summaryrefslogtreecommitdiffstats
path: root/diskdev_cmds/fdisk.tproj/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'diskdev_cmds/fdisk.tproj/disk.c')
-rw-r--r--diskdev_cmds/fdisk.tproj/disk.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/diskdev_cmds/fdisk.tproj/disk.c b/diskdev_cmds/fdisk.tproj/disk.c
new file mode 100644
index 0000000..2d04113
--- /dev/null
+++ b/diskdev_cmds/fdisk.tproj/disk.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2002, 2012 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Copyright (c) 1997, 2001 Tobias Weingartner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Tobias Weingartner.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/reboot.h>
+#include <sys/disk.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#ifdef __i386__
+#include <pexpert/i386/boot.h>
+#endif
+#include "disk.h"
+
+int
+DISK_open(disk, mode)
+ char *disk;
+ int mode;
+{
+ int fd;
+ struct stat st;
+
+ fd = open(disk, mode);
+ if (fd == -1)
+ err(1, "%s", disk);
+ if (fstat(fd, &st) == -1)
+ err(1, "%s", disk);
+ /* Don't be so picky about needing a character device */
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode))
+ errx(1, "%s is not a character device or a regular file", disk);
+ return (fd);
+}
+
+int
+DISK_openshared(disk, mode, shared)
+ char *disk;
+ int mode;
+ int *shared;
+{
+ int fd;
+ struct stat st;
+ *shared = 0;
+
+ fd = open(disk, mode|O_EXLOCK);
+ if (fd == -1) {
+ // if we can't have exclusive access, attempt
+ // to gracefully degrade to shared access
+ fd = open(disk, mode|O_SHLOCK);
+ if(fd == -1)
+ err(1, "%s", disk);
+
+ *shared = 1;
+ }
+
+ if (fstat(fd, &st) == -1)
+ err(1, "%s", disk);
+ /* Don't be so picky about needing a character device */
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode))
+ errx(1, "%s is not a character device or a regular file", disk);
+ return (fd);
+}
+
+int
+DISK_close(fd)
+ int fd;
+{
+
+ return (close(fd));
+}
+
+/* Given a size in the metrics,
+ * fake up a CHS geometry.
+ */
+void
+DISK_fake_CHS(DISK_metrics *lm)
+{
+ int heads = 4;
+ int spt = 63;
+ int cylinders = (lm->size / heads / spt);
+
+ while (cylinders > 1024 && heads < 256) {
+ heads *= 2;
+ cylinders /= 2;
+ }
+ if (heads == 256) {
+ heads = 255;
+ cylinders = (lm->size / heads / spt);
+ }
+ lm->cylinders = cylinders;
+ lm->heads = heads;
+ lm->sectors = spt;
+}
+
+/* Routine to go after the disklabel for geometry
+ * information. This should work everywhere, but
+ * in the land of PC, things are not always what
+ * they seem.
+ */
+DISK_metrics *
+DISK_getlabelmetrics(name)
+ char *name;
+{
+ DISK_metrics *lm = NULL;
+ long long size;
+ uint32_t sector_size;
+ int fd;
+ struct stat st;
+
+ /* Get label metrics */
+ if ((fd = DISK_open(name, O_RDONLY)) != -1) {
+ lm = malloc(sizeof(DISK_metrics));
+
+ if (fstat(fd, &st) == -1)
+ err(1, "%s", name);
+ if (!S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) {
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size) == -1) {
+ err(1, "Could not get disk block count");
+ free(lm);
+ return NULL;
+ }
+ if (ioctl(fd, DKIOCGETBLOCKSIZE, &sector_size) == -1) {
+ err(1, "Could not get disk block size");
+ free(lm);
+ return NULL;
+ }
+ } else {
+ sector_size = 512;
+ size = st.st_size / sector_size;
+ }
+
+ lm->sector_size = sector_size;
+ lm->size = size;
+ DISK_fake_CHS(lm);
+ DISK_close(fd);
+ }
+
+ return (lm);
+}
+
+/*
+ * Don't try to get BIOS disk metrics.
+ */
+DISK_metrics *
+DISK_getbiosmetrics(name)
+ char *name;
+{
+ return (NULL);
+}
+
+/* This is ugly, and convoluted. All the magic
+ * for disk geo/size happens here. Basically,
+ * the real size is the one we will use in the
+ * rest of the program, the label size is what we
+ * got from the disklabel. If the disklabel fails,
+ * we assume we are working with a normal file,
+ * and should request the user to specify the
+ * geometry he/she wishes to use.
+ */
+int
+DISK_getmetrics(disk, user)
+ disk_t *disk;
+ DISK_metrics *user;
+{
+
+ disk->label = DISK_getlabelmetrics(disk->name);
+ disk->bios = DISK_getbiosmetrics(disk->name);
+
+ /* If user supplied, use that */
+ if (user) {
+ disk->real = user;
+ return (0);
+ }
+
+ /* Fixup bios metrics to include cylinders past 1023 boundary */
+ if(disk->label && disk->bios){
+ int cyls, secs;
+
+ cyls = disk->label->size / (disk->bios->heads * disk->bios->sectors);
+ secs = cyls * (disk->bios->heads * disk->bios->sectors);
+ if (disk->label->size < secs) {
+ errx(1, "BIOS fixup botch (%u sectors)", disk->label->size - secs);
+ }
+ disk->bios->cylinders = cyls;
+ disk->bios->size = secs;
+ }
+
+ /* If we have a (fixed) BIOS geometry, use that */
+ if (disk->bios) {
+ disk->real = disk->bios;
+ return (0);
+ }
+
+ /* If we have a label, use that */
+ if (disk->label) {
+ disk->real = disk->label;
+ return (0);
+ }
+
+ /* Can not get geometry, punt */
+ disk->real = NULL;
+ return (1);
+}
+
+/* Get the disk's native sector size, updating the metrics' sector_size field.
+ */
+ int
+DISK_get_sector_size(disk, user)
+ disk_t *disk;
+ DISK_metrics *user;
+{
+ int ret;
+ int fd;
+ uint32_t sector_size;
+
+ /* Default to 512 bytes per sector, in case of failure. */
+ user->sector_size = 512;
+ ret = 1;
+
+ fd = DISK_open(disk->name, O_RDONLY);
+ if (fd == -1) {
+ err(1, "Could not open %s", disk->name);
+ } else {
+ if (ioctl(fd, DKIOCGETBLOCKSIZE, &sector_size) == -1) {
+ err(1, "Could not get disk block size");
+ } else {
+ user->sector_size = sector_size;
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int
+DISK_printmetrics(disk)
+ disk_t *disk;
+{
+
+ printf("Disk: %s\t", disk->name);
+ if (disk->real) {
+ printf("geometry: %u/%u/%u [%u sectors]\n", disk->real->cylinders,
+ disk->real->heads, disk->real->sectors, disk->real->size);
+ if (disk->real->sector_size != 512)
+ printf("Sector size: %u bytes\n", disk->real->sector_size);
+ } else {
+ printf("geometry: <none>\n");
+ }
+
+ return (0);
+}
+