1 /* $OpenBSD: disk.c,v 1.75 2022/04/25 17:10:09 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 char *readsectors(const uint64_t, const uint32_t);
42 int writesectors(const void *, const uint64_t, const uint32_t);
43
44 void
DISK_open(const char * name,const int oflags)45 DISK_open(const char *name, const int oflags)
46 {
47 struct stat st;
48 uint64_t ns, bs, sz, spc;
49
50 disk.dk_name = strdup(name);
51 if (disk.dk_name == NULL)
52 err(1, "dk_name");
53 disk.dk_fd = opendev(disk.dk_name, oflags, OPENDEV_PART, NULL);
54 if (disk.dk_fd == -1)
55 err(1, "opendev('%s', 0x%x)", disk.dk_name, oflags);
56 if (fstat(disk.dk_fd, &st) == -1)
57 err(1, "fstat('%s)", disk.dk_name);
58 if (!S_ISCHR(st.st_mode))
59 errx(1, "%s is not a character device", disk.dk_name);
60 if (ioctl(disk.dk_fd, DIOCGPDINFO, &dl) == -1)
61 err(1, "DIOCGPDINFO");
62
63 /* Set geometry to use in MBR partitions. */
64 if (disk.dk_size > 0) {
65 /* -l has set disk size. */
66 sz = disk.dk_size;
67 disk.dk_heads = 1;
68 disk.dk_sectors = 64;
69 disk.dk_size = DL_BLKTOSEC(&dl, sz);
70 disk.dk_cylinders = disk.dk_size / disk.dk_sectors;
71 } else if (disk.dk_cylinders > 0) {
72 /* -c/-h/-s has set disk geometry & therefore size. */
73 sz = disk.dk_cylinders * disk.dk_heads * disk.dk_sectors;
74 disk.dk_size = DL_BLKTOSEC(&dl, sz);
75 disk.dk_sectors = DL_BLKTOSEC(&dl, disk.dk_sectors);
76 } else {
77 disk.dk_cylinders = dl.d_ncylinders;
78 disk.dk_heads = dl.d_ntracks;
79 disk.dk_sectors = dl.d_nsectors;
80 /* MBR handles only first UINT32_MAX sectors. */
81 spc = (uint64_t)disk.dk_heads * disk.dk_sectors;
82 sz = DL_GETDSIZE(&dl);
83 if (sz > UINT32_MAX) {
84 disk.dk_cylinders = UINT32_MAX / spc;
85 disk.dk_size = disk.dk_cylinders * spc;
86 } else
87 disk.dk_size = sz;
88 }
89
90 if (disk.dk_size == 0)
91 errx(1, "disk size is 0");
92
93 if (disk.dk_bootprt.prt_ns > 0) {
94 ns = disk.dk_bootprt.prt_ns + DL_BLKSPERSEC(&dl) - 1;
95 bs = disk.dk_bootprt.prt_bs + DL_BLKSPERSEC(&dl) - 1;
96 disk.dk_bootprt.prt_ns = DL_BLKTOSEC(&dl, ns);
97 disk.dk_bootprt.prt_bs = DL_BLKTOSEC(&dl, bs);
98 }
99 }
100
101 void
DISK_printgeometry(const char * units)102 DISK_printgeometry(const char *units)
103 {
104 const struct unit_type *ut;
105 const int secsize = dl.d_secsize;
106 double size;
107
108 size = units_size(units, disk.dk_size, &ut);
109 printf("Disk: %s\tgeometry: %d/%d/%d [%.0f ", disk.dk_name,
110 disk.dk_cylinders, disk.dk_heads, disk.dk_sectors, size);
111 if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
112 printf("%d-byte ", secsize);
113 printf("%s]\n", ut->ut_lname);
114 }
115
116 /*
117 * The caller must free() the returned memory!
118 */
119 char *
readsectors(const uint64_t sector,const uint32_t count)120 readsectors(const uint64_t sector, const uint32_t count)
121 {
122 char *secbuf;
123 ssize_t len;
124 off_t off, where;
125 size_t bytes;
126
127 where = sector * dl.d_secsize;
128 bytes = count * dl.d_secsize;
129
130 off = lseek(disk.dk_fd, where, SEEK_SET);
131 if (off == -1) {
132 #ifdef DEBUG
133 warn("lseek(%lld) for read", (int64_t)where);
134 #endif
135 return NULL;
136 }
137
138 secbuf = calloc(1, bytes);
139 if (secbuf == NULL)
140 return NULL;
141
142 len = read(disk.dk_fd, secbuf, bytes);
143 if (len == -1) {
144 #ifdef DEBUG
145 warn("read(%zu @ %lld)", bytes, (int64_t)where);
146 #endif
147 free(secbuf);
148 return NULL;
149 }
150 if (len != (ssize_t)bytes) {
151 #ifdef DEBUG
152 warnx("short read(%zu @ %lld)", bytes, (int64_t)where);
153 #endif
154 free(secbuf);
155 return NULL;
156 }
157
158 return secbuf;
159 }
160
161 int
writesectors(const void * buf,const uint64_t sector,const uint32_t count)162 writesectors(const void *buf, const uint64_t sector, const uint32_t count)
163 {
164 ssize_t len;
165 off_t off, where;
166 size_t bytes;
167
168 where = sector * dl.d_secsize;
169 bytes = count * dl.d_secsize;
170
171 off = lseek(disk.dk_fd, where, SEEK_SET);
172 if (off == -1) {
173 #ifdef DEBUG
174 warn("lseek(%lld) for write", (int64_t)where);
175 #endif
176 return -1;
177 }
178
179 len = write(disk.dk_fd, buf, bytes);
180 if (len == -1) {
181 #ifdef DEBUG
182 warn("write(%zu @ %lld)", bytes, (int64_t)where);
183 #endif
184 return -1;
185 }
186 if (len != (ssize_t)bytes) {
187 #ifdef DEBUG
188 warnx("short write(%zu @ %lld)", bytes, (int64_t)where);
189 #endif
190 return -1;
191 }
192
193 return 0;
194 }
195
196 int
DISK_readbytes(void * buf,const uint64_t sector,const size_t sz)197 DISK_readbytes(void *buf, const uint64_t sector, const size_t sz)
198 {
199 char *secbuf;
200 uint32_t count;
201
202 count = (sz + dl.d_secsize - 1) / dl.d_secsize;
203
204 secbuf = readsectors(sector, count);
205 if (secbuf == NULL)
206 return -1;
207
208 memcpy(buf, secbuf, sz);
209 free(secbuf);
210
211 return 0;
212 }
213
214 int
DISK_writebytes(const void * buf,const uint64_t sector,const size_t sz)215 DISK_writebytes(const void *buf, const uint64_t sector, const size_t sz)
216 {
217 char *secbuf;
218 uint32_t count;
219 int rslt;
220
221 count = (sz + dl.d_secsize - 1) / dl.d_secsize;
222
223 secbuf = readsectors(sector, count);
224 if (secbuf == NULL)
225 return -1;
226
227 memcpy(secbuf, buf, sz);
228 rslt = writesectors(secbuf, sector, count);
229 free(secbuf);
230
231 return rslt;
232 }
233