xref: /openbsd-src/sbin/fdisk/disk.c (revision 824adb5411e4389b29bae28eba5c2c2bbd147f34)
1 /*	$OpenBSD: disk.c,v 1.73 2021/09/13 15:07:51 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Tobias Weingartner
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>		/* DEV_BSIZE */
20 #include <sys/ioctl.h>
21 #include <sys/dkio.h>
22 #include <sys/stat.h>
23 #include <sys/disklabel.h>
24 
25 #include <err.h>
26 #include <fcntl.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <util.h>
33 
34 #include "part.h"
35 #include "disk.h"
36 #include "misc.h"
37 
38 struct disk		disk;
39 struct disklabel	dl;
40 
41 void
42 DISK_open(const char *name, const int oflags)
43 {
44 	struct stat		st;
45 	uint64_t		ns, bs, sz, spc;
46 
47 	disk.dk_name = strdup(name);
48 	if (disk.dk_name == NULL)
49 		err(1, "dk_name");
50 	disk.dk_fd = opendev(disk.dk_name, oflags, OPENDEV_PART, NULL);
51 	if (disk.dk_fd == -1)
52 		err(1, "opendev('%s', 0x%x)", disk.dk_name, oflags);
53 	if (fstat(disk.dk_fd, &st) == -1)
54 		err(1, "fstat('%s)", disk.dk_name);
55 	if (!S_ISCHR(st.st_mode))
56 		errx(1, "%s is not a character device", disk.dk_name);
57 	if (ioctl(disk.dk_fd, DIOCGPDINFO, &dl) == -1)
58 		err(1, "DIOCGPDINFO");
59 
60 	/* Set geometry to use in MBR partitions. */
61 	if (disk.dk_size > 0) {
62 		/* -l has set disk size. */
63 		sz = disk.dk_size;
64 		disk.dk_heads = 1;
65 		disk.dk_sectors = 64;
66 		disk.dk_size = DL_BLKTOSEC(&dl, sz);
67 		disk.dk_cylinders = disk.dk_size / disk.dk_sectors;
68 	} else if (disk.dk_cylinders > 0) {
69 		/* -c/-h/-s has set disk geometry & therefore size. */
70 		sz = disk.dk_cylinders * disk.dk_heads * disk.dk_sectors;
71 		disk.dk_size = DL_BLKTOSEC(&dl, sz);
72 		disk.dk_sectors = DL_BLKTOSEC(&dl, disk.dk_sectors);
73 	} else {
74 		disk.dk_sectors = dl.d_nsectors;
75 		disk.dk_cylinders = dl.d_ncylinders;
76 		disk.dk_heads = dl.d_ntracks;
77 		disk.dk_sectors = dl.d_nsectors;
78 		/* MBR handles only first UINT32_MAX sectors. */
79 		spc = (uint64_t)disk.dk_heads * disk.dk_sectors;
80 		sz = DL_GETDSIZE(&dl);
81 		if (sz > UINT32_MAX) {
82 			disk.dk_cylinders = UINT32_MAX / spc;
83 			disk.dk_size = disk.dk_cylinders * spc;
84 		} else
85 			disk.dk_size = sz;
86 	}
87 
88 	if (disk.dk_size == 0)
89 		errx(1, "disk size is 0");
90 
91 	if (disk.dk_bootprt.prt_ns > 0) {
92 		ns = disk.dk_bootprt.prt_ns + DL_BLKSPERSEC(&dl) - 1;
93 		bs = disk.dk_bootprt.prt_bs + DL_BLKSPERSEC(&dl) - 1;
94 		disk.dk_bootprt.prt_ns = DL_BLKTOSEC(&dl, ns);
95 		disk.dk_bootprt.prt_bs = DL_BLKTOSEC(&dl, bs);
96 	}
97 }
98 
99 void
100 DISK_printgeometry(const char *units)
101 {
102 	const struct unit_type	*ut;
103 	const int		 secsize = dl.d_secsize;
104 	double			 size;
105 
106 	size = units_size(units, disk.dk_size, &ut);
107 	printf("Disk: %s\tgeometry: %d/%d/%d [%.0f ", disk.dk_name,
108 	    disk.dk_cylinders, disk.dk_heads, disk.dk_sectors, size);
109 	if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
110 		printf("%d-byte ", secsize);
111 	printf("%s]\n", ut->ut_lname);
112 }
113 
114 /*
115  * The caller must free() the returned memory!
116  */
117 char *
118 DISK_readsectors(const uint64_t sector, const uint32_t count)
119 {
120 	char			*secbuf;
121 	ssize_t			 len;
122 	off_t			 off, where;
123 	size_t			 bytes;
124 
125 	where = sector * dl.d_secsize;
126 	bytes = count * dl.d_secsize;
127 
128 	off = lseek(disk.dk_fd, where, SEEK_SET);
129 	if (off == -1) {
130 #ifdef DEBUG
131 		warn("lseek(%lld) for read", (int64_t)where);
132 #endif
133 		return NULL;
134 	}
135 
136 	secbuf = calloc(1, bytes);
137 	if (secbuf == NULL)
138 		return NULL;
139 
140 	len = read(disk.dk_fd, secbuf, bytes);
141 	if (len == -1) {
142 #ifdef DEBUG
143 		warn("read(%zu @ %lld)", bytes, (int64_t)where);
144 #endif
145 		free(secbuf);
146 		return NULL;
147 	}
148 	if (len != (ssize_t)bytes) {
149 #ifdef DEBUG
150 		warnx("short read(%zu @ %lld)", bytes, (int64_t)where);
151 #endif
152 		free(secbuf);
153 		return NULL;
154 	}
155 
156 	return secbuf;
157 }
158 
159 int
160 DISK_writesectors(const char *buf, const uint64_t sector,
161     const uint32_t count)
162 {
163 	ssize_t			len;
164 	off_t			off, where;
165 	size_t			bytes;
166 
167 	where = sector * dl.d_secsize;
168 	bytes = count * dl.d_secsize;
169 
170 	off = lseek(disk.dk_fd, where, SEEK_SET);
171 	if (off == -1) {
172 #ifdef DEBUG
173 		warn("lseek(%lld) for write", (int64_t)where);
174 #endif
175 		return -1;
176 	}
177 
178 	len = write(disk.dk_fd, buf, bytes);
179 	if (len == -1) {
180 #ifdef DEBUG
181 		warn("write(%zu @ %lld)", bytes, (int64_t)where);
182 #endif
183 		return -1;
184 	}
185 	if (len != (ssize_t)bytes) {
186 #ifdef DEBUG
187 		warnx("short write(%zu @ %lld)", bytes, (int64_t)where);
188 #endif
189 		return -1;
190 	}
191 
192 	return 0;
193 }
194