Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/common/disk.c
34677 views
1
/*-
2
* Copyright (c) 1998 Michael Smith <[email protected]>
3
* Copyright (c) 2012 Andrey V. Elsukov <[email protected]>
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/disk.h>
29
#include <sys/queue.h>
30
#include <stand.h>
31
#include <stdarg.h>
32
#include <bootstrap.h>
33
#include <part.h>
34
#include <assert.h>
35
36
#include "disk.h"
37
38
#ifdef DISK_DEBUG
39
# define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
40
#else
41
# define DPRINTF(fmt, args...) ((void)0)
42
#endif
43
44
struct open_disk {
45
struct ptable *table;
46
uint64_t mediasize;
47
uint64_t entrysize;
48
u_int sectorsize;
49
};
50
51
struct print_args {
52
struct disk_devdesc *dev;
53
const char *prefix;
54
int verbose;
55
};
56
57
/* Convert size to a human-readable number. */
58
static char *
59
display_size(uint64_t size, u_int sectorsize)
60
{
61
static char buf[80];
62
char unit;
63
64
size = size * sectorsize / 1024;
65
unit = 'K';
66
if (size >= 10485760000LL) {
67
size /= 1073741824;
68
unit = 'T';
69
} else if (size >= 10240000) {
70
size /= 1048576;
71
unit = 'G';
72
} else if (size >= 10000) {
73
size /= 1024;
74
unit = 'M';
75
}
76
snprintf(buf, sizeof(buf), "%4ld%cB", (long)size, unit);
77
return (buf);
78
}
79
80
int
81
ptblread(void *d, void *buf, size_t blocks, uint64_t offset)
82
{
83
struct disk_devdesc *dev;
84
struct open_disk *od;
85
86
dev = (struct disk_devdesc *)d;
87
od = (struct open_disk *)dev->dd.d_opendata;
88
89
/*
90
* The strategy function assumes the offset is in units of 512 byte
91
* sectors. For larger sector sizes, we need to adjust the offset to
92
* match the actual sector size.
93
*/
94
offset *= (od->sectorsize / 512);
95
/*
96
* As the GPT backup partition is located at the end of the disk,
97
* to avoid reading past disk end, flag bcache not to use RA.
98
*/
99
return (dev->dd.d_dev->dv_strategy(dev, F_READ | F_NORA, offset,
100
blocks * od->sectorsize, (char *)buf, NULL));
101
}
102
103
static int
104
ptable_print(void *arg, const char *pname, const struct ptable_entry *part)
105
{
106
struct disk_devdesc dev;
107
struct print_args *pa, bsd;
108
struct open_disk *od;
109
struct ptable *table;
110
char line[80];
111
int res;
112
u_int sectsize;
113
uint64_t partsize;
114
115
pa = (struct print_args *)arg;
116
od = (struct open_disk *)pa->dev->dd.d_opendata;
117
sectsize = od->sectorsize;
118
partsize = part->end - part->start + 1;
119
snprintf(line, sizeof(line), " %s%s: %s", pa->prefix, pname,
120
parttype2str(part->type));
121
if (pager_output(line))
122
return (1);
123
124
if (pa->verbose) {
125
/* Emit extra tab when the line is shorter than 3 tab stops */
126
if (strlen(line) < 24)
127
(void) pager_output("\t");
128
129
snprintf(line, sizeof(line), "\t%s",
130
display_size(partsize, sectsize));
131
if (pager_output(line))
132
return (1);
133
}
134
if (pager_output("\n"))
135
return (1);
136
137
res = 0;
138
if (part->type == PART_FREEBSD) {
139
/* Open slice with BSD label */
140
dev.dd.d_dev = pa->dev->dd.d_dev;
141
dev.dd.d_unit = pa->dev->dd.d_unit;
142
dev.d_slice = part->index;
143
dev.d_partition = D_PARTNONE;
144
if (disk_open(&dev, partsize, sectsize) == 0) {
145
table = ptable_open(&dev, partsize, sectsize, ptblread);
146
if (table != NULL) {
147
snprintf(line, sizeof(line), " %s%s",
148
pa->prefix, pname);
149
bsd.dev = pa->dev;
150
bsd.prefix = line;
151
bsd.verbose = pa->verbose;
152
res = ptable_iterate(table, &bsd, ptable_print);
153
ptable_close(table);
154
}
155
disk_close(&dev);
156
}
157
}
158
159
return (res);
160
}
161
162
int
163
disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
164
{
165
struct open_disk *od;
166
struct print_args pa;
167
168
/* Disk should be opened */
169
od = (struct open_disk *)dev->dd.d_opendata;
170
pa.dev = dev;
171
pa.prefix = prefix;
172
pa.verbose = verbose;
173
return (ptable_iterate(od->table, &pa, ptable_print));
174
}
175
176
int
177
disk_read(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
178
{
179
struct open_disk *od;
180
int ret;
181
182
od = (struct open_disk *)dev->dd.d_opendata;
183
ret = dev->dd.d_dev->dv_strategy(dev, F_READ, dev->d_offset + offset,
184
blocks * od->sectorsize, buf, NULL);
185
186
return (ret);
187
}
188
189
int
190
disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
191
{
192
struct open_disk *od;
193
int ret;
194
195
od = (struct open_disk *)dev->dd.d_opendata;
196
ret = dev->dd.d_dev->dv_strategy(dev, F_WRITE, dev->d_offset + offset,
197
blocks * od->sectorsize, buf, NULL);
198
199
return (ret);
200
}
201
202
int
203
disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
204
{
205
struct open_disk *od = dev->dd.d_opendata;
206
207
if (od == NULL)
208
return (ENOTTY);
209
210
switch (cmd) {
211
case DIOCGSECTORSIZE:
212
*(u_int *)data = od->sectorsize;
213
break;
214
case DIOCGMEDIASIZE:
215
if (dev->d_offset == 0)
216
*(uint64_t *)data = od->mediasize;
217
else
218
*(uint64_t *)data = od->entrysize * od->sectorsize;
219
break;
220
default:
221
return (ENOTTY);
222
}
223
224
return (0);
225
}
226
227
int
228
disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize)
229
{
230
struct disk_devdesc partdev;
231
struct open_disk *od;
232
struct ptable *table;
233
struct ptable_entry part;
234
int rc, slice, partition;
235
236
if (sectorsize == 0) {
237
DPRINTF("unknown sector size");
238
return (ENXIO);
239
}
240
rc = 0;
241
od = (struct open_disk *)malloc(sizeof(struct open_disk));
242
if (od == NULL) {
243
DPRINTF("no memory");
244
return (ENOMEM);
245
}
246
dev->dd.d_opendata = od;
247
od->entrysize = 0;
248
od->mediasize = mediasize;
249
od->sectorsize = sectorsize;
250
/*
251
* While we are reading disk metadata, make sure we do it relative
252
* to the start of the disk
253
*/
254
memcpy(&partdev, dev, sizeof(partdev));
255
partdev.d_offset = 0;
256
partdev.d_slice = D_SLICENONE;
257
partdev.d_partition = D_PARTNONE;
258
259
dev->d_offset = 0;
260
table = NULL;
261
slice = dev->d_slice;
262
partition = dev->d_partition;
263
264
DPRINTF("%s unit %d, slice %d, partition %d => %p", disk_fmtdev(dev),
265
dev->dd.d_unit, dev->d_slice, dev->d_partition, od);
266
267
/* Determine disk layout. */
268
od->table = ptable_open(&partdev, mediasize / sectorsize, sectorsize,
269
ptblread);
270
if (od->table == NULL) {
271
DPRINTF("Can't read partition table");
272
rc = ENXIO;
273
goto out;
274
}
275
276
if (ptable_getsize(od->table, &mediasize) != 0) {
277
rc = ENXIO;
278
goto out;
279
}
280
od->mediasize = mediasize;
281
282
if (ptable_gettype(od->table) == PTABLE_BSD &&
283
partition >= 0) {
284
/* It doesn't matter what value has d_slice */
285
rc = ptable_getpart(od->table, &part, partition);
286
if (rc == 0) {
287
dev->d_offset = part.start;
288
od->entrysize = part.end - part.start + 1;
289
}
290
} else if (ptable_gettype(od->table) == PTABLE_ISO9660) {
291
dev->d_offset = 0;
292
od->entrysize = mediasize;
293
} else if (slice >= 0) {
294
/* Try to get information about partition */
295
if (slice == 0)
296
rc = ptable_getbestpart(od->table, &part);
297
else
298
rc = ptable_getpart(od->table, &part, slice);
299
if (rc != 0) /* Partition doesn't exist */
300
goto out;
301
dev->d_offset = part.start;
302
od->entrysize = part.end - part.start + 1;
303
slice = part.index;
304
if (ptable_gettype(od->table) == PTABLE_GPT) {
305
partition = D_PARTISGPT;
306
goto out; /* Nothing more to do */
307
} else if (partition == D_PARTISGPT) {
308
/*
309
* When we try to open GPT partition, but partition
310
* table isn't GPT, reset partition value to
311
* D_PARTWILD and try to autodetect appropriate value.
312
*/
313
partition = D_PARTWILD;
314
}
315
316
/*
317
* If partition is D_PARTNONE, then disk_open() was called
318
* to open raw MBR slice.
319
*/
320
if (partition == D_PARTNONE)
321
goto out;
322
323
/*
324
* If partition is D_PARTWILD and we are looking at a BSD slice,
325
* then try to read BSD label, otherwise return the
326
* whole MBR slice.
327
*/
328
if (partition == D_PARTWILD &&
329
part.type != PART_FREEBSD)
330
goto out;
331
/* Try to read BSD label */
332
table = ptable_open(dev, part.end - part.start + 1,
333
od->sectorsize, ptblread);
334
if (table == NULL) {
335
DPRINTF("Can't read BSD label");
336
rc = ENXIO;
337
goto out;
338
}
339
/*
340
* If slice contains BSD label and partition < 0, then
341
* assume the 'a' partition. Otherwise just return the
342
* whole MBR slice, because it can contain ZFS.
343
*/
344
if (partition < 0) {
345
if (ptable_gettype(table) != PTABLE_BSD)
346
goto out;
347
partition = 0;
348
}
349
rc = ptable_getpart(table, &part, partition);
350
if (rc != 0)
351
goto out;
352
dev->d_offset += part.start;
353
od->entrysize = part.end - part.start + 1;
354
}
355
out:
356
if (table != NULL)
357
ptable_close(table);
358
359
if (rc != 0) {
360
if (od->table != NULL)
361
ptable_close(od->table);
362
free(od);
363
DPRINTF("%s could not open", disk_fmtdev(dev));
364
} else {
365
/* Save the slice and partition number to the dev */
366
dev->d_slice = slice;
367
dev->d_partition = partition;
368
DPRINTF("%s offset %lld => %p", disk_fmtdev(dev),
369
(long long)dev->d_offset, od);
370
}
371
return (rc);
372
}
373
374
int
375
disk_close(struct disk_devdesc *dev)
376
{
377
struct open_disk *od;
378
379
od = (struct open_disk *)dev->dd.d_opendata;
380
DPRINTF("%s closed => %p", disk_fmtdev(dev), od);
381
ptable_close(od->table);
382
free(od);
383
return (0);
384
}
385
386
char *
387
disk_fmtdev(struct devdesc *vdev)
388
{
389
struct disk_devdesc *dev = (struct disk_devdesc *)vdev;
390
static char buf[128];
391
char *cp;
392
393
assert(vdev->d_dev->dv_type == DEVT_DISK);
394
cp = buf + sprintf(buf, "%s%d", dev->dd.d_dev->dv_name, dev->dd.d_unit);
395
if (dev->d_slice > D_SLICENONE) {
396
#ifdef LOADER_GPT_SUPPORT
397
if (dev->d_partition == D_PARTISGPT) {
398
sprintf(cp, "p%d:", dev->d_slice);
399
return (buf);
400
} else
401
#endif
402
#ifdef LOADER_MBR_SUPPORT
403
cp += sprintf(cp, "s%d", dev->d_slice);
404
#endif
405
}
406
if (dev->d_partition > D_PARTNONE)
407
cp += sprintf(cp, "%c", dev->d_partition + 'a');
408
strcat(cp, ":");
409
return (buf);
410
}
411
412
int
413
disk_parsedev(struct devdesc **idev, const char *devspec, const char **path)
414
{
415
int unit, slice, partition;
416
const char *np;
417
char *cp;
418
struct disk_devdesc *dev;
419
420
np = devspec + 4; /* Skip the leading 'disk' */
421
unit = -1;
422
/*
423
* If there is path/file info after the device info, then any missing
424
* slice or partition info should be considered a request to search for
425
* an appropriate partition. Otherwise we want to open the raw device
426
* itself and not try to fill in missing info by searching.
427
*/
428
if ((cp = strchr(np, ':')) != NULL && cp[1] != '\0') {
429
slice = D_SLICEWILD;
430
partition = D_PARTWILD;
431
} else {
432
slice = D_SLICENONE;
433
partition = D_PARTNONE;
434
}
435
436
if (*np != '\0' && *np != ':') {
437
unit = strtol(np, &cp, 10);
438
if (cp == np)
439
return (EUNIT);
440
#ifdef LOADER_GPT_SUPPORT
441
if (*cp == 'p') {
442
np = cp + 1;
443
slice = strtol(np, &cp, 10);
444
if (np == cp)
445
return (ESLICE);
446
/* we don't support nested partitions on GPT */
447
if (*cp != '\0' && *cp != ':')
448
return (EINVAL);
449
partition = D_PARTISGPT;
450
} else
451
#endif
452
#ifdef LOADER_MBR_SUPPORT
453
if (*cp == 's') {
454
np = cp + 1;
455
slice = strtol(np, &cp, 10);
456
if (np == cp)
457
return (ESLICE);
458
}
459
#endif
460
if (*cp != '\0' && *cp != ':') {
461
partition = *cp - 'a';
462
if (partition < 0)
463
return (EPART);
464
cp++;
465
}
466
} else
467
return (EINVAL);
468
469
if (*cp != '\0' && *cp != ':')
470
return (EINVAL);
471
dev = malloc(sizeof(*dev));
472
if (dev == NULL)
473
return (ENOMEM);
474
dev->dd.d_unit = unit;
475
dev->d_slice = slice;
476
dev->d_partition = partition;
477
*idev = &dev->dd;
478
if (path != NULL)
479
*path = (*cp == '\0') ? cp: cp + 1;
480
return (0);
481
}
482
483