]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/disk.c
Reduce reliance on sys/sysproto.h pollution
[FreeBSD/FreeBSD.git] / stand / common / disk.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
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 }