disks.c revision 1.19 1 /* $NetBSD: disks.c,v 1.19 2018/11/07 21:59:30 martin Exp $ */
2
3 /*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse
18 * or promote products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 /* disks.c -- routines to deal with finding disks and labeling disks. */
36
37
38 #include <errno.h>
39 #include <inttypes.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <util.h>
45 #include <uuid.h>
46
47 #include <sys/param.h>
48 #include <sys/sysctl.h>
49 #include <sys/swap.h>
50 #include <ufs/ufs/dinode.h>
51 #include <ufs/ffs/fs.h>
52 #define FSTYPENAMES
53 #include <sys/disklabel.h>
54 #include <sys/disklabel_gpt.h>
55
56 #include <dev/scsipi/scsipi_all.h>
57 #include <sys/scsiio.h>
58
59 #include <dev/ata/atareg.h>
60 #include <sys/ataio.h>
61
62 #include "defs.h"
63 #include "md.h"
64 #include "msg_defs.h"
65 #include "menu_defs.h"
66 #include "txtwalk.h"
67
68 /* Disk descriptions */
69 struct disk_desc {
70 char dd_name[SSTRSIZE];
71 char dd_descr[70];
72 bool dd_no_mbr, dd_no_part;
73 uint dd_cyl;
74 uint dd_head;
75 uint dd_sec;
76 uint dd_secsize;
77 uint dd_totsec;
78 };
79
80 /* gpt(8) use different filesystem names.
81 So, we cant use ./common/lib/libutil/getfstypename.c */
82 struct gptfs_t {
83 const char *name;
84 int id;
85 uuid_t uuid;
86 };
87 static const struct gptfs_t gpt_filesystems[] = {
88 { "swap", FS_SWAP, GPT_ENT_TYPE_NETBSD_SWAP, },
89 { "ffs", FS_BSDFFS, GPT_ENT_TYPE_NETBSD_FFS, },
90 { "lfs", FS_BSDLFS, GPT_ENT_TYPE_NETBSD_LFS, },
91 { "linux", FS_EX2FS, GPT_ENT_TYPE_LINUX_DATA, },
92 { "windows,", FS_MSDOS, GPT_ENT_TYPE_MS_BASIC_DATA, },
93 { "hfs", FS_HFS, GPT_ENT_TYPE_APPLE_HFS, },
94 { "ufs", FS_OTHER, GPT_ENT_TYPE_APPLE_UFS, },
95 { "ccd", FS_CCD, GPT_ENT_TYPE_NETBSD_CCD, },
96 { "raid", FS_RAID, GPT_ENT_TYPE_NETBSD_RAIDFRAME, },
97 { "cgd", FS_CGD, GPT_ENT_TYPE_NETBSD_CGD, },
98 { "efi", FS_OTHER, GPT_ENT_TYPE_EFI, },
99 { "bios", FS_OTHER, GPT_ENT_TYPE_BIOS, },
100 { NULL, -1, GPT_ENT_TYPE_UNUSED, },
101 };
102
103 /* Local prototypes */
104 static int foundffs(struct data *, size_t);
105 #ifdef USE_SYSVBFS
106 static int foundsysvbfs(struct data *, size_t);
107 #endif
108 static int fsck_preen(const char *, int, const char *, bool silent);
109 static void fixsb(const char *, const char *, char);
110 static bool is_gpt(const char *);
111 static int incoregpt(pm_devs_t *, partinfo *);
112
113
114 static bool tmpfs_on_var_shm(void);
115
116 const char *
117 getfslabelname(uint8_t f)
118 {
119 if (f >= __arraycount(fstypenames) || fstypenames[f] == NULL)
120 return "invalid";
121 return fstypenames[f];
122 }
123
124 /*
125 * Decide wether we want to mount a tmpfs on /var/shm: we do this always
126 * when the machine has more than 16 MB of user memory. On smaller machines,
127 * shm_open() and friends will not perform well anyway.
128 */
129 static bool
130 tmpfs_on_var_shm()
131 {
132 uint64_t ram;
133 size_t len;
134
135 len = sizeof(ram);
136 if (sysctlbyname("hw.usermem64", &ram, &len, NULL, 0))
137 return false;
138
139 return ram > 16UL*1024UL*1024UL;
140 }
141
142 /* from src/sbin/atactl/atactl.c
143 * extract_string: copy a block of bytes out of ataparams and make
144 * a proper string out of it, truncating trailing spaces and preserving
145 * strict typing. And also, not doing unaligned accesses.
146 */
147 static void
148 ata_extract_string(char *buf, size_t bufmax,
149 uint8_t *bytes, unsigned numbytes,
150 int needswap)
151 {
152 unsigned i;
153 size_t j;
154 unsigned char ch1, ch2;
155
156 for (i = 0, j = 0; i < numbytes; i += 2) {
157 ch1 = bytes[i];
158 ch2 = bytes[i+1];
159 if (needswap && j < bufmax-1) {
160 buf[j++] = ch2;
161 }
162 if (j < bufmax-1) {
163 buf[j++] = ch1;
164 }
165 if (!needswap && j < bufmax-1) {
166 buf[j++] = ch2;
167 }
168 }
169 while (j > 0 && buf[j-1] == ' ') {
170 j--;
171 }
172 buf[j] = '\0';
173 }
174
175 /*
176 * from src/sbin/scsictl/scsi_subr.c
177 */
178 #define STRVIS_ISWHITE(x) ((x) == ' ' || (x) == '\0' || (x) == (u_char)'\377')
179
180 static void
181 scsi_strvis(char *sdst, size_t dlen, const char *ssrc, size_t slen)
182 {
183 u_char *dst = (u_char *)sdst;
184 const u_char *src = (const u_char *)ssrc;
185
186 /* Trim leading and trailing blanks and NULs. */
187 while (slen > 0 && STRVIS_ISWHITE(src[0]))
188 ++src, --slen;
189 while (slen > 0 && STRVIS_ISWHITE(src[slen - 1]))
190 --slen;
191
192 while (slen > 0) {
193 if (*src < 0x20 || *src >= 0x80) {
194 /* non-printable characters */
195 dlen -= 4;
196 if (dlen < 1)
197 break;
198 *dst++ = '\\';
199 *dst++ = ((*src & 0300) >> 6) + '0';
200 *dst++ = ((*src & 0070) >> 3) + '0';
201 *dst++ = ((*src & 0007) >> 0) + '0';
202 } else if (*src == '\\') {
203 /* quote characters */
204 dlen -= 2;
205 if (dlen < 1)
206 break;
207 *dst++ = '\\';
208 *dst++ = '\\';
209 } else {
210 /* normal characters */
211 if (--dlen < 1)
212 break;
213 *dst++ = *src;
214 }
215 ++src, --slen;
216 }
217
218 *dst++ = 0;
219 }
220
221
222 static int
223 get_descr_scsi(struct disk_desc *dd, int fd)
224 {
225 struct scsipi_inquiry_data inqbuf;
226 struct scsipi_inquiry cmd;
227 scsireq_t req;
228 /* x4 in case every character is escaped, +1 for NUL. */
229 char vendor[(sizeof(inqbuf.vendor) * 4) + 1],
230 product[(sizeof(inqbuf.product) * 4) + 1],
231 revision[(sizeof(inqbuf.revision) * 4) + 1];
232 char size[5];
233 int error;
234
235 memset(&inqbuf, 0, sizeof(inqbuf));
236 memset(&cmd, 0, sizeof(cmd));
237 memset(&req, 0, sizeof(req));
238
239 cmd.opcode = INQUIRY;
240 cmd.length = sizeof(inqbuf);
241 memcpy(req.cmd, &cmd, sizeof(cmd));
242 req.cmdlen = sizeof(cmd);
243 req.databuf = &inqbuf;
244 req.datalen = sizeof(inqbuf);
245 req.timeout = 10000;
246 req.flags = SCCMD_READ;
247 req.senselen = SENSEBUFLEN;
248
249 error = ioctl(fd, SCIOCCOMMAND, &req);
250 if (error == -1 || req.retsts != SCCMD_OK)
251 return 0;
252
253 scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor,
254 sizeof(inqbuf.vendor));
255 scsi_strvis(product, sizeof(product), inqbuf.product,
256 sizeof(inqbuf.product));
257 scsi_strvis(revision, sizeof(revision), inqbuf.revision,
258 sizeof(inqbuf.revision));
259
260 humanize_number(size, sizeof(size),
261 (uint64_t)dd->dd_secsize * (uint64_t)dd->dd_totsec,
262 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
263
264 snprintf(dd->dd_descr, sizeof(dd->dd_descr),
265 "%s (%s, %s %s)",
266 dd->dd_name, size, vendor, product);
267
268 return 1;
269 }
270
271 static int
272 get_descr_ata(struct disk_desc *dd, int fd)
273 {
274 struct atareq req;
275 static union {
276 unsigned char inbuf[DEV_BSIZE];
277 struct ataparams inqbuf;
278 } inbuf;
279 struct ataparams *inqbuf = &inbuf.inqbuf;
280 char model[sizeof(inqbuf->atap_model)+1];
281 char size[5];
282 int error, needswap = 0;
283
284 memset(&inbuf, 0, sizeof(inbuf));
285 memset(&req, 0, sizeof(req));
286
287 req.flags = ATACMD_READ;
288 req.command = WDCC_IDENTIFY;
289 req.databuf = (void *)&inbuf;
290 req.datalen = sizeof(inbuf);
291 req.timeout = 1000;
292
293 error = ioctl(fd, ATAIOCCOMMAND, &req);
294 if (error == -1 || req.retsts != ATACMD_OK)
295 return 0;
296
297 #if BYTE_ORDER == LITTLE_ENDIAN
298 /*
299 * On little endian machines, we need to shuffle the string
300 * byte order. However, we don't have to do this for NEC or
301 * Mitsumi ATAPI devices
302 */
303
304 if (!(inqbuf->atap_config != WDC_CFG_CFA_MAGIC &&
305 (inqbuf->atap_config & WDC_CFG_ATAPI) &&
306 ((inqbuf->atap_model[0] == 'N' &&
307 inqbuf->atap_model[1] == 'E') ||
308 (inqbuf->atap_model[0] == 'F' &&
309 inqbuf->atap_model[1] == 'X')))) {
310 needswap = 1;
311 }
312 #endif
313
314 ata_extract_string(model, sizeof(model),
315 inqbuf->atap_model, sizeof(inqbuf->atap_model), needswap);
316 humanize_number(size, sizeof(size),
317 (uint64_t)dd->dd_secsize * (uint64_t)dd->dd_totsec,
318 "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
319
320 snprintf(dd->dd_descr, sizeof(dd->dd_descr), "%s (%s, %s)",
321 dd->dd_name, size, model);
322
323 return 1;
324 }
325
326 static void
327 get_descr(struct disk_desc *dd)
328 {
329 char diskpath[MAXPATHLEN];
330 int fd = -1;
331
332 fd = opendisk(dd->dd_name, O_RDONLY, diskpath, sizeof(diskpath), 0);
333 if (fd < 0)
334 goto done;
335
336 dd->dd_descr[0] = '\0';
337
338 /* try ATA */
339 if (get_descr_ata(dd, fd))
340 goto done;
341 /* try SCSI */
342 if (get_descr_scsi(dd, fd))
343 goto done;
344 /* XXX: get description from raid, cgd, vnd... */
345
346 done:
347 if (fd >= 0)
348 close(fd);
349 if (strlen(dd->dd_descr) == 0)
350 strcpy(dd->dd_descr, dd->dd_name);
351 }
352
353 /* disknames - contains device names without partition letters
354 * cdrom_devices - contains devices including partition letters
355 * returns the first entry in hw.disknames matching a cdrom_device, or
356 * first entry on error or no match
357 */
358 bool
359 get_default_cdrom(char *cd, size_t max_len)
360 {
361 static const char *cdrom_devices[] = { CD_NAMES, 0 };
362 static const char mib_name[] = "hw.disknames";
363 size_t len;
364 char *disknames;
365 char *last;
366 char *name;
367 const char **arg;
368 const char *cd_dev;
369
370 /* On error just use first entry in cdrom_devices */
371 if (sysctlbyname(mib_name, NULL, &len, NULL, 0) == -1)
372 return cdrom_devices[0];
373 if ((disknames = malloc(len + 2)) == 0) /* skip on malloc fail */
374 return cdrom_devices[0];
375
376 (void)sysctlbyname(mib_name, disknames, &len, NULL, 0);
377 for ((name = strtok_r(disknames, " ", &last)); name;
378 (name = strtok_r(NULL, " ", &last))) {
379 for (arg = cdrom_devices; *arg; ++arg) {
380 cd_dev = *arg;
381 /* skip unit and partition */
382 if (strncmp(cd_dev, name, strlen(cd_dev) - 2) != 0)
383 continue;
384 if (name != disknames)
385 strcpy(disknames, name);
386 strcat(disknames, "a");
387 /* XXX: leaks, but so what? */
388 return disknames;
389 }
390 }
391 free(disknames);
392 return cdrom_devices[0];
393 }
394
395 static void
396 get_wedge_descr(struct disk_desc *dd)
397 {
398 struct dkwedge_info dkw;
399 char buf[MAXPATHLEN];
400 int fd;
401
402 fd = opendisk(dd->dd_name, O_RDONLY, buf, sizeof(buf), 0);
403 if (fd == -1)
404 return;
405
406 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
407 fprintf(stderr, "device %s\n", dd->dd_name);
408 sprintf(dd->dd_descr, "%s (%s@%s)",
409 dkw.dkw_wname, dkw.dkw_devname, dkw.dkw_parent);
410 }
411 close(fd);
412 }
413
414 static bool
415 get_name_and_parent(const char *dev, char *name, char *parent)
416 {
417 struct dkwedge_info dkw;
418 char buf[MAXPATHLEN];
419 int fd;
420 bool res = false;
421
422 fd = opendisk(dev, O_RDONLY, buf, sizeof(buf), 0);
423 if (fd == -1)
424 return false;
425
426 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
427 strcpy(name, (const char *)dkw.dkw_wname);
428 strcpy(parent, dkw.dkw_parent);
429 res = true;
430 }
431 close(fd);
432 return res;
433 }
434
435 static bool
436 find_swap_part_on(const char *dev, char *swap_name)
437 {
438 struct dkwedge_info *dkw;
439 struct dkwedge_list dkwl;
440 char buf[MAXPATHLEN];
441 size_t bufsize;
442 int fd;
443 u_int i;
444 bool res = false;
445
446 dkw = NULL;
447 dkwl.dkwl_buf = dkw;
448 dkwl.dkwl_bufsize = 0;
449
450 fd = opendisk(dev, O_RDONLY, buf, sizeof(buf), 0);
451 if (fd == -1)
452 return false;
453
454 for (;;) {
455 if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1) {
456 dkwl.dkwl_ncopied = 0;
457 break;
458 }
459 if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied)
460 break;
461 bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
462 if (dkwl.dkwl_bufsize < bufsize) {
463 dkw = realloc(dkwl.dkwl_buf, bufsize);
464 if (dkw == NULL)
465 break;
466 dkwl.dkwl_buf = dkw;
467 dkwl.dkwl_bufsize = bufsize;
468 }
469 }
470
471 for (i = 0; i < dkwl.dkwl_nwedges; i++) {
472 res = strcmp(dkw[i].dkw_ptype, DKW_PTYPE_SWAP) == 0;
473 if (res) {
474 strcpy(swap_name, (const char*)dkw[i].dkw_wname);
475 break;
476 }
477 }
478
479 close(fd);
480
481 return res;
482 }
483
484 static bool
485 is_ffs_wedge(const char *dev)
486 {
487 struct dkwedge_info dkw;
488 char buf[MAXPATHLEN];
489 int fd;
490 bool res;
491
492 fd = opendisk(dev, O_RDONLY, buf, sizeof(buf), 0);
493 if (fd == -1)
494 return false;
495
496 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == -1)
497 return false;
498
499 res = strcmp(dkw.dkw_ptype, DKW_PTYPE_FFS) == 0;
500 close(fd);
501
502 return res;
503 }
504
505 /*
506 * Does this device match an entry in our default CDROM device list?
507 */
508 static bool
509 is_cdrom_device(const char *dev)
510 {
511 static const char *cdrom_devices[] = { CD_NAMES, 0 };
512 char pat[SSTRSIZE], comp[SSTRSIZE], *star, *p;
513 const char **dev_pat;
514
515 /* trim device number off */
516 strcpy(comp, dev);
517 for (p = comp + strlen(comp) - 1; p != comp; p--)
518 if (!isdigit((unsigned char)*p))
519 break;
520 if (p != comp)
521 p[1] = 0;
522
523 for (dev_pat = cdrom_devices; *dev_pat; dev_pat++) {
524 strcpy(pat, *dev_pat);
525 star = strchr(pat, '*');
526 if (star) {
527 *star = 0;
528 if (strcmp(comp, pat) == 0)
529 return true;
530 } else {
531 if (strcmp(dev, pat) == 0)
532 return true;
533 }
534 }
535
536 return false;
537 }
538
539 /*
540 * Multi-purpose helper function:
541 * iterate all known disks, either
542 * - skip all CD devices
543 * - recognize the first available CD device and set its name
544 * When doing non-CDs, optionally skip non-partionable devices
545 * (i.e. wedges).
546 */
547 static int
548 get_disks(struct disk_desc *dd, bool with_non_partitionable,
549 char *cd_dev, size_t max_len)
550 {
551 static const int mib[] = { CTL_HW, HW_DISKNAMES };
552 static const unsigned int miblen = __arraycount(mib);
553 const char *xd;
554 struct disklabel l;
555 int numdisks;
556 size_t len;
557 char *disk_names;
558
559 /* initialize */
560 numdisks = 0;
561
562 if (sysctl(mib, miblen, NULL, &len, NULL, 0) == -1)
563 return 0;
564 disk_names = malloc(len);
565 if (disk_names == NULL)
566 return 0;
567
568 if (sysctl(mib, miblen, disk_names, &len, NULL, 0) == -1) {
569 free(disk_names);
570 return 0;
571 }
572
573 for (xd = strtok(disk_names, " "); xd != NULL; xd = strtok(NULL, " ")) {
574 /* is this a CD device? */
575 if (is_cdrom_device(xd)) {
576 if (cd_dev && max_len) {
577 /* return first found CD device name */
578 strlcpy(cd_dev, xd, max_len);
579 return 1;
580 } else {
581 /* skip this device */
582 continue;
583 }
584 }
585
586 strlcpy(dd->dd_name, xd, sizeof dd->dd_name - 2);
587 dd->dd_no_mbr = false;
588 dd->dd_no_part = false;
589
590 if (strncmp(xd, "dk", 2) == 0) {
591 char *endp;
592 int e;
593
594 /* if this device is dkNNNN, no partitioning is possible */
595 strtou(xd+2, &endp, 10, 0, INT_MAX, &e);
596 if (endp && *endp == 0 && e == 0)
597 dd->dd_no_part = true;
598 }
599 if (dd->dd_no_part && !with_non_partitionable)
600 continue;
601
602 if (!get_geom(dd->dd_name, &l)) {
603 if (errno == ENOENT)
604 break;
605 if (errno != ENOTTY || !dd->dd_no_part)
606 /*
607 * Allow plain partitions,
608 * like already existing wedges
609 * (like dk0) if marked as
610 * non-partitioning device.
611 * For all other cases, continue
612 * with the next disk.
613 */
614 continue;
615 if (!is_ffs_wedge(dd->dd_name))
616 continue;
617 }
618
619 /*
620 * Exclude a disk mounted as root partition,
621 * in case of install-image on a USB memstick.
622 */
623 if (is_active_rootpart(dd->dd_name, 0))
624 continue;
625
626 if (!dd->dd_no_part) {
627 dd->dd_cyl = l.d_ncylinders;
628 dd->dd_head = l.d_ntracks;
629 dd->dd_sec = l.d_nsectors;
630 dd->dd_secsize = l.d_secsize;
631 dd->dd_totsec = l.d_secperunit;
632 }
633 if (dd->dd_no_part)
634 get_wedge_descr(dd);
635 else
636 get_descr(dd);
637 dd++;
638 numdisks++;
639 if (numdisks == MAX_DISKS)
640 break;
641 }
642 free(disk_names);
643 return numdisks;
644 }
645
646 int
647 find_disks(const char *doingwhat)
648 {
649 struct disk_desc disks[MAX_DISKS];
650 menu_ent dsk_menu[nelem(disks) + 1]; // + 1 for extended partitioning entry
651 struct disk_desc *disk;
652 int i = 0, skipped = 0;
653 int already_found, numdisks, selected_disk = -1;
654 int menu_no;
655 pm_devs_t *pm_i, *pm_last = NULL;
656
657 /* Find disks. */
658 numdisks = get_disks(disks, partman_go <= 0, NULL, 0);
659
660 /* need a redraw here, kernel messages hose everything */
661 touchwin(stdscr);
662 refresh();
663 /* Kill typeahead, it won't be what the user had in mind */
664 fpurge(stdin);
665
666 /*
667 * partman_go: <0 - we want to see menu with extended partitioning
668 * ==0 - we want to see simple select disk menu
669 * >0 - we do not want to see any menus, just detect
670 * all disks
671 */
672 if (partman_go <= 0) {
673 if (numdisks == 0) {
674 /* No disks found! */
675 msg_display(MSG_nodisk);
676 process_menu(MENU_ok, NULL);
677 /*endwin();*/
678 return -1;
679 } else {
680 /* One or more disks found! */
681 for (i = 0; i < numdisks; i++) {
682 dsk_menu[i].opt_name =
683 disks[i].dd_descr;
684 dsk_menu[i].opt_menu = OPT_NOMENU;
685 dsk_menu[i].opt_flags = OPT_EXIT;
686 dsk_menu[i].opt_action = set_menu_select;
687 }
688 if (partman_go < 0) {
689 dsk_menu[i].opt_name = MSG_partman;
690 dsk_menu[i].opt_menu = OPT_NOMENU;
691 dsk_menu[i].opt_flags = OPT_EXIT;
692 dsk_menu[i].opt_action = set_menu_select;
693 }
694 menu_no = new_menu(MSG_Available_disks,
695 dsk_menu, numdisks
696 + ((partman_go<0)?1:0), -1,
697 4, 0, 0, MC_SCROLL,
698 NULL, NULL, NULL, NULL, NULL);
699 if (menu_no == -1)
700 return -1;
701 msg_display(MSG_ask_disk, doingwhat);
702 process_menu(menu_no, &selected_disk);
703 free_menu(menu_no);
704 }
705 if (partman_go < 0 && selected_disk == numdisks) {
706 partman_go = 1;
707 return -2;
708 } else
709 partman_go = 0;
710 if (selected_disk < 0 || selected_disk >= numdisks)
711 return -1;
712 }
713
714 /* Fill pm struct with device(s) info */
715 for (i = 0; i < numdisks; i++) {
716 if (! partman_go)
717 disk = disks + selected_disk;
718 else {
719 disk = disks + i;
720 already_found = 0;
721 SLIST_FOREACH(pm_i, &pm_head, l) {
722 pm_last = pm_i;
723 if (!already_found &&
724 strcmp(pm_i->diskdev, disk->dd_name) == 0) {
725 pm_i->found = 1;
726 break;
727 }
728 }
729 if (pm_i != NULL && pm_i->found)
730 /* We already added this device, skipping */
731 continue;
732 }
733 pm = pm_new;
734 pm->found = 1;
735 pm->bootable = 0;
736 pm->pi.menu_no = -1;
737 pm->disktype = "unknown";
738 pm->doessf = "";
739 strlcpy(pm->diskdev, disk->dd_name, sizeof pm->diskdev);
740 strlcpy(pm->diskdev_descr, disk->dd_descr, sizeof pm->diskdev_descr);
741 /* Use as a default disk if the user has the sets on a local disk */
742 strlcpy(localfs_dev, disk->dd_name, sizeof localfs_dev);
743
744 pm->gpt = is_gpt(pm->diskdev);
745 pm->no_mbr = disk->dd_no_mbr || pm->gpt;
746 pm->no_part = disk->dd_no_part;
747 if (!pm->no_part) {
748 pm->sectorsize = disk->dd_secsize;
749 pm->dlcyl = disk->dd_cyl;
750 pm->dlhead = disk->dd_head;
751 pm->dlsec = disk->dd_sec;
752 pm->dlsize = disk->dd_totsec;
753 if (pm->dlsize == 0)
754 pm->dlsize = disk->dd_cyl * disk->dd_head * disk->dd_sec;
755 if (pm->dlsize > UINT32_MAX && ! partman_go) {
756 if (logfp)
757 fprintf(logfp, "Cannot process disk %s: too big size (%d)\n",
758 pm->diskdev, (int)pm->dlsize);
759 msg_display(MSG_toobigdisklabel);
760 process_menu(MENU_ok, NULL);
761 return -1;
762 }
763 } else {
764 pm->sectorsize = 0;
765 pm->dlcyl = 0;
766 pm->dlhead = 0;
767 pm->dlsec = 0;
768 pm->dlsize = 0;
769 pm->rootpart = -1;
770 pm->no_mbr = 1;
771 memset(&pm->bsdlabel, 0, sizeof(pm->bsdlabel));
772 }
773 pm->dlcylsize = pm->dlhead * pm->dlsec;
774
775 label_read();
776 if (partman_go) {
777 pm_getrefdev(pm_new);
778 if (SLIST_EMPTY(&pm_head) || pm_last == NULL)
779 SLIST_INSERT_HEAD(&pm_head, pm_new, l);
780 else
781 SLIST_INSERT_AFTER(pm_last, pm_new, l);
782 pm_new = malloc(sizeof (pm_devs_t));
783 memset(pm_new, 0, sizeof *pm_new);
784 } else
785 /* We is not in partman and do not want to process all devices, exit */
786 break;
787 }
788
789 return numdisks-skipped;
790 }
791
792
793 void
794 label_read(void)
795 {
796
797 if (pm->no_part)
798 return;
799
800 check_available_binaries();
801
802 /* Get existing/default label */
803 memset(&pm->oldlabel, 0, sizeof pm->oldlabel);
804 if (!have_gpt || !pm->gpt)
805 incorelabel(pm->diskdev, pm->oldlabel);
806 else
807 incoregpt(pm, pm->oldlabel);
808 /* Set 'target' label to current label in case we don't change it */
809 memcpy(&pm->bsdlabel, &pm->oldlabel, sizeof pm->bsdlabel);
810 #ifndef NO_DISKLABEL
811 if (! pm->gpt)
812 savenewlabel(pm->oldlabel, getmaxpartitions());
813 #endif
814 }
815
816 void
817 fmt_fspart(menudesc *m, int ptn, void *arg)
818 {
819 unsigned int poffset, psize, pend;
820 const char *desc;
821 static const char *Yes;
822 partinfo *p = pm->bsdlabel + ptn;
823
824 if (Yes == NULL)
825 Yes = msg_string(MSG_Yes);
826
827 poffset = p->pi_offset / sizemult;
828 psize = p->pi_size / sizemult;
829 if (psize == 0)
830 pend = 0;
831 else
832 pend = (p->pi_offset + p->pi_size) / sizemult - 1;
833
834 if (p->pi_fstype == FS_BSDFFS)
835 if (p->pi_flags & PIF_FFSv2)
836 desc = "FFSv2";
837 else
838 desc = "FFSv1";
839 else
840 desc = getfslabelname(p->pi_fstype);
841
842 #ifdef PART_BOOT
843 if (ptn == PART_BOOT)
844 desc = msg_string(MSG_Boot_partition_cant_change);
845 #endif
846 if (ptn == getrawpartition())
847 desc = msg_string(MSG_Whole_disk_cant_change);
848 else {
849 if (ptn == PART_C)
850 desc = msg_string(MSG_NetBSD_partition_cant_change);
851 }
852
853 wprintw(m->mw, msg_string(MSG_fspart_row),
854 poffset, pend, psize, desc,
855 p->pi_flags & PIF_NEWFS ? Yes : "",
856 p->pi_flags & PIF_MOUNT ? Yes : "",
857 p->pi_mount);
858 }
859
860 /*
861 * Label a disk using an MD-specific string DISKLABEL_CMD for
862 * to invoke disklabel.
863 * if MD code does not define DISKLABEL_CMD, this is a no-op.
864 *
865 * i386 port uses "/sbin/disklabel -w -r", just like i386
866 * miniroot scripts, though this may leave a bogus incore label.
867 *
868 * Sun ports should use DISKLABEL_CMD "/sbin/disklabel -w"
869 * to get incore to ondisk inode translation for the Sun proms.
870 */
871 int
872 write_disklabel (void)
873 {
874 int rv = 0;
875
876 if (pm && pm->no_part)
877 return 0;
878
879 #ifdef DISKLABEL_CMD
880 /* disklabel the disk */
881 rv = run_program(RUN_DISPLAY, "%s -f /tmp/disktab %s '%s'",
882 DISKLABEL_CMD, pm->diskdev, pm->bsddiskname);
883 if (rv == 0)
884 update_wedges(pm->diskdev);
885 #endif
886 return rv;
887 }
888
889
890 static int
891 ptn_sort(const void *a, const void *b)
892 {
893 return strcmp(pm->bsdlabel[*(const int *)a].pi_mount,
894 pm->bsdlabel[*(const int *)b].pi_mount);
895 }
896
897 int
898 make_filesystems(void)
899 {
900 unsigned int i;
901 int ptn;
902 int ptn_order[nelem(pm->bsdlabel)];
903 int error = 0;
904 unsigned int maxpart = getmaxpartitions();
905 char *newfs = NULL, *dev = NULL, *devdev = NULL;
906 partinfo *lbl;
907
908 if (pm->no_part) {
909 /* check if this target device already has a ffs */
910 error = fsck_preen(pm->diskdev, -1, "ffs", true);
911 if (error) {
912 if (!ask_noyes(MSG_No_filesystem_newfs))
913 return EINVAL;
914 error = run_program(RUN_DISPLAY | RUN_PROGRESS,
915 "/sbin/newfs -V2 -O2 /dev/r%s", pm->diskdev);
916 }
917
918 md_pre_mount();
919
920 make_target_dir("/");
921 asprintf(&devdev, "/dev/%s", pm->diskdev);
922 if (devdev == NULL)
923 return (ENOMEM);
924 error = target_mount_do("-o async", devdev, "/");
925 if (error) {
926 msg_display(MSG_mountfail, devdev, ' ',
927 "/");
928 process_menu(MENU_ok, NULL);
929 }
930 free(devdev);
931 return error;
932 }
933
934 if (maxpart > nelem(pm->bsdlabel))
935 maxpart = nelem(pm->bsdlabel);
936
937 /* Making new file systems and mounting them */
938
939 /* sort to ensure /usr/local is mounted after /usr (etc) */
940 for (i = 0; i < maxpart; i++)
941 ptn_order[i] = i;
942 qsort(ptn_order, maxpart, sizeof ptn_order[0], ptn_sort);
943
944 for (i = 0; i < maxpart; i++) {
945 /*
946 * newfs and mount. For now, process only BSD filesystems.
947 * but if this is the mounted-on root, has no mount
948 * point defined, or is marked preserve, don't touch it!
949 */
950 ptn = ptn_order[i];
951 lbl = pm->bsdlabel + ptn;
952
953 if (is_active_rootpart(pm->diskdev, ptn))
954 continue;
955
956 if (*lbl->pi_mount == 0)
957 /* No mount point */
958 continue;
959
960 if (pm->isspecial) {
961 asprintf(&dev, "%s", pm->diskdev);
962 ptn = 0 - 'a';
963 } else {
964 asprintf(&dev, "%s%c", pm->diskdev, 'a' + ptn);
965 }
966 if (dev == NULL)
967 return (ENOMEM);
968 asprintf(&devdev, "/dev/%s", dev);
969 if (devdev == NULL)
970 return (ENOMEM);
971
972 newfs = NULL;
973 lbl->mnt_opts = NULL;
974 lbl->fsname = NULL;
975 switch (lbl->pi_fstype) {
976 case FS_APPLEUFS:
977 asprintf(&newfs, "/sbin/newfs %s%.0d",
978 lbl->pi_isize != 0 ? "-i" : "", lbl->pi_isize);
979 lbl->mnt_opts = "-tffs -o async";
980 lbl->fsname = "ffs";
981 break;
982 case FS_BSDFFS:
983 asprintf(&newfs,
984 "/sbin/newfs -V2 -O %d -b %d -f %d%s%.0d",
985 lbl->pi_flags & PIF_FFSv2 ? 2 : 1,
986 lbl->pi_fsize * lbl->pi_frag, lbl->pi_fsize,
987 lbl->pi_isize != 0 ? " -i " : "", lbl->pi_isize);
988 if (lbl->pi_flags & PIF_LOG)
989 lbl->mnt_opts = "-tffs -o log";
990 else
991 lbl->mnt_opts = "-tffs -o async";
992 lbl->fsname = "ffs";
993 break;
994 case FS_BSDLFS:
995 asprintf(&newfs, "/sbin/newfs_lfs -b %d",
996 lbl->pi_fsize * lbl->pi_frag);
997 lbl->mnt_opts = "-tlfs";
998 lbl->fsname = "lfs";
999 break;
1000 case FS_MSDOS:
1001 #ifdef USE_NEWFS_MSDOS
1002 asprintf(&newfs, "/sbin/newfs_msdos");
1003 #endif
1004 lbl->mnt_opts = "-tmsdos";
1005 lbl->fsname = "msdos";
1006 break;
1007 #ifdef USE_SYSVBFS
1008 case FS_SYSVBFS:
1009 asprintf(&newfs, "/sbin/newfs_sysvbfs");
1010 lbl->mnt_opts = "-tsysvbfs";
1011 lbl->fsname = "sysvbfs";
1012 break;
1013 #endif
1014 #ifdef USE_EXT2FS
1015 case FS_EX2FS:
1016 asprintf(&newfs, "/sbin/newfs_ext2fs");
1017 lbl->mnt_opts = "-text2fs";
1018 lbl->fsname = "ext2fs";
1019 break;
1020 #endif
1021 }
1022 if (lbl->pi_flags & PIF_NEWFS && newfs != NULL) {
1023 #ifdef USE_NEWFS_MSDOS
1024 if (lbl->pi_fstype == FS_MSDOS) {
1025 /* newfs only if mount fails */
1026 if (run_program(RUN_SILENT | RUN_ERROR_OK,
1027 "mount -rt msdos /dev/%s /mnt2", dev) != 0)
1028 error = run_program(
1029 RUN_DISPLAY | RUN_PROGRESS,
1030 "%s /dev/r%s",
1031 newfs, dev);
1032 else {
1033 run_program(RUN_SILENT | RUN_ERROR_OK,
1034 "umount /mnt2");
1035 error = 0;
1036 }
1037 } else
1038 #endif
1039 error = run_program(RUN_DISPLAY | RUN_PROGRESS,
1040 "%s /dev/r%s", newfs, dev);
1041 } else {
1042 /* We'd better check it isn't dirty */
1043 error = fsck_preen(pm->diskdev, ptn, lbl->fsname, false);
1044 }
1045 free(newfs);
1046 if (error != 0) {
1047 free(devdev);
1048 free(dev);
1049 return error;
1050 }
1051
1052 lbl->pi_flags ^= PIF_NEWFS;
1053 md_pre_mount();
1054
1055 if (partman_go == 0 && lbl->pi_flags & PIF_MOUNT &&
1056 lbl->mnt_opts != NULL) {
1057 make_target_dir(lbl->pi_mount);
1058 error = target_mount_do(lbl->mnt_opts, devdev, lbl->pi_mount);
1059 if (error) {
1060 msg_display(MSG_mountfail, dev, ' ', lbl->pi_mount);
1061 process_menu(MENU_ok, NULL);
1062 free(devdev);
1063 free(dev);
1064 return error;
1065 }
1066 }
1067 free(devdev);
1068 free(dev);
1069 }
1070 return 0;
1071 }
1072
1073 int
1074 make_fstab(void)
1075 {
1076 FILE *f;
1077 int i, swap_dev = -1;
1078 const char *dump_dev;
1079 char *dev = NULL;
1080 pm_devs_t *pm_i;
1081 #ifndef HAVE_TMPFS
1082 pm_devs_t *pm_with_swap = NULL;
1083 #endif
1084
1085 /* Create the fstab. */
1086 make_target_dir("/etc");
1087 f = target_fopen("/etc/fstab", "w");
1088 scripting_fprintf(NULL, "cat <<EOF >%s/etc/fstab\n", target_prefix());
1089
1090 if (logfp)
1091 (void)fprintf(logfp,
1092 "Making %s/etc/fstab (%s).\n", target_prefix(), pm->diskdev);
1093
1094 if (f == NULL) {
1095 msg_display(MSG_createfstab);
1096 if (logfp)
1097 (void)fprintf(logfp, "Failed to make /etc/fstab!\n");
1098 process_menu(MENU_ok, NULL);
1099 #ifndef DEBUG
1100 return 1;
1101 #else
1102 f = stdout;
1103 #endif
1104 }
1105
1106 scripting_fprintf(f, "# NetBSD /etc/fstab\n# See /usr/share/examples/"
1107 "fstab/ for more examples.\n");
1108
1109 if (pm->no_part) {
1110 /* single dk? target */
1111 char buf[200], parent[200], swap[200], *prompt;
1112 int res;
1113
1114 if (!get_name_and_parent(pm->diskdev, buf, parent))
1115 goto done_with_disks;
1116 scripting_fprintf(f, "NAME=%s\t/\tffs\trw\t\t1 1\n",
1117 buf);
1118 if (!find_swap_part_on(parent, swap))
1119 goto done_with_disks;
1120 asprintf(&prompt, msg_string(MSG_Auto_add_swap_part),
1121 swap, parent);
1122 res = ask_yesno(prompt);
1123 free(prompt);
1124 if (res)
1125 scripting_fprintf(f, "NAME=%s\tnone"
1126 "\tswap\tsw,dp\t\t0 0\n", swap);
1127 goto done_with_disks;
1128 }
1129
1130 if (! partman_go) {
1131 /* We want to process only one disk... */
1132 pm_i = pm;
1133 goto onlyonediskinfstab;
1134 }
1135 SLIST_FOREACH(pm_i, &pm_head, l) {
1136 onlyonediskinfstab:
1137 for (i = 0; i < getmaxpartitions(); i++) {
1138 const char *s = "";
1139 const char *mp = pm_i->bsdlabel[i].pi_mount;
1140 const char *fstype = "ffs";
1141 int fsck_pass = 0, dump_freq = 0;
1142
1143 if (dev != NULL)
1144 free(dev);
1145 if (pm_i->isspecial)
1146 asprintf(&dev, "%s", pm_i->diskdev);
1147 else
1148 asprintf(&dev, "%s%c", pm_i->diskdev, 'a' + i);
1149 if (dev == NULL)
1150 return (ENOMEM);
1151
1152 if (!*mp) {
1153 /*
1154 * No mount point specified, comment out line and
1155 * use /mnt as a placeholder for the mount point.
1156 */
1157 s = "# ";
1158 mp = "/mnt";
1159 }
1160
1161 switch (pm_i->bsdlabel[i].pi_fstype) {
1162 case FS_UNUSED:
1163 continue;
1164 case FS_BSDLFS:
1165 /* If there is no LFS, just comment it out. */
1166 if (!check_lfs_progs())
1167 s = "# ";
1168 fstype = "lfs";
1169 /* FALLTHROUGH */
1170 case FS_BSDFFS:
1171 fsck_pass = (strcmp(mp, "/") == 0) ? 1 : 2;
1172 dump_freq = 1;
1173 break;
1174 case FS_MSDOS:
1175 fstype = "msdos";
1176 break;
1177 case FS_SWAP:
1178 if (pm_i->isspecial)
1179 continue;
1180 if (swap_dev == -1) {
1181 swap_dev = i;
1182 dump_dev = ",dp";
1183 #ifndef HAVE_TMPFS
1184 pm_with_swap = pm_i;
1185 #endif
1186 } else {
1187 dump_dev = "";
1188 }
1189 scripting_fprintf(f, "/dev/%s\t\tnone\tswap\tsw%s\t\t 0 0\n",
1190 dev, dump_dev);
1191 continue;
1192 #ifdef USE_SYSVBFS
1193 case FS_SYSVBFS:
1194 fstype = "sysvbfs";
1195 make_target_dir("/stand");
1196 break;
1197 #endif
1198 default:
1199 fstype = "???";
1200 s = "# ";
1201 break;
1202 }
1203 /* The code that remounts root rw doesn't check the partition */
1204 if (strcmp(mp, "/") == 0 && !(pm_i->bsdlabel[i].pi_flags & PIF_MOUNT))
1205 s = "# ";
1206
1207 scripting_fprintf(f,
1208 "%s/dev/%s\t\t%s\t%s\trw%s%s%s%s%s%s%s%s\t\t %d %d\n",
1209 s, dev, mp, fstype,
1210 pm_i->bsdlabel[i].pi_flags & PIF_LOG ? ",log" : "",
1211 pm_i->bsdlabel[i].pi_flags & PIF_MOUNT ? "" : ",noauto",
1212 pm_i->bsdlabel[i].pi_flags & PIF_ASYNC ? ",async" : "",
1213 pm_i->bsdlabel[i].pi_flags & PIF_NOATIME ? ",noatime" : "",
1214 pm_i->bsdlabel[i].pi_flags & PIF_NODEV ? ",nodev" : "",
1215 pm_i->bsdlabel[i].pi_flags & PIF_NODEVMTIME ? ",nodevmtime" : "",
1216 pm_i->bsdlabel[i].pi_flags & PIF_NOEXEC ? ",noexec" : "",
1217 pm_i->bsdlabel[i].pi_flags & PIF_NOSUID ? ",nosuid" : "",
1218 dump_freq, fsck_pass);
1219 if (pm_i->isspecial)
1220 /* Special device (such as dk*) have only one partition */
1221 break;
1222 }
1223 /* Simple install, only one disk */
1224 if (!partman_go)
1225 break;
1226 }
1227 done_with_disks:
1228 if (tmp_ramdisk_size != 0) {
1229 #ifdef HAVE_TMPFS
1230 scripting_fprintf(f, "tmpfs\t\t/tmp\ttmpfs\trw,-m=1777,-s=%"
1231 PRIi64 "\n",
1232 tmp_ramdisk_size * 512);
1233 #else
1234 if (swap_dev != -1 && pm_with_swap != NULL)
1235 scripting_fprintf(f, "/dev/%s%c\t\t/tmp\tmfs\trw,-s=%"
1236 PRIi64 "\n",
1237 pm_with_swap->diskdev, 'a' + swap_dev, tmp_ramdisk_size);
1238 else
1239 scripting_fprintf(f, "swap\t\t/tmp\tmfs\trw,-s=%"
1240 PRIi64 "\n",
1241 tmp_ramdisk_size);
1242 #endif
1243 }
1244
1245 if (cdrom_dev[0] == 0)
1246 get_default_cdrom(cdrom_dev, sizeof(cdrom_dev));
1247
1248 /* Add /kern, /proc and /dev/pts to fstab and make mountpoint. */
1249 scripting_fprintf(f, "kernfs\t\t/kern\tkernfs\trw\n");
1250 scripting_fprintf(f, "ptyfs\t\t/dev/pts\tptyfs\trw\n");
1251 scripting_fprintf(f, "procfs\t\t/proc\tprocfs\trw\n");
1252 scripting_fprintf(f, "/dev/%s\t\t/cdrom\tcd9660\tro,noauto\n",
1253 cdrom_dev);
1254 scripting_fprintf(f, "%stmpfs\t\t/var/shm\ttmpfs\trw,-m1777,-sram%%25\n",
1255 tmpfs_on_var_shm() ? "" : "#");
1256 make_target_dir("/kern");
1257 make_target_dir("/proc");
1258 make_target_dir("/dev/pts");
1259 make_target_dir("/cdrom");
1260 make_target_dir("/var/shm");
1261
1262 scripting_fprintf(NULL, "EOF\n");
1263
1264 if (dev != NULL)
1265 free(dev);
1266 fclose(f);
1267 fflush(NULL);
1268 return 0;
1269 }
1270
1271
1272
1273 static int
1274 /*ARGSUSED*/
1275 foundffs(struct data *list, size_t num)
1276 {
1277 int error;
1278
1279 if (num < 2 || strcmp(list[1].u.s_val, "/") == 0 ||
1280 strstr(list[2].u.s_val, "noauto") != NULL)
1281 return 0;
1282
1283 error = fsck_preen(list[0].u.s_val, ' '-'a', "ffs", false);
1284 if (error != 0)
1285 return error;
1286
1287 error = target_mount("", list[0].u.s_val, ' '-'a', list[1].u.s_val);
1288 if (error != 0) {
1289 msg_display(MSG_mount_failed, list[0].u.s_val);
1290 if (!ask_noyes(NULL))
1291 return error;
1292 }
1293 return 0;
1294 }
1295
1296 #ifdef USE_SYSVBFS
1297 static int
1298 /*ARGSUSED*/
1299 foundsysvbfs(struct data *list, size_t num)
1300 {
1301 int error;
1302
1303 if (num < 2 || strcmp(list[1].u.s_val, "/") == 0 ||
1304 strstr(list[2].u.s_val, "noauto") != NULL)
1305 return 0;
1306
1307 error = target_mount("", list[0].u.s_val, ' '-'a', list[1].u.s_val);
1308 if (error != 0)
1309 return error;
1310 return 0;
1311 }
1312 #endif
1313
1314 /*
1315 * Do an fsck. On failure, inform the user by showing a warning
1316 * message and doing menu_ok() before proceeding.
1317 * Returns 0 on success, or nonzero return code from fsck() on failure.
1318 */
1319 static int
1320 fsck_preen(const char *disk, int ptn, const char *fsname, bool silent)
1321 {
1322 char *prog;
1323 int error;
1324
1325 ptn = (ptn < 0)? 0 : 'a' + ptn;
1326 if (fsname == NULL)
1327 return 0;
1328 /* first, check if fsck program exists, if not, assume ok */
1329 asprintf(&prog, "/sbin/fsck_%s", fsname);
1330 if (prog == NULL)
1331 return 0;
1332 if (access(prog, X_OK) != 0) {
1333 free(prog);
1334 return 0;
1335 }
1336 if (!strcmp(fsname,"ffs"))
1337 fixsb(prog, disk, ptn);
1338 error = run_program(silent? RUN_SILENT|RUN_ERROR_OK : 0, "%s -p -q /dev/r%s%c", prog, disk, ptn);
1339 free(prog);
1340 if (error != 0 && !silent) {
1341 msg_display(MSG_badfs, disk, ptn, error);
1342 if (ask_noyes(NULL))
1343 error = 0;
1344 /* XXX at this point maybe we should run a full fsck? */
1345 }
1346 return error;
1347 }
1348
1349 /* This performs the same function as the etc/rc.d/fixsb script
1350 * which attempts to correct problems with ffs1 filesystems
1351 * which may have been introduced by booting a netbsd-current kernel
1352 * from between April of 2003 and January 2004. For more information
1353 * This script was developed as a response to NetBSD pr install/25138
1354 * Additional prs regarding the original issue include:
1355 * bin/17910 kern/21283 kern/21404 port-macppc/23925 port-macppc/23926
1356 */
1357 static void
1358 fixsb(const char *prog, const char *disk, char ptn)
1359 {
1360 int fd;
1361 int rval;
1362 union {
1363 struct fs fs;
1364 char buf[SBLOCKSIZE];
1365 } sblk;
1366 struct fs *fs = &sblk.fs;
1367
1368 snprintf(sblk.buf, sizeof(sblk.buf), "/dev/r%s%c",
1369 disk, ptn == ' ' ? 0 : ptn);
1370 fd = open(sblk.buf, O_RDONLY);
1371 if (fd == -1)
1372 return;
1373
1374 /* Read ffsv1 main superblock */
1375 rval = pread(fd, sblk.buf, sizeof sblk.buf, SBLOCK_UFS1);
1376 close(fd);
1377 if (rval != sizeof sblk.buf)
1378 return;
1379
1380 if (fs->fs_magic != FS_UFS1_MAGIC &&
1381 fs->fs_magic != FS_UFS1_MAGIC_SWAPPED)
1382 /* Not FFSv1 */
1383 return;
1384 if (fs->fs_old_flags & FS_FLAGS_UPDATED)
1385 /* properly updated fslevel 4 */
1386 return;
1387 if (fs->fs_bsize != fs->fs_maxbsize)
1388 /* not messed up */
1389 return;
1390
1391 /*
1392 * OK we have a munged fs, first 'upgrade' to fslevel 4,
1393 * We specify -b16 in order to stop fsck bleating that the
1394 * sb doesn't match the first alternate.
1395 */
1396 run_program(RUN_DISPLAY | RUN_PROGRESS,
1397 "%s -p -b 16 -c 4 /dev/r%s%c", prog, disk, ptn);
1398 /* Then downgrade to fslevel 3 */
1399 run_program(RUN_DISPLAY | RUN_PROGRESS,
1400 "%s -p -c 3 /dev/r%s%c", prog, disk, ptn);
1401 }
1402
1403 /*
1404 * fsck and mount the root partition.
1405 */
1406 static int
1407 mount_root(void)
1408 {
1409 int error;
1410 int ptn = (pm->isspecial)? 0 - 'a' : pm->rootpart;
1411
1412 error = fsck_preen(pm->diskdev, ptn, "ffs", false);
1413 if (error != 0)
1414 return error;
1415
1416 md_pre_mount();
1417
1418 /* Mount /dev/<diskdev>a on target's "".
1419 * If we pass "" as mount-on, Prefixing will DTRT.
1420 * for now, use no options.
1421 * XXX consider -o remount in case target root is
1422 * current root, still readonly from single-user?
1423 */
1424 return target_mount("", pm->diskdev, ptn, "");
1425 }
1426
1427 /* Get information on the file systems mounted from the root filesystem.
1428 * Offer to convert them into 4.4BSD inodes if they are not 4.4BSD
1429 * inodes. Fsck them. Mount them.
1430 */
1431
1432 int
1433 mount_disks(void)
1434 {
1435 char *fstab;
1436 int fstabsize;
1437 int error;
1438
1439 static struct lookfor fstabbuf[] = {
1440 {"/dev/", "/dev/%s %s ffs %s", "c", NULL, 0, 0, foundffs},
1441 {"/dev/", "/dev/%s %s ufs %s", "c", NULL, 0, 0, foundffs},
1442 #ifdef USE_SYSVBFS
1443 {"/dev/", "/dev/%s %s sysvbfs %s", "c", NULL, 0, 0,
1444 foundsysvbfs},
1445 #endif
1446 };
1447 static size_t numfstabbuf = sizeof(fstabbuf) / sizeof(struct lookfor);
1448
1449 /* First the root device. */
1450 if (target_already_root())
1451 /* avoid needing to call target_already_root() again */
1452 targetroot_mnt[0] = 0;
1453 else {
1454 error = mount_root();
1455 if (error != 0 && error != EBUSY)
1456 return -1;
1457 }
1458
1459 /* Check the target /etc/fstab exists before trying to parse it. */
1460 if (target_dir_exists_p("/etc") == 0 ||
1461 target_file_exists_p("/etc/fstab") == 0) {
1462 msg_display(MSG_noetcfstab, pm->diskdev);
1463 process_menu(MENU_ok, NULL);
1464 return -1;
1465 }
1466
1467
1468 /* Get fstab entries from the target-root /etc/fstab. */
1469 fstabsize = target_collect_file(T_FILE, &fstab, "/etc/fstab");
1470 if (fstabsize < 0) {
1471 /* error ! */
1472 msg_display(MSG_badetcfstab, pm->diskdev);
1473 process_menu(MENU_ok, NULL);
1474 return -2;
1475 }
1476 error = walk(fstab, (size_t)fstabsize, fstabbuf, numfstabbuf);
1477 free(fstab);
1478
1479 return error;
1480 }
1481
1482 int
1483 set_swap_if_low_ram(const char *disk, partinfo *pp)
1484 {
1485 if (get_ramsize() <= 32)
1486 return set_swap(disk, pp);
1487 return 0;
1488 }
1489
1490 int
1491 set_swap(const char *disk, partinfo *pp)
1492 {
1493 int i;
1494 char *cp;
1495 int rval;
1496
1497 if (pp == NULL)
1498 pp = pm->oldlabel;
1499
1500 for (i = 0; i < MAXPARTITIONS; i++) {
1501 if (pp[i].pi_fstype != FS_SWAP)
1502 continue;
1503 asprintf(&cp, "/dev/%s%c", disk, 'a' + i);
1504 rval = swapctl(SWAP_ON, cp, 0);
1505 free(cp);
1506 if (rval != 0)
1507 return -1;
1508 }
1509
1510 return 0;
1511 }
1512
1513 int
1514 check_swap(const char *disk, int remove_swap)
1515 {
1516 struct swapent *swap;
1517 char *cp;
1518 int nswap;
1519 int l;
1520 int rval = 0;
1521
1522 nswap = swapctl(SWAP_NSWAP, 0, 0);
1523 if (nswap <= 0)
1524 return 0;
1525
1526 swap = malloc(nswap * sizeof *swap);
1527 if (swap == NULL)
1528 return -1;
1529
1530 nswap = swapctl(SWAP_STATS, swap, nswap);
1531 if (nswap < 0)
1532 goto bad_swap;
1533
1534 l = strlen(disk);
1535 while (--nswap >= 0) {
1536 /* Should we check the se_dev or se_path? */
1537 cp = swap[nswap].se_path;
1538 if (memcmp(cp, "/dev/", 5) != 0)
1539 continue;
1540 if (memcmp(cp + 5, disk, l) != 0)
1541 continue;
1542 if (!isalpha(*(unsigned char *)(cp + 5 + l)))
1543 continue;
1544 if (cp[5 + l + 1] != 0)
1545 continue;
1546 /* ok path looks like it is for this device */
1547 if (!remove_swap) {
1548 /* count active swap areas */
1549 rval++;
1550 continue;
1551 }
1552 if (swapctl(SWAP_OFF, cp, 0) == -1)
1553 rval = -1;
1554 }
1555
1556 done:
1557 free(swap);
1558 return rval;
1559
1560 bad_swap:
1561 rval = -1;
1562 goto done;
1563 }
1564
1565 #ifdef HAVE_BOOTXX_xFS
1566 char *
1567 bootxx_name(void)
1568 {
1569 int fstype;
1570 const char *bootxxname;
1571 char *bootxx;
1572
1573 /* check we have boot code for the root partition type */
1574 fstype = pm->bsdlabel[pm->rootpart].pi_fstype;
1575 switch (fstype) {
1576 #if defined(BOOTXX_FFSV1) || defined(BOOTXX_FFSV2)
1577 case FS_BSDFFS:
1578 if (pm->bsdlabel[pm->rootpart].pi_flags & PIF_FFSv2) {
1579 #ifdef BOOTXX_FFSV2
1580 bootxxname = BOOTXX_FFSV2;
1581 #else
1582 bootxxname = NULL;
1583 #endif
1584 } else {
1585 #ifdef BOOTXX_FFSV1
1586 bootxxname = BOOTXX_FFSV1;
1587 #else
1588 bootxxname = NULL;
1589 #endif
1590 }
1591 break;
1592 #endif
1593 #ifdef BOOTXX_LFSV2
1594 case FS_BSDLFS:
1595 bootxxname = BOOTXX_LFSV2;
1596 break;
1597 #endif
1598 default:
1599 bootxxname = NULL;
1600 break;
1601 }
1602
1603 if (bootxxname == NULL)
1604 return NULL;
1605
1606 asprintf(&bootxx, "%s/%s", BOOTXXDIR, bootxxname);
1607 return bootxx;
1608 }
1609 #endif
1610
1611 static int
1612 get_fsid_by_gptuuid(const char *str)
1613 {
1614 int i;
1615 uuid_t uuid;
1616 uint32_t status;
1617
1618 uuid_from_string(str, &uuid, &status);
1619 if (status == uuid_s_ok) {
1620 for (i = 0; gpt_filesystems[i].id > 0; i++)
1621 if (uuid_equal(&uuid, &(gpt_filesystems[i].uuid), NULL))
1622 return gpt_filesystems[i].id;
1623 }
1624 return FS_OTHER;
1625 }
1626
1627 const char *
1628 get_gptfs_by_id(int filesystem)
1629 {
1630 int i;
1631 for (i = 0; gpt_filesystems[i].id > 0; i++)
1632 if (filesystem == gpt_filesystems[i].id)
1633 return gpt_filesystems[i].name;
1634 return NULL;
1635 }
1636
1637 /* from dkctl.c */
1638 static int
1639 get_dkwedges_sort(const void *a, const void *b)
1640 {
1641 const struct dkwedge_info *dkwa = a, *dkwb = b;
1642 const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset;
1643 return (oa < ob) ? -1 : (oa > ob) ? 1 : 0;
1644 }
1645
1646 int
1647 get_dkwedges(struct dkwedge_info **dkw, const char *diskdev)
1648 {
1649 int fd;
1650 char buf[STRSIZE];
1651 size_t bufsize;
1652 struct dkwedge_list dkwl;
1653
1654 *dkw = NULL;
1655 dkwl.dkwl_buf = *dkw;
1656 dkwl.dkwl_bufsize = 0;
1657 fd = opendisk(diskdev, O_RDONLY, buf, STRSIZE, 0);
1658 if (fd < 0)
1659 return -1;
1660
1661 for (;;) {
1662 if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1)
1663 return -2;
1664 if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied)
1665 break;
1666 bufsize = dkwl.dkwl_nwedges * sizeof(**dkw);
1667 if (dkwl.dkwl_bufsize < bufsize) {
1668 *dkw = realloc(dkwl.dkwl_buf, bufsize);
1669 if (*dkw == NULL)
1670 return -3;
1671 dkwl.dkwl_buf = *dkw;
1672 dkwl.dkwl_bufsize = bufsize;
1673 }
1674 }
1675
1676 if (dkwl.dkwl_nwedges > 0 && *dkw != NULL)
1677 qsort(*dkw, dkwl.dkwl_nwedges, sizeof(**dkw), get_dkwedges_sort);
1678
1679 close(fd);
1680 return dkwl.dkwl_nwedges;
1681 }
1682
1683 /* XXX: rewrite */
1684 static int
1685 incoregpt(pm_devs_t *pm_cur, partinfo *lp)
1686 {
1687 int i, num;
1688 unsigned int p_num;
1689 uint64_t p_start, p_size;
1690 char *textbuf, *t, *tt, p_type[STRSIZE];
1691 struct dkwedge_info *dkw;
1692
1693 num = get_dkwedges(&dkw, pm_cur->diskdev);
1694 if (dkw != NULL) {
1695 for (i = 0; i < num && i < MAX_WEDGES; i++)
1696 run_program(RUN_SILENT, "dkctl %s delwedge %s",
1697 pm_cur->diskdev, dkw[i].dkw_devname);
1698 free (dkw);
1699 }
1700
1701 if (collect(T_OUTPUT, &textbuf, "gpt show -u %s 2>/dev/null", pm_cur->diskdev) < 1)
1702 return -1;
1703
1704 (void)strtok(textbuf, "\n"); /* ignore first line */
1705 while ((t = strtok(NULL, "\n")) != NULL) {
1706 i = 0; p_start = 0; p_size = 0; p_num = 0; strcpy(p_type, ""); /* init */
1707 while ((tt = strsep(&t, " \t")) != NULL) {
1708 if (strlen(tt) == 0)
1709 continue;
1710 if (i == 0)
1711 p_start = strtouq(tt, NULL, 10);
1712 if (i == 1)
1713 p_size = strtouq(tt, NULL, 10);
1714 if (i == 2)
1715 p_num = strtouq(tt, NULL, 10);
1716 if (i > 2 || (i == 2 && p_num == 0))
1717 if (
1718 strcmp(tt, "GPT") &&
1719 strcmp(tt, "part") &&
1720 strcmp(tt, "-")
1721 )
1722 strlcat(p_type, tt, STRSIZE);
1723 i++;
1724 }
1725 if (p_start == 0 || p_size == 0)
1726 continue;
1727 else if (! strcmp(p_type, "Pritable"))
1728 pm_cur->ptstart = p_start + p_size;
1729 else if (! strcmp(p_type, "Sectable"))
1730 pm_cur->ptsize = p_start - pm_cur->ptstart - 1;
1731 else if (p_num == 0 && strlen(p_type) > 0)
1732 /* Utilitary entry (PMBR, etc) */
1733 continue;
1734 else if (p_num == 0) {
1735 /* Free space */
1736 continue;
1737 } else {
1738 /* Usual partition */
1739 lp[p_num].pi_size = p_size;
1740 lp[p_num].pi_offset = p_start;
1741 lp[p_num].pi_fstype = get_fsid_by_gptuuid(p_type);
1742 }
1743 }
1744 free(textbuf);
1745
1746 return 0;
1747 }
1748
1749 static bool
1750 is_gpt(const char *dev)
1751 {
1752
1753 check_available_binaries();
1754
1755 if (!have_gpt)
1756 return false;
1757
1758 return run_program(RUN_SILENT | RUN_ERROR_OK,
1759 "gpt -qr header %s", dev) == 0;
1760 }
1761