source: MondoRescue/branches/3.3/mondo/src/mondorestore/mondo-prep.c@ 3736

Last change on this file since 3736 was 3736, checked in by Bruno Cornec, 4 years ago

create and mount btrfs subvolumes if any

  • Property svn:keywords set to Id
File size: 71.9 KB
Line 
1/***************************************************************************
2$Id: mondo-prep.c 3736 2019-11-18 00:47:33Z bruno $
3* Functions for prepping hard drives: partitioning, formatting, etc.
4*/
5
6
7#include "my-stuff.h"
8#include "mr_mem.h"
9#include "../common/mondostructures.h"
10#include "mondoprep.h"
11#include "../common/libmondo.h"
12#include "../common/libmondo-tools-EXT.h"
13#include "mondo-rstr-tools-EXT.h"
14#include "libmondo-mountlist-EXT.h"
15#include <sys/ioctl.h>
16#include <linux/hdreg.h>
17#include <math.h>
18
19
20#define FDISK_LOG "/var/log/mondofdisk.log"
21
22#ifdef __FreeBSD__
23#define DKTYPENAMES
24#define FSTYPENAMES
25#include <sys/disklabel.h>
26#include <sys/disk.h>
27#include <err.h>
28#include <libgen.h>
29#define OSSWAP(x,y) y
30#else
31#define OSSWAP(x,y) x
32#endif
33
34#define ARCHIVES_PATH MNT_CDROM"/archives"
35#define MONDO_WAS_HERE "MONDOWOZEREMONDOWOZEREMONDOWOZEREhahahaMOJOJOJO"
36
37//static char cvsid[] = "$Id: mondo-prep.c 3736 2019-11-18 00:47:33Z bruno $";
38
39extern char *g_mountlist_fname;
40extern long g_current_progress, g_maximum_progress;
41
42extern bool g_text_mode;
43
44extern void pause_for_N_seconds(int, char *);
45extern char *MONDO_LOGFILE;
46
47FILE *g_fprep = NULL;
48extern char *g_mondo_cfg_file; // where mondorestore.cfg (the config file) is stored
49
50int g_partition_table_locked_up = 0;
51
52
53
54void wipe_MBRs_and_reboot_if_necessary(struct mountlist_itself *mountlist) {
55
56char *command = NULL;
57int lino;
58int i;
59FILE *fout;
60char *buf;
61const int blocksize = 512;
62struct list_of_disks *drivelist = NULL;
63
64// If LVMs are present and a zero-and-reboot wasn't recently undertaken
65// then zero & insist on reboot.
66buf = malloc(blocksize);
67if (does_file_exist("/tmp/i-want-my-lvm")) { // FIXME - cheating :)
68 drivelist = malloc(sizeof(struct list_of_disks));
69 make_list_of_drives_in_mountlist(mountlist, drivelist);
70 for (lino = 0; lino < drivelist->entries; lino++) {
71 mr_asprintf(command, "dd if=%s bs=512 count=1 2> /dev/null | grep \"%s\"", drivelist->el[lino].device, MONDO_WAS_HERE);
72 if (!run_program_and_log_output(command, 1)) {
73 log_msg(1, "Found MONDO_WAS_HERE marker on drive#%d (%s)", lino, drivelist->el[lino].device);
74 break;
75 }
76 mr_free(command);
77 }
78
79 if (lino == drivelist->entries) {
80 // zero & reboot
81 log_to_screen("I am sorry for the inconvenience but I must ask you to reboot.");
82 log_to_screen("I need to reset the Master Boot Record; in order to be");
83 log_to_screen("sure the kernel notices, I must reboot after doing it.");
84 log_to_screen("Please hit 'Enter' to reboot.");
85 for (lino = 0; lino < drivelist->entries; lino++) {
86 for (i = 0; i < blocksize; i++) {
87 buf[i] = 0;
88 }
89 sprintf(buf, "%s\n", MONDO_WAS_HERE);
90 fout = fopen(drivelist->el[lino].device, "w+");
91 if (!fout) {
92 log_msg(1, "Unable to open+wipe %s", drivelist->el[lino].device);
93 } else {
94 if (1 != fwrite(buf, blocksize, 1, fout)) {
95 log_msg(1, "Failed to wipe %s", drivelist->el[lino].device);
96 } else {
97 log_msg(1, "Successfully wiped %s", drivelist->el[lino].device);
98 }
99 fclose(fout);
100 }
101 }
102 paranoid_system("sync");
103 paranoid_system("sync");
104 paranoid_system("sync");
105 popup_and_OK("I must now reboot. Please leave the boot media in the drive and repeat your actions - e.g. type 'nuke' - and it should work fine.");
106 paranoid_system("reboot");
107 }
108}
109// Still here? Cool!
110log_msg(1, "Cool. I didn't have to wipe anything.");
111}
112
113
114int fput_string_one_char_at_a_time(FILE * fout, char *str) {
115int i, j;
116FILE *fq;
117
118if (ferror(fout)) {
119 return (-1);
120}
121log_msg(5, "Writing string '%s', one char at a time", str);
122j = strlen(str);
123for (i = 0; i < j; i++) {
124 log_msg(6, "Writing %d ('%c')", str[i], str[i]);
125 if ((fq = fopen(FDISK_LOG, "a+"))) {
126 fputc(str[i], fq);
127 fclose(fq);
128 }
129 fputc(str[i], fout);
130 fflush(fout);
131 usleep(1000L * 100L);
132 if (str[i] < 32) {
133 usleep(1000L * 10L);
134 }
135}
136log_msg(5, "Returning");
137
138return (i);
139}
140
141
142/**
143 * @addtogroup prepGroup
144 * @{
145 */
146/**
147 * Execute the commands in /tmp/i-want-my-lvm.
148 * These should probably be commands to set up LVM.
149 * @return The number of errors encountered (0 for success).
150 */
151
152int do_my_funky_lvm_stuff(bool just_erase_existing_volumes, bool vacuum_pack) {
153/** buffers **********************************************/
154char *tmp;
155char *tmp1 = NULL;
156char *tmp2 = NULL;
157char *incoming;
158char *command;
159char *cmd = NULL;
160char *lvscan_sz = NULL;
161char *lvremove_sz = NULL;
162char *pvscan_sz = NULL;
163char *vgscan_sz = NULL;
164char *vgchange_sz = NULL;
165char *vgremove_sz = NULL;
166
167/** char **************************************************/
168char *p;
169char *q;
170char *r;
171
172/** int ***************************************************/
173int retval = 0;
174int res = 0;
175int i;
176int lvmversion = 1;
177long extents;
178fpos_t orig_pos;
179
180/** pointers **********************************************/
181FILE *fin;
182
183/** end *****************************************************/
184
185#ifdef __FreeBSD__
186return (0);
187#endif
188
189tmp = call_program_and_get_last_line_of_output("cat "CMDLINE);
190if (strstr(tmp, "nolvm")) {
191 mr_free(tmp);
192 return(0);
193}
194mr_free(tmp);
195
196if (!(fin = fopen("/tmp/i-want-my-lvm", "r"))) {
197 log_OS_error("/tmp/i-want-my-lvm");
198 return (1);
199}
200
201log_it("STARTING");
202log_msg(1, "OK, opened i-want-my-lvm. Shutting down LVM volumes...");
203if (find_home_of_exe("lvm")) { // found it :) cool
204 mr_asprintf(lvscan_sz, "lvm lvscan");
205 mr_asprintf(lvremove_sz, "lvm lvremove");
206 mr_asprintf(vgscan_sz, "lvm vgscan");
207 mr_asprintf(pvscan_sz, "lvm pvscan");
208 mr_asprintf(vgchange_sz, "lvm vgchange");
209 mr_asprintf(vgremove_sz, "lvm vgremove -f");
210} else {
211 mr_asprintf(lvscan_sz, "lvscan");
212 mr_asprintf(lvremove_sz, "lvremove");
213 mr_asprintf(vgscan_sz, "vgscan");
214 mr_asprintf(pvscan_sz, "pvscan");
215 mr_asprintf(vgchange_sz, "vgchange");
216 mr_asprintf(vgremove_sz, "vgremove");
217}
218mr_asprintf(cmd, "for i in `%s | cut -d\"'\" -f2 | sort -r` ; do echo \"Shutting down lv $i\" >> %s ; %s -f $i; done", lvscan_sz, MONDO_LOGFILE, lvremove_sz);
219mr_free(lvscan_sz);
220mr_free(lvremove_sz);
221
222run_program_and_log_output(cmd, 5);
223mr_free(cmd);
224
225sleep(1);
226mr_asprintf(cmd, "for i in `%s | grep -i lvm | cut -d'\"' -f2` ; do %s -a n $i ; %s $i; echo \"Shutting down vg $i\" >> %s ; done", vgscan_sz, vgchange_sz, vgremove_sz, MONDO_LOGFILE);
227mr_free(vgchange_sz);
228mr_free(vgremove_sz);
229
230run_program_and_log_output(cmd, 5);
231mr_free(cmd);
232
233if (just_erase_existing_volumes) {
234 paranoid_fclose(fin);
235 log_msg(1, "Closed i-want-my-lvm. Finished erasing LVMs.");
236 retval = 0;
237} else {
238
239 malloc_string(tmp);
240 malloc_string(incoming);
241 command = malloc(512);
242
243 log_msg(1, "OK, rewound i-want-my-lvm. Doing funky stuff...");
244 rewind(fin);
245 for (r = fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin) && (r != NULL); r = fgets(incoming, MAX_STR_LEN - 1, fin)) {
246 fgetpos(fin, &orig_pos);
247 if (incoming[0] != '#') {
248 continue;
249 }
250 if (res && strstr(command, "create") && vacuum_pack) {
251 sleep(2);
252 paranoid_system("sync");
253 paranoid_system("sync");
254 paranoid_system("sync");
255 }
256 if ((p = strstr(incoming, "vgcreate"))) {
257 // include next line(s) if they end in /dev (cos we've got a broken i-want-my-lvm)
258 for (q = fgets(tmp, MAX_STR_LEN - 1, fin); !feof(fin) && (q != NULL); q = fgets(tmp, MAX_STR_LEN - 1, fin)) {
259 if (tmp[0] == '#') {
260 fsetpos(fin, &orig_pos);
261 break;
262 } else {
263 fgetpos(fin, &orig_pos);
264 strcat(incoming, tmp);
265 }
266 }
267 for (q = incoming; *q != '\0'; q++) {
268 if (*q < 32) {
269 *q = ' ';
270 }
271 }
272 strcpy(tmp, p + strlen("vgcreate") + 1);
273 for (q = tmp; *q > 32; q++);
274 *q = '\0';
275 log_msg(1, "Deleting old entries at /dev/%s", tmp);
276 mr_asprintf(cmd, "rm -Rf /dev/%s", tmp);
277 run_program_and_log_output(cmd, 1);
278 mr_free(cmd);
279
280 run_program_and_log_output(vgscan_sz, 1);
281 run_program_and_log_output(pvscan_sz, 1);
282 log_msg(3, "After working around potentially broken i-want-my-lvm, incoming[] is now '%s'", incoming);
283 }
284 for (p = incoming + 1; *p == ' '; p++);
285 strcpy(command, p);
286 for (p = command; *p != '\0'; p++);
287 for (; *(p - 1) < 32; p--);
288 *p = '\0';
289 res = run_program_and_log_output(command, 5);
290 if (res > 0 && (p = strstr(command, "lvm "))) {
291 log_msg(0, "%s --> %d failed so removing lvm just in case", command, res);
292 *p = *(p + 1) = *(p + 2) = ' ';
293 res = run_program_and_log_output(command, 5);
294 }
295 log_msg(0, "%s --> %d", command, res);
296 if (res > 0) {
297 res = 1;
298 }
299 if (res && strstr(command, "lvcreate") && vacuum_pack) {
300 res = 0;
301 if (strstr(command, "lvm lvcreate"))
302 lvmversion = 2;
303 log_it("%s... so I'll get creative.", tmp);
304 if (lvmversion == 2) {
305 mr_asprintf(tmp1, "tail -n5 %s | grep Insufficient | tail -n1", MONDO_LOGFILE);
306 } else {
307 mr_asprintf(tmp1, "tail -n5 %s | grep lvcreate | tail -n1", MONDO_LOGFILE);
308 }
309 tmp2 = call_program_and_get_last_line_of_output(tmp1);
310 strcpy(tmp, tmp2);
311 mr_free(tmp2);
312 mr_free(tmp1);
313
314 for (p = tmp; *p != '\0' && !isdigit(*p); p++);
315 extents = atol(p);
316 log_msg(5, "p='%s' --> extents=%ld", p, extents);
317 p = strstr(command, "-L");
318 if (!p) {
319 log_msg(0, "Fiddlesticks. '%s' returned %d", command, res);
320 } else {
321 if (lvmversion == 2) {
322 *p++ = '-';
323 *p++ = 'l';
324 *p++ = ' ';
325 for (q = p; *q != ' '; q++) {
326 *q = ' ';
327 }
328 sprintf(p, "%ld", extents);
329 i = strlen(p);
330 *(p + i) = ' ';
331 } else {
332 p++;
333 p++;
334 p++;
335 for (q = p; *q != ' '; q++) {
336 *(q - 1) = ' ';
337 }
338 sprintf(p, "%ld%c", extents, 'm');
339 i = strlen(p);
340 *(p + i) = ' ';
341 }
342 log_msg(5, "Retrying with '%s'", command);
343 res = run_program_and_log_output(command, 5);
344 if (res > 0) {
345 res = 1;
346 }
347 if (g_fprep) {
348 fprintf(g_fprep, "%s\n", command);
349 }
350 log_msg(0, "%s --> %d", command, res);
351 if (!res) {
352 log_msg(5, "YAY! This time, it succeeded.");
353 }
354 }
355 }
356 if (strstr(command, "vgcreate")) {
357 log_msg(0, "In case you're interested...");
358 run_program_and_log_output(vgscan_sz, 1);
359 run_program_and_log_output(pvscan_sz, 1);
360 }
361 if (res != 0 && !strstr(command, "insmod")) {
362 retval++;
363 }
364 sprintf(tmp, "echo \"%s\" >> /tmp/out.sh", command);
365 paranoid_system(tmp);
366 sleep(1);
367 }
368
369 paranoid_fclose(fin);
370 paranoid_free(tmp);
371 paranoid_free(incoming);
372 paranoid_free(command);
373 log_msg(1, "Closed i-want-my-lvm. Finished doing funky stuff.");
374}
375mr_free(pvscan_sz);
376mr_free(vgscan_sz);
377
378paranoid_system("sync");
379paranoid_system("sync");
380paranoid_system("sync");
381sleep(1);
382log_it("ENDING");
383if (retval > 2) {
384 log_msg(1, "%d errors. I'm reporting this.", retval);
385 return (retval);
386} else {
387 log_msg(1, "Not many errors. Returning 0.");
388 return (0);
389}
390}
391
392
393/**
394 * Add RAID partitions while copying @p old_mountlist to @p new_mountlist.
395 * We go through @p old_mountlist and check if any RAID device (/dev/md? on Linux)
396 * is in it; if it is, then we put the disks contained within that RAID device
397 * into the mountlist as well.
398 * @param old_mountlist The mountlist to read.
399 * @param new_mountlist The mountlist to write, with the RAID partitions added.
400 * @return 0 for success, nonzero for failure.
401 */
402int extrapolate_mountlist_to_include_raid_partitions(struct mountlist_itself *new_mountlist, struct mountlist_itself *old_mountlist) {
403
404/** pointers *********************************************************/
405FILE *fin;
406
407/** int **************************************************************/
408int lino;
409int j;
410
411/** buffers **********************************************************/
412char *incoming;
413
414/** pointers *********************************************************/
415char *p;
416char *q;
417
418/** init *************************************************************/
419new_mountlist->entries = 0;
420
421/** end **************************************************************/
422
423malloc_string(incoming);
424assert(new_mountlist != NULL);
425assert(old_mountlist != NULL);
426
427#ifdef __FreeBSD__
428log_to_screen("I don't know how to extrapolate the mountlist on FreeBSD. Sorry.");
429return (1);
430#endif
431
432for (lino = 0; lino < old_mountlist->entries; lino++) {
433 if (strstr(old_mountlist->el[lino].device, RAID_DEVICE_STUB)) // raid
434 {
435 if (!does_file_exist("/etc/raidtab")) {
436 log_to_screen
437 ("Cannot find /etc/raidtab - cannot extrapolate the fdisk entries");
438 finish(1);
439 }
440 if (!(fin = fopen("/etc/raidtab", "r"))) {
441 log_OS_error("Cannot open /etc/raidtab");
442 finish(1);
443 }
444 for (q = fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin) && (q != NULL)
445 && !strstr(incoming, old_mountlist->el[lino].device);
446 q = fgets(incoming, MAX_STR_LEN - 1, fin));
447 if (!feof(fin)) {
448 log_it("Investigating %s", old_mountlist->el[lino].device);
449 for (q = fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin) && (q != NULL) && !strstr(incoming, "raiddev"); q = fgets(incoming, MAX_STR_LEN - 1, fin)) {
450 if (strstr(incoming, OSSWAP("device", "drive")) && !strchr(incoming, '#')) {
451 for (p = incoming + strlen(incoming);
452 *(p - 1) <= 32; p--);
453 *p = '\0';
454 for (p--; p > incoming && *(p - 1) > 32; p--);
455 log_it("Extrapolating %s", p);
456 for (j = 0; j < new_mountlist->entries && strcmp(new_mountlist->el[j].device, p); j++);
457 if (j >= new_mountlist->entries) {
458 strcpy(new_mountlist-> el[new_mountlist->entries].device, p);
459 strcpy(new_mountlist-> el[new_mountlist->entries].mountpoint, "raid");
460 strcpy(new_mountlist-> el[new_mountlist->entries].format, "raid");
461 new_mountlist->el[new_mountlist->entries]. size = old_mountlist->el[lino].size;
462 new_mountlist->entries++;
463 } else {
464 log_it("Not adding %s to mountlist: it's already there", p);
465 }
466 }
467 }
468 }
469 paranoid_fclose(fin);
470 } else {
471 strcpy(new_mountlist->el[new_mountlist->entries].device, old_mountlist->el[lino].device);
472 strcpy(new_mountlist->el[new_mountlist->entries].mountpoint, old_mountlist->el[lino].mountpoint);
473 strcpy(new_mountlist->el[new_mountlist->entries].format, old_mountlist->el[lino].format);
474 new_mountlist->el[new_mountlist->entries].size = old_mountlist->el[lino].size;
475 new_mountlist->entries++;
476 }
477}
478paranoid_free(incoming);
479
480return (0);
481}
482
483
484/**
485 * Create @p RAID device using information from @p structure.
486 * This will create the specified RAID devive using information provided in
487 * raidlist by means of the mdadm tool.
488 * @param raidlist The structure containing all RAID information
489 * @param device The RAID device to create.
490 * @return 0 for success, nonzero for failure.
491 */
492int create_raid_device_via_mdadm(struct raidlist_itself *raidlist, char *device, bool test) {
493
494/** int **************************************************************/
495int i = 0;
496int j = 0;
497int v = 0;
498int res = 0;
499
500/** buffers ***********************************************************/
501char *devices = NULL;
502char *level = NULL;
503char *program = NULL;
504
505// leave straight away if raidlist is initial or has no entries
506if (!raidlist || raidlist->entries == 0) {
507 log_msg(1, "No RAID arrays found.");
508 return 1;
509} else {
510 log_msg(1, "%d RAID arrays found.", raidlist->entries);
511}
512// find raidlist entry for requested device
513for (i = 0; i < raidlist->entries; i++) {
514 if (!strcmp(raidlist->el[i].raid_device, device)) break;
515}
516// check whether RAID device was found in raidlist
517if (i == raidlist->entries) {
518 log_msg(1, "RAID device %s not found in list.", device);
519 return 1;
520} else {
521 log_msg(1, "RAID device %s found in list (%d).", device, i);
522}
523
524// create device list from normal disks followed by spare ones
525if (raidlist->el[i].data_disks.el[0].device != NULL) {
526 mr_asprintf(devices, "%s", raidlist->el[i].data_disks.el[0].device);
527 log_msg(4, "Adding device %s to list", raidlist->el[i].data_disks.el[0].device);
528} else {
529 log_msg(1, "Strange, there are entries but no device");
530}
531for (j = 1; j < raidlist->el[i].data_disks.entries; j++) {
532 mr_strcat(devices, " %s", raidlist->el[i].data_disks.el[j].device);
533 log_msg(4, "Adding device %s to list", raidlist->el[i].data_disks.el[j].device);
534}
535for (j = 0; j < raidlist->el[i].spare_disks.entries; j++) {
536 mr_strcat(devices, " %s", raidlist->el[i].spare_disks.el[j].device);
537 log_msg(4, "Adding spare device %s to list", raidlist->el[i].spare_disks.el[j].device);
538}
539log_msg(4, "RAID devices: %s", devices);
540// translate RAID level
541if (raidlist->el[i].raid_level == -2) {
542 mr_asprintf(level, "multipath");
543} else if (raidlist->el[i].raid_level == -1) {
544 mr_asprintf(level, "linear");
545} else {
546 mr_asprintf(level, "raid%d", raidlist->el[i].raid_level);
547}
548// create RAID device:
549// - RAID device, number of devices and devices mandatory
550// - parity algorithm, chunk size and spare devices optional
551// - faulty devices ignored
552// - persistent superblock always used as this is recommended
553
554mr_asprintf(program, "mdadm --create --force --run --auto=yes %s --level=%s --raid-devices=%d", raidlist->el[i].raid_device, level, raidlist->el[i].data_disks.entries);
555mr_free(level);
556log_msg(4, "cmd built: %s", program);
557// Restoring the UUID and Version stored at backup time of present
558for (v = 0; v < raidlist->el[i].additional_vars.entries ; v++ ) {
559 log_msg(4,"Working on additional param #%d (Label: %s)",v,raidlist->el[i].additional_vars.el[v].label);
560 if ((raidlist->el[i].additional_vars.el[v].label != NULL) && (strcmp(raidlist->el[i].additional_vars.el[v].label,"UUID") == 0)) {
561 // We have a UUID to handle
562 if (raidlist->el[i].additional_vars.el[v].value != NULL) {
563 // force its restoration in order to avoid modifying all conf files using it
564 log_it("Managing previous UUID %s", raidlist->el[i].additional_vars.el[v].value);
565 mr_strcat(program, " --uuid %s",raidlist->el[i].additional_vars.el[v].value);
566 continue;
567 } else {
568 log_msg(1,"Unable to manage previous NULL UUID");
569 }
570 }
571 if ((raidlist->el[i].additional_vars.el[v].label != NULL) && (strcmp(raidlist->el[i].additional_vars.el[v].label,"Version") == 0)) {
572 // We have a Version to handle
573 if (raidlist->el[i].additional_vars.el[v].value != NULL) {
574 // force its restoration in order to support all complex boot loader + md format cases
575 // Also see bug #473
576 log_it("Managing previous Version %s", raidlist->el[i].additional_vars.el[v].value);
577 mr_strcat(program, " -e %s",raidlist->el[i].additional_vars.el[v].value);
578 continue;
579 } else {
580 log_msg(1,"Unable to manage previous NULL Version");
581 }
582 }
583}
584log_msg(4, "cmd built: %s", program);
585if (raidlist->el[i].parity != -1) {
586 switch(raidlist->el[i].parity) {
587 case 0:
588 mr_strcat(program, " --parity=%s", "la");
589 break;
590 case 1:
591 mr_strcat(program, " --parity=%s", "ra");
592 break;
593 case 2:
594 mr_strcat(program, " --parity=%s", "ls");
595 break;
596 case 3:
597 mr_strcat(program, " --parity=%s", "rs");
598 break;
599 default:
600 fatal_error("Unknown RAID parity algorithm.");
601 break;
602 }
603}
604log_msg(4, "cmd built: %s", program);
605if (raidlist->el[i].chunk_size != -1) {
606 mr_strcat(program, " --chunk=%d", raidlist->el[i].chunk_size);
607}
608if (raidlist->el[i].spare_disks.entries > 0) {
609 mr_strcat(program, " --spare-devices=%d", raidlist->el[i].spare_disks.entries);
610}
611log_msg(4, "cmd built: %s", program);
612mr_strcat(program, " %s", devices);
613log_msg(2, "RAID device re-created with the following command:\n%s", program);
614if (test == TRUE) {
615 res = run_program_and_log_output(program, 1);
616} else {
617 // test mode, always returns TRUE without executing the mdadm command
618 res = TRUE;
619}
620// free memory
621mr_free(devices);
622mr_free(program);
623// return to calling instance
624return res;
625}
626
627
628/**
629 * Format @p device as a @p format filesystem.
630 * This will use the format command returned by which_format_command_do_i_need().
631 * If @p device is an LVM PV, it will not be formatted, and LVM will be started
632 * (if not already done). If it's an imagedev, software RAID component, or
633 * (under BSD) swap partition, no format will be done.
634 * @param device The device to format.
635 * @param format The filesystem type to format it as.
636 * @return 0 for success, nonzero for failure.
637 */
638int format_device(char *device, char *format, struct raidlist_itself *raidlist) {
639
640/** int **************************************************************/
641#ifdef __FreeBSD__
642static bool vinum_started_yet = FALSE;
643#endif
644
645/** buffers ***********************************************************/
646char *program = NULL;
647char *tmp = NULL;
648int res = 0;
649int retval = 0;
650
651/** end ****************************************************************/
652
653assert_string_is_neither_NULL_nor_zerolength(device);
654assert(format != NULL);
655
656if (strstr(format, "raid")) { // do not form RAID disks; do it to /dev/md* instead
657 log_it("Not formatting %s (it is a RAID disk)", device);
658 return (0);
659}
660#ifdef __FreeBSD__
661if (strcmp(format, "swap") == 0) {
662 log_it("Not formatting %s - it's swap", device);
663 return (0);
664}
665#endif
666if (strlen(format) <= 2) {
667 log_it("%s has a really small format type ('%s') - this is probably a hexadecimal string, which would suggest the partition is an image --- I shouldn't format it", device, format);
668 return (0);
669}
670if (is_this_device_mounted(device)) {
671 log_to_screen("%s is mounted - cannot format it ", device);
672 return (1);
673}
674
675if (strstr(device, RAID_DEVICE_STUB)) {
676 newtSuspend();
677#ifdef __FreeBSD__
678if (!vinum_started_yet) {
679 if (!does_file_exist("/tmp/raidconf.txt")) {
680 log_to_screen("/tmp/raidconf.txt does not exist. I therefore cannot start Vinum.");
681 } else {
682 int res;
683 res = run_program_and_log_output("vinum create /tmp/raidconf.txt", TRUE);
684 if (res) {
685 log_to_screen("`vinum create /tmp/raidconf.txt' returned errors. Please fix them and re-run mondorestore.");
686 finish(1);
687 }
688 vinum_started_yet = TRUE;
689 }
690}
691
692if (vinum_started_yet) {
693 FILE *fin;
694 char line[MAX_STR_LEN];
695
696 log_to_screen(tmp, "Initializing Vinum device %s (this may take a *long* time)", device);
697
698 /* format raid partition */
699 mr_asprintf(program, "for plex in `vinum lv -r %s | grep '^P' | tr '\t' ' ' | tr -s ' ' | cut -d' ' -f2`; do echo $plex; done > /tmp/plexes", basename(device));
700 paranoid_system(program);
701 if (g_fprep) {
702 fprintf(g_fprep, "%s\n", program);
703 }
704 mr_free(program);
705 fin = fopen("/tmp/plexes", "r");
706 while (fgets(line, MAX_STR_LEN - 1, fin)) {
707 if (strchr(line, '\n'))
708 *(strchr(line, '\n')) = '\0'; // get rid of the \n on the end
709
710 mr_asprintf(tmp, "Initializing plex: %s", line);
711 open_evalcall_form(tmp);
712 mr_free(tmp);
713
714 mr_asprintf(tmp, "vinum init %s", line);
715 paranoid_system(tmp);
716 mr_free(tmp);
717
718 while (1) {
719 mr_asprintf(tmp, "vinum lp -r %s | grep '^S' | head -1 | tr -s ' ' | cut -d: -f2 | cut -f1 | sed 's/^ //' | sed 's/I //' | sed 's/%%//'", line);
720 FILE *pin = popen(tmp, "r");
721 mr_free(tmp);
722
723 char status[MAX_STR_LEN / 4];
724 if (fgets(status, MAX_STR_LEN / 4 - 1, pin)) {
725 // FIXME
726 }
727 pclose(pin);
728
729 if (!strcmp(status, "up")) {
730 break; /* it's done */
731 }
732 update_evalcall_form(atoi(status));
733 usleep(250000);
734 }
735 close_evalcall_form();
736 }
737 fclose(fin);
738 unlink("/tmp/plexes");
739}
740#else
741log_to_screen("Initializing RAID device %s", device);
742
743// Shouldn't be necessary.
744log_to_screen("Stopping %s", device);
745stop_raid_device(device);
746paranoid_system("sync");
747sleep(1);
748
749log_msg(1, "Making %s", device);
750// use mkraid if it exists, otherwise use mdadm
751if (run_program_and_log_output("which mkraid", FALSE)) {
752 res = create_raid_device_via_mdadm(raidlist, device, TRUE);
753 log_msg(1, "Creating RAID device %s via mdadm returned %d", device, res);
754} else {
755 mr_asprintf(program, "mkraid --really-force %s", device);
756 res = run_program_and_log_output(program, 1);
757 log_msg(1, "%s returned %d", program, res);
758 paranoid_system("sync");
759 sleep(3);
760 start_raid_device(device);
761 if (g_fprep) {
762 fprintf(g_fprep, "%s\n", program);
763 }
764 mr_free(program);
765}
766paranoid_system("sync");
767sleep(2);
768#endif
769paranoid_system("sync");
770sleep(1);
771newtResume();
772}
773
774if (!strcmp(format, "lvm")) {
775 log_msg(1, "Don't format %s - it's part of an lvm volume", device);
776 return (0);
777}
778program = which_format_command_do_i_need(format);
779mr_asprintf(tmp, "%s %s", program, device);
780mr_free(program);
781
782mr_asprintf(program, "sh -c 'echo -en \"y\\ny\\ny\\n\" | %s'", tmp);
783mr_free(tmp);
784
785mr_asprintf(tmp, "Formatting %s as %s", device, format);
786update_progress_form(tmp);
787
788res = run_program_and_log_output(program, FALSE);
789mr_free(program);
790if (res) {
791 mr_strcat(tmp, "...failed");
792} else {
793 mr_strcat(tmp, "...OK");
794}
795log_to_screen(tmp);
796mr_free(tmp);
797
798/* BR#852: we need to create BTRFS subvols now */
799if ((res) && (strcmp(format, "btrfs") == 0)) {
800 /* Creating potential btrfs subvolumes */
801 if (does_file_exist("/tmp/create-btrfs-subvol")) {
802 run_program_and_log_output("/tmp/create-btrfs-subvol",3);
803 }
804 /* Mounting potential btrfs subvolumes */
805 if (does_file_exist("/tmp/mount-btrfs-subvol")) {
806 run_program_and_log_output("/tmp/mount-btrfs-subvol",3);
807 }
808}
809
810/* BR#836: we need to do it a second time if -m wasn't working in that version for XFS */
811if ((res) && (strcmp(format, "xfs") == 0)) {
812 mr_asprintf(program, "sh -c 'echo -en \"y\\ny\\ny\\n\" | mkfs.xfs -f -q %s'", device);
813
814 mr_asprintf(tmp, "Formatting %s as %s", device, format);
815 update_progress_form(tmp);
816
817 res = run_program_and_log_output(program, FALSE);
818 mr_free(program);
819 if (res) {
820 mr_strcat(tmp, "...failed");
821 } else {
822 mr_strcat(tmp, "...OK");
823 }
824 log_to_screen(tmp);
825 mr_free(tmp);
826}
827
828retval += res;
829paranoid_system("sync");
830sleep(1);
831return (retval);
832}
833
834
835/**
836 * Format all drives (except those excluded by format_device()) in @p mountlist.
837 * @param mountlist The mountlist containing partitions to be formatted.
838 * @param interactively If TRUE, then prompt the user before each partition.
839 * @return The number of errors encountered (0 for success).
840 */
841int format_everything(struct mountlist_itself *mountlist, bool interactively, struct raidlist_itself *raidlist) {
842
843/** int **************************************************************/
844int retval = 0;
845int lino;
846int res;
847
848/** long *************************************************************/
849long progress_step;
850
851/** bools ************************************************************/
852bool do_it;
853
854/** buffers **********************************************************/
855char *tmp = NULL;
856
857/** pointers *********************************************************/
858struct mountlist_line *me; // mountlist entry
859/** end **************************************************************/
860
861assert(mountlist != NULL);
862log_it("format_everything (mountlist, interactively = %s", (interactively) ? "true" : "false");
863mvaddstr_and_log_it(g_currentY, 0, "Formatting partitions ");
864open_progress_form("Formatting partitions", "I am now formatting your hard disk partitions.", "This may take up to five minutes.", "", mountlist->entries + 1);
865
866progress_step = (mountlist->entries > 0) ? g_maximum_progress / mountlist->entries : 1;
867// start soft-raids now (because LVM might depend on them)
868// ...and for simplicity's sake, let's format them at the same time :)
869log_msg(1, "Stopping all RAID devices");
870stop_all_raid_devices(mountlist);
871paranoid_system("sync");
872paranoid_system("sync");
873paranoid_system("sync");
874sleep(2);
875log_msg(1, "Prepare soft-RAIDs"); // prep and format too
876for (lino = 0; lino < mountlist->entries; lino++) {
877 me = &mountlist->el[lino]; // the current mountlist entry
878 log_msg(2, "Examining %s", me->device);
879 if (!strncmp(me->device, "/dev/md", 7)) {
880 if (interactively) {
881 // ask user if we should format the current device
882 mr_asprintf(tmp, "Shall I format %s (%s) ?", me->device, me->mountpoint);
883 do_it = ask_me_yes_or_no(tmp);
884 mr_free(tmp);
885 } else {
886 do_it = TRUE;
887 }
888 if (do_it) {
889 // NB: format_device() also stops/starts RAID device if necessary
890 retval += format_device(me->device, me->format, raidlist);
891 }
892 g_current_progress += progress_step;
893 }
894}
895paranoid_system("sync");
896paranoid_system("sync");
897paranoid_system("sync");
898sleep(2);
899// do LVMs now
900log_msg(1, "Creating LVMs");
901if (does_file_exist("/tmp/i-want-my-lvm")) {
902 wait_until_software_raids_are_prepped("/proc/mdstat", 100);
903 log_to_screen("Configuring LVM");
904 if (!g_text_mode) {
905 newtSuspend();
906 }
907 res = do_my_funky_lvm_stuff(FALSE, TRUE);
908 if (!g_text_mode) {
909 newtResume();
910 }
911 if (!res) {
912 log_to_screen("LVM initialized OK");
913 } else {
914 log_to_screen("Failed to initialize LVM");
915 }
916 // retval += res;
917 if (res) {
918 retval++;
919 }
920 sleep(3);
921}
922// do regulars at last
923sleep(2); // woo!
924log_msg(1, "Formatting regulars");
925for (lino = 0; lino < mountlist->entries; lino++) {
926 me = &mountlist->el[lino]; // the current mountlist entry
927 if (!strcmp(me->mountpoint, "image")) {
928 log_it("Not formatting %s - it's an image", me->device);
929 } else if (!strcmp(me->format, "raid")) {
930 log_it("Not formatting %s - it's a raid-let", me->device);
931 continue;
932 } else if (!strcmp(me->format, "lvm")) {
933 log_it("Not formatting %s - it's an LVM", me->device);
934 continue;
935 } else if (!strncmp(me->device, "/dev/md", 7)) {
936 log_it("Already formatted %s - it's a soft-RAID dev", me->device);
937 continue;
938 } else if (!does_file_exist(me->device) && strncmp(me->device, "/dev/hd", 7) && strncmp(me->device, "/dev/sd", 7)) {
939 log_it("Not formatting %s yet - doesn't exist - probably an LVM", me->device);
940 continue;
941 } else {
942 if (interactively) {
943 // ask user if we should format the current device
944 mr_asprintf(tmp, "Shall I format %s (%s) ?", me->device, me->mountpoint);
945 do_it = ask_me_yes_or_no(tmp);
946 mr_free(tmp);
947 } else {
948 do_it = TRUE;
949 }
950
951 if (do_it)
952 retval += format_device(me->device, me->format, raidlist);
953 }
954
955 // update progress bar
956 g_current_progress += progress_step;
957}
958
959
960// update progress bar to 100% to compensate for
961// rounding errors of the progress_step calculation
962if (lino >= mountlist->entries)
963 g_current_progress = g_maximum_progress;
964
965close_progress_form();
966
967if (retval) {
968 mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
969 log_to_screen("Errors may have occurred during the formatting of your hard drives.");
970} else {
971 mvaddstr_and_log_it(g_currentY++, 74, "Done.");
972}
973
974log_it("format_everything () - %s", (retval) ? "failed!" : "finished successfully");
975
976if (g_partition_table_locked_up > 0) {
977 if (retval > 0 && !interactively) {
978 //123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789
979 log_to_screen("Partition table locked up %d times. At least one 'mkfs' (format) command", g_partition_table_locked_up);
980 log_to_screen("failed. I think these two events are related. Sometimes, fdisk's ioctl() call");
981 log_to_screen("to refresh its copy of the partition table causes the kernel to lock the ");
982 log_to_screen("partition table. I believe this has just happened.");
983 if (ask_me_yes_or_no("Please choose 'yes' to reboot and try again; or 'no' to ignore this warning and continue.")) {
984 paranoid_system("sync");
985 paranoid_system("sync");
986 paranoid_system("sync");
987 paranoid_system("reboot");
988 }
989 } else {
990 log_to_screen("Partition table locked up %d time%c. However, disk formatting succeeded.", g_partition_table_locked_up, (g_partition_table_locked_up == 1) ? '.' : 's');
991 }
992}
993newtSuspend();
994paranoid_system("clear");
995newtResume();
996return (retval);
997}
998
999
1000/**
1001 * Create small dummy partitions to fill in the gaps in partition numbering for @p drivename.
1002 * Each partition created is 32k in size.
1003 * @param drivename The drive to create the dummy partitions on.
1004 * @param devno_we_must_allow_for The lowest-numbered real partition; create
1005 * dummies up to (this - 1).
1006 * @return The number of errors encountered (0 for success).
1007 */
1008int make_dummy_partitions(FILE * pout_to_fdisk, char *drivename, int devno_we_must_allow_for) {
1009
1010/** int **************************************************************/
1011int current_devno;
1012int previous_devno;
1013int retval = 0;
1014int res;
1015
1016assert_string_is_neither_NULL_nor_zerolength(drivename);
1017
1018if (devno_we_must_allow_for >= 5) {
1019 log_it("Making dummy primary 1 on %s", drivename);
1020 g_maximum_progress++;
1021 res = partition_device(pout_to_fdisk, drivename, 1, 0, "ext2", 32000);
1022 retval += res;
1023 previous_devno = 1;
1024 current_devno = 5;
1025} else {
1026 previous_devno = 0;
1027 current_devno = 1;
1028}
1029for (; current_devno < devno_we_must_allow_for; current_devno++) {
1030 log_it("Creating dummy partition %d on %s", current_devno, drivename);
1031 g_maximum_progress++;
1032 res = partition_device(pout_to_fdisk, drivename, current_devno, previous_devno, OSSWAP("ext2", "ufs"), 32000);
1033 retval += res;
1034 previous_devno = current_devno;
1035}
1036return (previous_devno);
1037}
1038
1039
1040/**
1041 * Decide whether @p mountlist contains any RAID devices.
1042 * @param mountlist The mountlist to examine.
1043 * @return TRUE if it does, FALSE if it doesn't.
1044 */
1045bool mountlist_contains_raid_devices(struct mountlist_itself * mountlist) {
1046
1047/** int *************************************************************/
1048int i;
1049int matching = 0;
1050
1051/** end **************************************************************/
1052
1053assert(mountlist != NULL);
1054
1055for (i = 0; i < mountlist->entries; i++) {
1056 if (strstr(mountlist->el[i].device, RAID_DEVICE_STUB)) {
1057 matching++;
1058 }
1059}
1060if (matching) {
1061 return (TRUE);
1062} else {
1063 return (FALSE);
1064}
1065}
1066
1067/* The following 2 functions are stolen from /usr/src/sbin/disklabel/disklabel.c */
1068#ifdef __FreeBSD__
1069static void display_disklabel(FILE * f, const struct disklabel *lp) {
1070
1071int i, j;
1072const struct partition *pp;
1073
1074fprintf(f, "# %s\n", "Generated by Mondo Rescue");
1075if (lp->d_type < DKMAXTYPES)
1076 fprintf(f, "type: %s\n", dktypenames[lp->d_type]);
1077else
1078 fprintf(f, "type: %u\n", lp->d_type);
1079fprintf(f, "disk: %.*s\n", (int) sizeof(lp->d_typename), lp->d_typename);
1080fprintf(f, "label: %.*s\n", (int) sizeof(lp->d_packname), lp->d_packname);
1081fprintf(f, "flags:");
1082if (lp->d_flags & D_REMOVABLE)
1083 fprintf(f, " removeable");
1084if (lp->d_flags & D_ECC)
1085 fprintf(f, " ecc");
1086if (lp->d_flags & D_BADSECT)
1087 fprintf(f, " badsect");
1088fprintf(f, "\n");
1089fprintf(f, "bytes/sector: %lu\n", (u_long) lp->d_secsize);
1090fprintf(f, "sectors/track: %lu\n", (u_long) lp->d_nsectors);
1091fprintf(f, "tracks/cylinder: %lu\n", (u_long) lp->d_ntracks);
1092fprintf(f, "sectors/cylinder: %lu\n", (u_long) lp->d_secpercyl);
1093fprintf(f, "cylinders: %lu\n", (u_long) lp->d_ncylinders);
1094fprintf(f, "sectors/unit: %lu\n", (u_long) lp->d_secperunit);
1095fprintf(f, "rpm: %u\n", lp->d_rpm);
1096fprintf(f, "interleave: %u\n", lp->d_interleave);
1097fprintf(f, "trackskew: %u\n", lp->d_trackskew);
1098fprintf(f, "cylinderskew: %u\n", lp->d_cylskew);
1099fprintf(f, "headswitch: %lu\t\t# milliseconds\n", (u_long) lp->d_headswitch);
1100fprintf(f, "track-to-track seek: %ld\t# milliseconds\n", (u_long) lp->d_trkseek);
1101fprintf(f, "drivedata: ");
1102for (i = NDDATA - 1; i >= 0; i--)
1103 if (lp->d_drivedata[i])
1104 break;
1105if (i < 0)
1106 i = 0;
1107for (j = 0; j <= i; j++)
1108 fprintf(f, "%lu ", (u_long) lp->d_drivedata[j]);
1109fprintf(f, "\n\n%u partitions:\n", lp->d_npartitions);
1110fprintf(f, "# size offset fstype [fsize bsize bps/cpg]\n");
1111pp = lp->d_partitions;
1112for (i = 0; i < lp->d_npartitions; i++, pp++) {
1113 if (pp->p_size) {
1114 fprintf(f, " %c: %8lu %8lu ", 'a' + i, (u_long) pp->p_size, (u_long) pp->p_offset);
1115 if (pp->p_fstype < FSMAXTYPES)
1116 fprintf(f, "%8.8s", fstypenames[pp->p_fstype]);
1117 else
1118 fprintf(f, "%8d", pp->p_fstype);
1119 switch (pp->p_fstype) {
1120
1121 case FS_UNUSED: /* XXX */
1122 fprintf(f, " %5lu %5lu %5.5s ", (u_long) pp->p_fsize, (u_long) (pp->p_fsize * pp->p_frag), "");
1123 break;
1124
1125 case FS_BSDFFS:
1126 fprintf(f, " %5lu %5lu %5u ", (u_long) pp->p_fsize, (u_long) (pp->p_fsize * pp->p_frag), pp->p_cpg);
1127 break;
1128
1129 case FS_BSDLFS:
1130 fprintf(f, " %5lu %5lu %5d", (u_long) pp->p_fsize, (u_long) (pp->p_fsize * pp->p_frag), pp->p_cpg);
1131 break;
1132
1133 default:
1134 fprintf(f, "%20.20s", "");
1135 break;
1136 }
1137 fprintf(f, "\t# (Cyl. %4lu", (u_long) (pp->p_offset / lp->d_secpercyl));
1138 if (pp->p_offset % lp->d_secpercyl)
1139 putc('*', f);
1140 else
1141 putc(' ', f);
1142 fprintf(f, "- %lu", (u_long) ((pp->p_offset + pp->p_size + lp->d_secpercyl - 1) / lp->d_secpercyl - 1));
1143 if (pp->p_size % lp->d_secpercyl)
1144 putc('*', f);
1145 fprintf(f, ")\n");
1146 }
1147}
1148fflush(f);
1149}
1150
1151static struct disklabel *get_virgin_disklabel(char *dkname) {
1152
1153static struct disklabel loclab;
1154struct partition *dp;
1155char *lnamebuf = NULL;
1156int f;
1157u_int secsize, u;
1158off_t mediasize;
1159
1160mr_asprintf(lnamebuf, "%s", dkname);
1161if ((f = open(lnamebuf, O_RDONLY)) == -1) {
1162 warn("cannot open %s", lnamebuf);
1163 mr_free(lnamebuf);
1164 return (NULL);
1165}
1166mr_free(lnamebuf);
1167
1168/* New world order */
1169if ((ioctl(f, DIOCGMEDIASIZE, &mediasize) != 0)
1170 || (ioctl(f, DIOCGSECTORSIZE, &secsize) != 0)) {
1171 close(f);
1172 return (NULL);
1173}
1174memset(&loclab, 0, sizeof loclab);
1175loclab.d_magic = DISKMAGIC;
1176loclab.d_magic2 = DISKMAGIC;
1177loclab.d_secsize = secsize;
1178loclab.d_secperunit = mediasize / secsize;
1179
1180/*
1181 * Nobody in these enligthened days uses the CHS geometry for
1182 * anything, but nontheless try to get it right. If we fail
1183 * to get any good ideas from the device, construct something
1184 * which is IBM-PC friendly.
1185 */
1186if (ioctl(f, DIOCGFWSECTORS, &u) == 0)
1187 loclab.d_nsectors = u;
1188else
1189 loclab.d_nsectors = 63;
1190
1191if (ioctl(f, DIOCGFWHEADS, &u) == 0)
1192 loclab.d_ntracks = u;
1193else if (loclab.d_secperunit <= 63 * 1 * 1024)
1194 loclab.d_ntracks = 1;
1195else if (loclab.d_secperunit <= 63 * 16 * 1024)
1196 loclab.d_ntracks = 16;
1197else
1198 loclab.d_ntracks = 255;
1199loclab.d_secpercyl = loclab.d_ntracks * loclab.d_nsectors;
1200loclab.d_ncylinders = loclab.d_secperunit / loclab.d_secpercyl;
1201loclab.d_npartitions = MAXPARTITIONS;
1202
1203/* Various (unneeded) compat stuff */
1204loclab.d_rpm = 3600;
1205loclab.d_bbsize = BBSIZE;
1206loclab.d_interleave = 1;;
1207strncpy(loclab.d_typename, "amnesiac", sizeof(loclab.d_typename));
1208
1209 dp = &loclab.d_partitions[RAW_PART];
1210dp->p_size = loclab.d_secperunit;
1211loclab.d_checksum = dkcksum(&loclab);
1212close(f);
1213return (&loclab);
1214}
1215
1216/* End stolen from /usr/src/sbin/disklabel/disklabel.c. */
1217
1218char *canonical_name(char *drivename) {
1219
1220if (drivename) {
1221 if (strncmp(drivename, "/dev/", 5) == 0) {
1222 return drivename + 5;
1223 }
1224}
1225return drivename;
1226}
1227
1228/**
1229 * (BSD only) Create a disklabel on @p drivename according to @p mountlist.
1230 * @param mountlist The mountlist to get the subpartition information from.
1231 * @param drivename The drive or slice to create a disklabel on.
1232 * @param ret If non-NULL, store the created disklabel here.
1233 * @return The number of errors encountered (0 for success).
1234 */
1235int label_drive_or_slice(struct mountlist_itself *mountlist, char *drivename, struct disklabel *ret) {
1236
1237char *subdev_str = NULL;
1238char *command = NULL;
1239struct disklabel *lp;
1240int i, lo = 0;
1241int retval = 0;
1242char c;
1243FILE *ftmp;
1244
1245lp = get_virgin_disklabel(drivename);
1246for (c = 'a'; c <= 'z'; ++c) {
1247 int idx;
1248 mr_asprintf(subdev_str, "%s%c", drivename, c);
1249 if ((idx = find_device_in_mountlist(mountlist, subdev_str)) < 0) {
1250 lp->d_partitions[c - 'a'].p_size = 0;
1251 lp->d_partitions[c - 'a'].p_fstype = FS_UNUSED;
1252 } else {
1253 lo = c - 'a';
1254 lp->d_partitions[c - 'a'].p_size = mountlist->el[idx].size * 2;
1255 lp->d_partitions[c - 'a'].p_fsize = 0;
1256 lp->d_partitions[c - 'a'].p_frag = 0;
1257 lp->d_partitions[c - 'a'].p_cpg = 0;
1258 if (!strcmp(mountlist->el[idx].format, "ufs") || !strcmp(mountlist->el[idx].format, "ffs") || !strcmp(mountlist->el[idx].format, "4.2BSD")) {
1259 lp->d_partitions[c - 'a'].p_fstype = FS_BSDFFS;
1260 lp->d_partitions[c - 'a'].p_fsize = 2048;
1261 lp->d_partitions[c - 'a'].p_frag = 8;
1262 lp->d_partitions[c - 'a'].p_cpg = 64;
1263 } else if (!strcasecmp(mountlist->el[idx].format, "raid") || !strcasecmp(mountlist->el[idx].format, "vinum")) {
1264 lp->d_partitions[c - 'a'].p_fstype = FS_VINUM;
1265 } else if (!strcmp(mountlist->el[idx].format, "swap")) {
1266 lp->d_partitions[c - 'a'].p_fstype = FS_SWAP;
1267 } else
1268 lp->d_partitions[c - 'a'].p_fstype = FS_OTHER;
1269 }
1270 mr_free(subdev_str);
1271}
1272
1273// fix up the offsets
1274lp->d_partitions[0].p_offset = 0;
1275lp->d_partitions[RAW_PART].p_offset = 0;
1276lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
1277lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
1278
1279for (i = 1; i < lp->d_npartitions; ++i) {
1280 int lastone;
1281 if ((i == RAW_PART) || (lp->d_partitions[i].p_size == 0))
1282 continue;
1283 for (lastone = i - 1; lastone >= 0; lastone--) {
1284 if ((lp->d_partitions[lastone].p_size) && (lastone != RAW_PART))
1285 break;
1286 }
1287 lp->d_partitions[i].p_offset = lp->d_partitions[lastone].p_offset + lp->d_partitions[lastone].p_size;
1288}
1289if (lp->d_partitions[lo].p_offset + lp->d_partitions[lo].p_size > lp->d_secperunit) {
1290 lp->d_partitions[lo].p_size = lp->d_secperunit - lp->d_partitions[lo].p_offset;
1291}
1292
1293ftmp = fopen("/tmp/disklabel", "w");
1294display_disklabel(ftmp, lp);
1295fclose(ftmp);
1296mr_asprintf(command, "disklabel -wr %s auto", canonical_name(drivename));
1297retval += run_program_and_log_output(command, TRUE);
1298mr_free(command);
1299
1300mr_asprintf(command, "disklabel -R %s /tmp/disklabel", canonical_name(drivename));
1301retval += run_program_and_log_output(command, TRUE);
1302mr_free(command);
1303
1304if (ret)
1305 *ret = *lp;
1306return retval;
1307}
1308#endif
1309
1310
1311/**
1312 * Partition @p drivename based on @p mountlist.
1313 * @param mountlist The mountlist to use to guide the partitioning.
1314 * @param drivename The drive to partition.
1315 * @return 0 for success, nonzero for failure.
1316 */
1317int partition_drive(struct mountlist_itself *mountlist, char *drivename) {
1318
1319/** int *************************************************************/
1320int current_devno;
1321int previous_devno = 0;
1322int lino;
1323int retval = 0;
1324int i;
1325FILE *pout_to_fdisk = NULL;
1326char *part_table_fmt = NULL;
1327
1328#ifdef __FreeBSD__
1329bool fbsd_part = FALSE;
1330char *subdev_str = NULL;
1331#endif
1332
1333/** long long *******************************************************/
1334long long partsize;
1335
1336/** buffers *********************************************************/
1337char *device_str;
1338char *format = NULL;
1339char *tmp = NULL;
1340char *tmp1 = NULL;
1341
1342/** end *************************************************************/
1343
1344assert(mountlist != NULL);
1345assert_string_is_neither_NULL_nor_zerolength(drivename);
1346
1347
1348log_it("Partitioning drive %s", drivename);
1349
1350#if __FreeBSD__
1351 log_it("(Not opening fdisk now; that's the Linux guy's job)");
1352 pout_to_fdisk = NULL;
1353#else
1354make_hole_for_file(FDISK_LOG);
1355mr_asprintf(tmp, "mr-parted2fdisk %s >> %s 2>> %s", drivename, FDISK_LOG, FDISK_LOG);
1356pout_to_fdisk = popen(tmp, "w");
1357if (!pout_to_fdisk) {
1358 log_to_screen("Cannot call mr-parted2fdisk to configure %s", drivename);
1359 mr_free(tmp);
1360 return (1);
1361}
1362mr_free(tmp);
1363#endif
1364
1365malloc_string(device_str);
1366
1367for (current_devno = 1; current_devno < 99; current_devno++) {
1368 build_partition_name(device_str, drivename, current_devno);
1369 lino = find_device_in_mountlist(mountlist, device_str);
1370
1371 if (lino < 0) {
1372 // device not found in mountlist
1373#if __FreeBSD__
1374 // If this is the first partition (just as a sentinel value),
1375 // then see if the user has picked 'dangerously-dedicated' mode.
1376 // If so, then we just call label_drive_or_slice() and return.
1377 char c;
1378 if (current_devno == 1) {
1379 // try DangerouslyDedicated mode
1380 for (c = 'a'; c <= 'z'; c++) {
1381 mr_asprintf(subdev_str, "%s%c", drivename, c);
1382 if (find_device_in_mountlist(mountlist, subdev_str) > 0) {
1383 fbsd_part = TRUE;
1384 }
1385 mr_free(subdev_str);
1386 }
1387 if (fbsd_part) {
1388 int r = label_drive_or_slice(mountlist,
1389 drivename,
1390 0);
1391 char command[MAX_STR_LEN];
1392 sprintf(command, "disklabel -B %s",
1393 basename(drivename));
1394 if (system(command)) {
1395 log_to_screen
1396 ("Warning! Unable to make the drive bootable.");
1397 }
1398 paranoid_free(device_str);
1399 return r;
1400 }
1401 }
1402 for (c = 'a'; c <= 'z'; c++) {
1403 mr_assprintf(subdev_str, "%s%c", device_str, c);
1404 if (find_device_in_mountlist(mountlist, subdev_str) > 0) {
1405 fbsd_part = TRUE;
1406 }
1407 mr_free(subdev_str);
1408 }
1409 // Now we check the subpartitions of the current partition.
1410 if (fbsd_part) {
1411 int i, line;
1412
1413 mr_asprintf(format, "ufs");
1414 partsize = 0;
1415 for (i = 'a'; i < 'z'; ++i) {
1416 mr_asprintf(subdev_str, "%s%c", device_str, i);
1417 line = find_device_in_mountlist(mountlist, subdev_str);
1418 mr_free(subdev_str);
1419
1420 if (line > 0) {
1421 // We found one! Add its size to the total size.
1422 partsize += mountlist->el[line].size;
1423 }
1424 }
1425 } else {
1426 continue;
1427 }
1428#else
1429 continue;
1430#endif
1431 }
1432
1433 /* OK, we've found partition /dev/hdxN in mountlist; let's prep it */
1434 /* For FreeBSD, that is /dev/adXsY */
1435
1436 log_it("Found partition %s in mountlist", device_str);
1437 if (!previous_devno) {
1438
1439 log_it("Wiping %s's partition table", drivename);
1440#if __FreeBSD__
1441 // FreeBSD doesn't let you write to blk devices in <512byte chunks.
1442 file = open(drivename, O_WRONLY);
1443 if (file != -1) {
1444 log_to_screnn("Warning - unable to open %s for wiping it's partition table", drivename);
1445 }
1446
1447 for (i = 0; i < 512; i++) {
1448 if (!write(file, "\0", 1)) {
1449 log_to_screen("Warning - unable to write to %s", drivename);
1450 }
1451 }
1452 paranoid_system("sync");
1453#else
1454 log_it("New, kernel-friendly partition remover");
1455 for (i = 20; i > 0; i--) {
1456 fprintf(pout_to_fdisk, "d\n%d\n", i);
1457 fflush(pout_to_fdisk);
1458 }
1459#endif
1460 if (current_devno > 1) {
1461 previous_devno = make_dummy_partitions(pout_to_fdisk, drivename, current_devno);
1462 }
1463 }
1464#ifdef __FreeBSD__
1465 if (!fbsd_part) {
1466 mr_free(format);
1467#endif
1468
1469 mr_asprintf(format, "%s", mountlist->el[lino].format);
1470 partsize = mountlist->el[lino].size;
1471
1472#ifdef __FreeBSD__
1473 }
1474#endif
1475
1476#ifndef __IA64__
1477 part_table_fmt = which_partition_format(drivename);
1478
1479 /* MBR needs an extended partition if more than 4 partitions */
1480 if (strcmp(part_table_fmt, "MBR") == 0) {
1481 if (current_devno == 5 && previous_devno == 4) {
1482 log_to_screen("You must leave at least one partition spare as the Extended partition.");
1483 paranoid_free(device_str);
1484 mr_free(format);
1485 return (1);
1486 }
1487 }
1488 mr_free(part_table_fmt);
1489#endif
1490
1491 retval += partition_device(pout_to_fdisk, drivename, current_devno, previous_devno, format, partsize);
1492 mr_free(format);
1493
1494#ifdef __FreeBSD__
1495 if ((current_devno <= 4) && fbsd_part) {
1496 mr_asprintf(tmp, "disklabel -B %s", basename(device_str));
1497 retval += label_drive_or_slice(mountlist, device_str, 0);
1498 if (system(tmp)) {
1499 log_to_screen("Warning! Unable to make the slice bootable.");
1500 }
1501 mr_free(tmp);
1502 }
1503#endif
1504
1505 previous_devno = current_devno;
1506}
1507
1508if (pout_to_fdisk) {
1509 // close fdisk
1510 fput_string_one_char_at_a_time(pout_to_fdisk, "p\n");
1511 fput_string_one_char_at_a_time(pout_to_fdisk, "w\n");
1512 paranoid_pclose(pout_to_fdisk);
1513 paranoid_system("sync");
1514 log_msg(0,"------------------- fdisk.log looks like this ------------------");
1515 mr_asprintf(tmp, "cat %s >> %s", FDISK_LOG, MONDO_LOGFILE);
1516 paranoid_system(tmp);
1517 mr_free(tmp);
1518
1519 // mark relevant partition as bootable
1520 mr_asprintf(tmp1,"mr-make-me-bootable /tmp/mountlist.txt %s",drivename);
1521 tmp = call_program_and_get_last_line_of_output(tmp1);
1522 mr_free(tmp1);
1523 mr_free(tmp);
1524
1525 log_msg(0,"------------------- end of fdisk.log... ------------------");
1526 paranoid_system("sync");
1527 mr_asprintf(tmp, "tail -n6 %s | grep -F \"16: \"", FDISK_LOG);
1528 if (!run_program_and_log_output(tmp, 5)) {
1529 g_partition_table_locked_up++;
1530 }
1531 mr_free(tmp);
1532
1533 mr_asprintf(tmp, "partprobe %s", drivename);
1534 if (!run_program_and_log_output(tmp, 5)) {
1535 g_partition_table_locked_up--;
1536 }
1537 mr_free(tmp);
1538
1539 if (g_partition_table_locked_up > 0) {
1540 log_to_screen("A flaw in the Linux kernel has locked the partition table. Even calling partprobe did not suceed :-(");
1541 }
1542}
1543paranoid_free(device_str);
1544return (retval);
1545}
1546
1547/**
1548 * Create partition number @p partno on @p drive with @p fdisk.
1549 * @param drive The drive to create the partition on.
1550// * @param partno The partition number of the new partition (1-4 are primary, >=5 is logical).
1551 * @param prev_partno The partition number of the most recently prepped partition.
1552 * @param format The filesystem type of this partition (used to set the type).
1553 * @param partsize The size of the partition in @e bytes.
1554 * @return 0 for success, nonzero for failure.
1555 */
1556int partition_device(FILE * pout_to_fdisk, const char *drive, int partno, int prev_partno, const char *format, long long partsize) {
1557
1558/** int **************************************************************/
1559int retval = 0;
1560int res = 0;
1561
1562/** buffers **********************************************************/
1563char *program = NULL;
1564char *partition_name;
1565char *tmp = NULL;
1566char *output = NULL;
1567
1568/** pointers **********************************************************/
1569char *part_table_fmt = NULL;
1570FILE *fout;
1571
1572/** end ***************************************************************/
1573
1574assert_string_is_neither_NULL_nor_zerolength(drive);
1575assert(format != NULL);
1576
1577log_it("partition_device('%s', %d, %d, '%s', %lld) --- starting", drive, partno, prev_partno, format, partsize);
1578
1579if (!strncmp(drive, RAID_DEVICE_STUB, strlen(RAID_DEVICE_STUB))) {
1580 log_it("Not partitioning %s - it is a virtual drive", drive);
1581 return (0);
1582}
1583
1584malloc_string(partition_name);
1585build_partition_name(partition_name, drive, partno);
1586if (partsize <= 0) {
1587 mr_asprintf(tmp, "Partitioning device %s (max size)", partition_name);
1588} else {
1589 mr_asprintf(tmp, "Partitioning device %s (%lld MB)", partition_name, (long long) partsize / 1024);
1590}
1591update_progress_form(tmp);
1592log_it(tmp);
1593mr_free(tmp);
1594
1595if (is_this_device_mounted(partition_name)) {
1596 log_to_screen("%s is mounted, and should not be partitioned", partition_name);
1597 paranoid_free(partition_name);
1598 return (1);
1599}
1600
1601
1602mr_asprintf(program, "mr-parted2fdisk %s >> %s 2>> %s", drive, MONDO_LOGFILE, MONDO_LOGFILE);
1603
1604/* TODO: should not be called each time */
1605part_table_fmt = which_partition_format(drive);
1606mr_asprintf(output, "");
1607
1608/* make it a primary/extended/logical */
1609if (partno <= 4) {
1610 mr_strcat(output, "n\np\n%d\n", partno);
1611} else {
1612 /* MBR needs an extended partition if more than 4 partitions */
1613 if (strcmp(part_table_fmt, "MBR") == 0) {
1614 if (partno == 5) {
1615 if (prev_partno >= 4) {
1616 log_to_screen("You need to leave at least one partition free, for 'extended/logical'");
1617 paranoid_free(partition_name);
1618 paranoid_free(output);
1619 return (1);
1620 } else {
1621 mr_strcat(output, "n\ne\n%d\n\n\n", prev_partno + 1);
1622 }
1623 }
1624 mr_strcat(output, "n\nl\n");
1625 } else {
1626 /* GPT allows more than 4 primary partitions */
1627 mr_strcat(output, "n\np\n%d\n", partno);
1628 }
1629}
1630mr_free(part_table_fmt);
1631
1632mr_strcat(output, "\n"); /*start block (ENTER for next free blk */
1633if (partsize > 0) {
1634 if (!strcmp(format, "7")) {
1635 log_msg(1, "Adding 512K, just in case");
1636 partsize += 512;
1637 }
1638 mr_strcat(output, "+%lldK", (long long) (partsize));
1639}
1640mr_strcat(output, "\n");
1641#if 0
1642/*
1643#endif
1644log_it("PARTSIZE = +%ld",(long)partsize);
1645log_it("---fdisk command---");
1646log_it(output);
1647log_it("---end of fdisk---");
1648#if 0
1649*/
1650#endif
1651
1652
1653if (pout_to_fdisk) {
1654 log_msg(1, "Doing the new all-in-one fdisk thing");
1655 mr_strcat(output, "\n\np\n\n");
1656 log_msg(1, "output = '%s'", output);
1657 fput_string_one_char_at_a_time(pout_to_fdisk, output);
1658 mr_asprintf(tmp, "%s", last_line_of_file(FDISK_LOG));
1659 if (strstr(tmp, " (m ")) {
1660 log_msg(1, "Successfully created partition %d on %s", partno, drive);
1661 } else {
1662 log_msg(1, "last line = %s", tmp);
1663 log_msg(1, "Failed to create partition %d on %s; sending 'Enter'...", partno, drive);
1664 }
1665 mr_free(tmp);
1666
1667 if (!retval) {
1668 log_msg(1, "Trying to set partition %d type now on %s", partno, drive);
1669 retval = set_partition_type(pout_to_fdisk, drive, partno, format, partsize);
1670 if (retval) {
1671 log_msg(1, "Failed. Trying again...");
1672 retval = set_partition_type(pout_to_fdisk, drive, partno, format, partsize);
1673 }
1674 }
1675 if (retval) {
1676 log_msg(1, "...but failed to set type");
1677 }
1678} else {
1679 mr_strcat(output, "w\n\n");
1680 if (g_fprep) {
1681 fprintf(g_fprep, "echo \"%s\" | %s\n", output, program);
1682 }
1683 /* write to disk; close fdisk's stream */
1684 if (!(fout = popen(program, "w"))) {
1685 log_OS_error("can't popen-out to program");
1686 } else {
1687 fputs(output, fout);
1688 paranoid_pclose(fout);
1689 }
1690
1691 if (!does_partition_exist(drive, partno) && partsize > 0) {
1692 log_it("Vaccum-packing");
1693 g_current_progress--;
1694 res = partition_device(pout_to_fdisk, drive, partno, prev_partno, format, -1);
1695 if (res) {
1696 log_it("Failed to vacuum-pack %s", partition_name);
1697 retval++;
1698 } else {
1699 retval = 0;
1700 }
1701 }
1702 if (does_partition_exist(drive, partno)) {
1703 retval = set_partition_type(pout_to_fdisk, drive, partno, format, partsize);
1704 if (retval) {
1705 log_it("Partitioned %s but failed to set its type", partition_name);
1706 } else {
1707 if (partsize > 0) {
1708 log_to_screen("Partition %s created+configured OK", partition_name);
1709 } else {
1710 log_it("Returning from a successful vacuum-pack");
1711 }
1712 }
1713 } else {
1714 mr_asprintf(tmp, "Failed to partition %s", partition_name);
1715 if (partsize > 0) {
1716 log_to_screen(tmp);
1717 } else {
1718 log_it(tmp);
1719 }
1720 mr_free(tmp);
1721 retval++;
1722 }
1723}
1724mr_free(program);
1725mr_free(output);
1726
1727g_current_progress++;
1728log_it("partition_device() --- leaving");
1729paranoid_free(partition_name);
1730return (retval);
1731}
1732
1733
1734
1735/**
1736 * Create all partitions listed in @p mountlist.
1737 * @param mountlist The mountlist to use to guide the partitioning.
1738 * @return The number of errors encountered (0 for success).
1739 * @note This sets the partition types but doesn't actually do the formatting.
1740 * Use format_everything() for that.
1741 */
1742int partition_everything(struct mountlist_itself *mountlist) {
1743
1744/** int ************************************************************/
1745int lino;
1746int retval = 0;
1747int i;
1748int res;
1749
1750/** buffer *********************************************************/
1751struct list_of_disks *drivelist;
1752
1753/** end ************************************************************/
1754
1755drivelist = malloc(sizeof(struct list_of_disks));
1756assert(mountlist != NULL);
1757
1758log_it("partition_everything() --- starting");
1759mvaddstr_and_log_it(g_currentY, 0, "Partitioning hard drives ");
1760
1761if (mountlist_contains_raid_devices(mountlist)) {
1762 log_msg(0, "Mountlist, including the partitions incorporated in RAID devices:-");
1763 for (i = 0; i < mountlist->entries; i++) {
1764 log_it(mountlist->el[i].device);
1765 }
1766 log_msg(0, "End of mountlist.");
1767}
1768log_msg(0, "Stopping all LVMs, just in case");
1769if (!g_text_mode) {
1770 newtSuspend();
1771}
1772do_my_funky_lvm_stuff(TRUE, FALSE); // just remove old partitions
1773if (!g_text_mode) {
1774 newtResume();
1775}
1776log_msg(0, "Stopping all software RAID devices, just in case");
1777stop_all_raid_devices(mountlist);
1778log_msg(0, "Done.");
1779
1780open_progress_form("Partitioning devices", "I am now going to partition all your drives.", "This should not take more than five minutes.", "", mountlist->entries);
1781
1782make_list_of_drives_in_mountlist(mountlist, drivelist);
1783
1784/* partition each drive */
1785for (lino = 0; lino < drivelist->entries; lino++) {
1786 res = partition_drive(mountlist, drivelist->el[lino].device);
1787 retval += res;
1788}
1789close_progress_form();
1790if (retval) {
1791 mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1792 log_to_screen("Errors may have occurred during the partitioning of your hard drives.");
1793} else {
1794 mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1795 paranoid_system("rm -f /tmp/fdisk*.log 2> /dev/null");
1796}
1797newtSuspend();
1798paranoid_system("clear");
1799newtResume();
1800paranoid_free(drivelist);
1801return (retval);
1802}
1803
1804
1805
1806/**
1807 * Set the type of partition number @p partno on @p drive to @p format.
1808 * @param drive The drive to change the type of a partition on.
1809 * @param partno The partition number on @p drive to change the type of.
1810 * @param format The filesystem type this partition will eventually contain.
1811 * @param partsize The size of this partition, in @e bytes (used for vfat
1812 * type calculations).
1813 * @return 0 for success, nonzero for failure.
1814 */
1815int set_partition_type(FILE * pout_to_fdisk, const char *drive, int partno, const char *format, long long partsize) {
1816
1817/** buffers *********************************************************/
1818char *partition;
1819char *command = NULL;
1820char *output = NULL;
1821char *tmp = NULL;
1822char *partcode = NULL;
1823
1824/** pointers *********************************************************/
1825FILE *fout;
1826
1827/** int **************************************************************/
1828int res = 0;
1829
1830/** end **************************************************************/
1831
1832assert_string_is_neither_NULL_nor_zerolength(drive);
1833assert(format != NULL);
1834
1835malloc_string(partition);
1836
1837build_partition_name(partition, drive, partno);
1838if (strcmp(format, "swap") == 0) {
1839 mr_asprintf(partcode, "82");
1840} else if (strcmp(format, "vfat") == 0) {
1841 if (partsize / 1024 > 8192) {
1842 mr_asprintf(partcode, "c");
1843 } else {
1844 mr_asprintf(partcode, "b");
1845 }
1846} else if (strcmp(format, "ext2") == 0
1847 || strcmp(format, "reiserfs") == 0
1848 || strcmp(format, "ext3") == 0
1849 || strcmp(format, "ext4") == 0
1850 || strcmp(format, "xfs") == 0
1851 || strcmp(format, "jfs") == 0
1852 || strcmp(format, "btrfs") == 0) {
1853 mr_asprintf(partcode, "83");
1854} else if (strcmp(format, "minix") == 0) {
1855 mr_asprintf(partcode, "81");
1856} else if (strcmp(format, "vmfs3") == 0) {
1857 mr_asprintf(partcode, "fb");
1858} else if (strcmp(format, "vmkcore") == 0) {
1859 mr_asprintf(partcode, "fc");
1860} else if (strcmp(format, "raid") == 0) {
1861 mr_asprintf(partcode, "fd");
1862} else if (strcmp(format, "ntfs") == 0) {
1863 mr_asprintf(partcode, "7");
1864} else if ((strcmp(format, "ufs") == 0)
1865 || (strcmp(format, "ffs") == 0)) { /* raid autodetect */
1866 mr_asprintf(partcode, "a5");
1867} else if (strcmp(format, "lvm") == 0) {
1868 mr_asprintf(partcode, "8e");
1869} else if (format[0] == '\0') { /* LVM physical partition */
1870 mr_asprintf(partcode, "");
1871} else if (strlen(format) >= 1 && strlen(format) <= 2) {
1872 mr_asprintf(partcode, "%s", format);
1873} else {
1874 /* probably an image */
1875 mr_asprintf(tmp, "Unknown format ('%s') - using supplied string anyway", format);
1876 mvaddstr_and_log_it(g_currentY++, 0, tmp);
1877 mr_free(tmp);
1878
1879#ifdef __FreeBSD__
1880 mr_asprintf(partcode, "%s", format); // was a5
1881#else
1882 mr_asprintf(partcode, "%s", format); // was 83
1883#endif
1884}
1885log_msg(1, "Setting %s's type to %s (%s)", partition, format, partcode);
1886if ((strcmp(partcode,"") != 0) && strcmp(partcode, "83")) { /* no need to set type if 83: 83 is default */
1887
1888 if (pout_to_fdisk) {
1889 res = 0;
1890 fput_string_one_char_at_a_time(pout_to_fdisk, "t\n");
1891 if (partno > 1
1892 || strstr(last_line_of_file(FDISK_LOG), " (1-4)")) {
1893 log_msg(5, "Specifying partno (%d) - yay", partno);
1894 mr_asprintf(tmp, "%d\n", partno);
1895 fput_string_one_char_at_a_time(pout_to_fdisk, tmp);
1896 log_msg(5, "A - last line = '%s'", last_line_of_file(FDISK_LOG));
1897 mr_free(tmp);
1898 }
1899
1900 mr_asprintf(tmp, "%s\n", partcode);
1901 fput_string_one_char_at_a_time(pout_to_fdisk, tmp);
1902 mr_free(tmp);
1903
1904 log_msg(5, "B - last line = '%s'",
1905 last_line_of_file(FDISK_LOG));
1906 fput_string_one_char_at_a_time(pout_to_fdisk, "\n");
1907 log_msg(5, "C - last line = '%s'",
1908 last_line_of_file(FDISK_LOG));
1909
1910 mr_asprintf(tmp, "%s", last_line_of_file(FDISK_LOG));
1911 if (!strstr(tmp, " (m ")) {
1912 log_msg(1, "last line = '%s'; part type set failed", tmp);
1913 res++;
1914 fput_string_one_char_at_a_time(pout_to_fdisk, "\n");
1915 }
1916 mr_free(tmp);
1917
1918 fput_string_one_char_at_a_time(pout_to_fdisk, "p\n");
1919 } else {
1920 mr_asprintf(output, "t\n%d\n%s\nw\n", partno, partcode);
1921 mr_asprintf(command, "mr-parted2fdisk %s >> %s 2>> %s", drive, MONDO_LOGFILE, MONDO_LOGFILE);
1922 log_msg(5, "output = '%s'", output);
1923 log_msg(5, "partno=%d; partcode=%s", partno, partcode);
1924 log_msg(5, "command = '%s'", command);
1925 fout = popen(command, "w");
1926 if (!fout) {
1927 log_OS_error(command);
1928 res = 1;
1929 } else {
1930 res = 0;
1931 fprintf(fout, "%s", output);
1932 paranoid_pclose(fout);
1933 }
1934 mr_free(command);
1935 paranoid_free(output);
1936 }
1937}
1938mr_free(partcode);
1939
1940paranoid_free(partition);
1941
1942return (res);
1943}
1944
1945
1946int start_raid_device(char *raid_device) {
1947
1948/** int *************************************************************/
1949int res;
1950int retval = 0;
1951
1952/** buffers *********************************************************/
1953char *program = NULL;
1954
1955/** end *************************************************************/
1956
1957assert_string_is_neither_NULL_nor_zerolength(raid_device);
1958
1959#ifdef __FreeBSD__
1960if (is_this_device_mounted(raid_device)) {
1961 log_it("Can't start %s when it's mounted!", raid_device);
1962 return 1;
1963}
1964mr_asprintf(program, "vinum start -f %s", raid_device);
1965#else
1966mr_asprintf(program, "raidstart %s", raid_device);
1967#endif
1968log_msg(1, "program = %s", program);
1969res = run_program_and_log_output(program, 1);
1970if (g_fprep) {
1971 fprintf(g_fprep, "%s\n", program);
1972}
1973mr_free(program);
1974
1975if (res) {
1976 log_msg(1, "Warning - failed to start RAID device %s", raid_device);
1977}
1978retval += res;
1979sleep(1);
1980return (retval);
1981}
1982
1983
1984
1985/**
1986 * Stop @p raid_device using @p raidstop.
1987 * @param raid_device The software RAID device to stop.
1988 * @return 0 for success, nonzero for failure.
1989 */
1990int stop_raid_device(char *raid_device) {
1991
1992/** int *************************************************************/
1993int res;
1994int retval = 0;
1995
1996/** buffers *********************************************************/
1997char *program = NULL;
1998
1999/** end *************************************************************/
2000
2001assert_string_is_neither_NULL_nor_zerolength(raid_device);
2002
2003#ifdef __FreeBSD__
2004if (is_this_device_mounted(raid_device)) {
2005 log_it("Can't stop %s when it's mounted!", raid_device);
2006 return 1;
2007}
2008mr_asprintf(program, "vinum stop -f %s", raid_device);
2009#else
2010// use raidstop if it exists, otherwise use mdadm
2011if (run_program_and_log_output("which raidstop", FALSE)) {
2012 mr_asprintf(program, "mdadm -S %s", raid_device);
2013} else {
2014 mr_asprintf(program, "raidstop %s", raid_device);
2015}
2016#endif
2017log_msg(1, "program = %s", program);
2018res = run_program_and_log_output(program, 1);
2019if (g_fprep) {
2020 fprintf(g_fprep, "%s\n", program);
2021}
2022mr_free(program);
2023
2024if (res) {
2025 log_msg(1, "Warning - failed to stop RAID device %s", raid_device);
2026}
2027retval += res;
2028return (retval);
2029}
2030
2031
2032int start_all_raid_devices(struct mountlist_itself *mountlist) {
2033
2034int i;
2035int retval = 0;
2036int res;
2037
2038for (i = 0; i < mountlist->entries; i++) {
2039 if (!strncmp(mountlist->el[i].device, RAID_DEVICE_STUB, strlen(RAID_DEVICE_STUB))) {
2040 log_msg(1, "Starting %s", mountlist->el[i].device);
2041 res = start_raid_device(mountlist->el[i].device);
2042 retval += res;
2043 }
2044}
2045if (retval) {
2046 log_msg(1, "Started all s/w raid devices OK");
2047} else {
2048 log_msg(1, "Failed to start some/all s/w raid devices");
2049}
2050return (retval);
2051}
2052
2053/**
2054 * Stop all software RAID devices listed in @p mountlist.
2055 * @param mountlist The mountlist to stop the RAID devices in.
2056 * @return The number of errors encountered (0 for success).
2057 * @bug @p mountlist is not used.
2058 */
2059int stop_all_raid_devices(struct mountlist_itself *mountlist) {
2060
2061/** int *************************************************************/
2062int retval = 0;
2063
2064/** char ************************************************************/
2065char *incoming;
2066#ifndef __FreeBSD__
2067char *dev;
2068#endif
2069/** pointers ********************************************************/
2070#ifndef __FreeBSD__
2071char *p;
2072#endif
2073FILE *fin;
2074char *q;
2075int i;
2076
2077/** end ****************************************************************/
2078
2079malloc_string(incoming);
2080assert(mountlist != NULL);
2081
2082for (i = 0; i < 3; i++) {
2083#ifdef __FreeBSD__
2084 fin = popen("vinum list | grep '^[PVS]' | sed 's/S/1/;s/P/2/;s/V/3/' | sort | cut -d' ' -f2", "r");
2085 if (!fin) {
2086 paranoid_free(incoming);
2087 return (1);
2088 }
2089 for (q = fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin) && (q != NULL); q = fgets(incoming, MAX_STR_LEN - 1, fin)) {
2090 retval += stop_raid_device(incoming);
2091 }
2092#else
2093 fin = fopen("/proc/mdstat", "r");
2094 if (!fin) {
2095 log_OS_error("/proc/mdstat");
2096 paranoid_free(incoming);
2097 return (1);
2098 }
2099 for (q = fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin) && (q != NULL); q = fgets(incoming, MAX_STR_LEN - 1, fin)) {
2100 for (p = incoming; *p != '\0' && (*p != 'm' || *(p + 1) != 'd' || !isdigit(*(p + 2))); p++);
2101 if (*p != '\0') {
2102 malloc_string(dev);
2103 sprintf(dev, "/dev/%s", p);
2104 for (p = dev; *p > 32; p++);
2105 *p = '\0';
2106 retval += stop_raid_device(dev);
2107 paranoid_free(dev);
2108 }
2109 }
2110#endif
2111}
2112paranoid_fclose(fin);
2113if (retval) {
2114 log_msg(1, "Warning - unable to stop some RAID devices");
2115}
2116paranoid_free(incoming);
2117paranoid_system("sync");
2118paranoid_system("sync");
2119paranoid_system("sync");
2120sleep(1);
2121return (retval);
2122}
2123
2124
2125
2126/**
2127 * Decide which command we need to use to format a device of type @p format.
2128 * @param format The filesystem type we are about to format.
2129 * @param program Where to put the binary name for this format.
2130 * @return 0 for success, nonzero for failure.
2131 */
2132char *which_format_command_do_i_need(char *format) {
2133
2134char *program = NULL;
2135
2136assert_string_is_neither_NULL_nor_zerolength(format);
2137
2138if (strcmp(format, "swap") == 0) {
2139#ifdef __FreeBSD__
2140 mr_asprintf(program, "true");
2141#else
2142 mr_asprintf(program, "mkswap -f");
2143#endif
2144} else if (strcmp(format, "vfat") == 0) {
2145#ifdef __FreeBSD__
2146 mr_asprintf(program, "newfs_msdos -F 32");
2147#else
2148#ifdef __IA64__
2149 /* For EFI partitions on ia64 take fat16
2150 * as we want to make small ones */
2151 mr_asprintf(program, "mkfs.vfat -F 16");
2152#else
2153 /* mkfs.vfat will make the best possible choice itself */
2154 /* should avoid problems with mr-label later on when used */
2155 mr_asprintf(program, "mkfs.vfat");
2156#endif
2157#endif
2158#ifndef __FreeBSD__
2159} else if (strcmp(format, "reiserfs") == 0) {
2160 mr_asprintf(program, "mkreiserfs -ff");
2161} else if (strcmp(format, "xfs") == 0) {
2162 /* Cf: https://bugzilla.redhat.com/show_bug.cgi?id=1309498 */
2163 mr_asprintf(program, "mkfs.xfs -f -q -m crc=0 -n ftype=0");
2164} else if (strcmp(format, "jfs") == 0) {
2165 mr_asprintf(program, "mkfs.jfs");
2166} else if (strcmp(format, "ext3") == 0) {
2167 mr_asprintf(program, "mkfs -t ext3 -F -q");
2168} else if (strcmp(format, "ext4") == 0) {
2169 mr_asprintf(program, "mkfs -t ext4 -F -q");
2170} else if (strcmp(format, "btrfs") == 0) {
2171 mr_asprintf(program, "mkfs.btrfs -f");
2172} else if (strcmp(format, "minix") == 0) {
2173 mr_asprintf(program, "mkfs.minix");
2174} else if (strcmp(format, "vmfs") == 0) {
2175 mr_asprintf(program, "mkfs -t vmfs");
2176} else if (strcmp(format, "ntfs") == 0) {
2177 /*
2178 * mkfs.ntfs treats the '-c' switch as 'specify cluster size'
2179 * so the default "mkfs -t %s -c" command structure fails
2180 */
2181 mr_asprintf(program, "mkfs -t ntfs");
2182} else if (strcmp(format, "ocfs2") == 0) {
2183 /*
2184 * For existing OCFS2 volumes, mkfs.ocfs2 ensures the volume is not mounted on any node in the cluster before formatting. For that to work, mkfs.ocfs2 expects the O2CB cluster service to be running. Specify this option to disable this check.
2185 *
2186 */
2187 mr_asprintf(program, "mkfs -t ocfs2 -F");
2188#endif
2189} else if (strcmp(format, "ext2") == 0) {
2190 mr_asprintf(program, "mke2fs -F -q");
2191} else {
2192#ifdef __FreeBSD__
2193 mr_asprintf(program, "newfs_%s", format);
2194#else
2195 mr_asprintf(program, "mkfs -t %s -c", format); // -c checks for bad blocks
2196#endif
2197 log_it("Unknown format (%s) - assuming '%s' will do", format, program);
2198}
2199return(program);
2200}
2201
2202
2203/**
2204 * Resize a drive's entries in @p mountlist proportionately to fit its new size.
2205 * There are a few problems with this function:
2206 * - It won't work if there was any unallocated space on the user's hard drive
2207 * when it was backed up.
2208 * - It won't work if the user's hard drive lies about its size (more common
2209 * than you'd think).
2210 *
2211 * @param mountlist The mountlist to use for resizing @p drive_name.
2212 * @param drive_name The drive to resize.
2213 */
2214void resize_drive_proportionately_to_suit_new_drives(struct mountlist_itself *mountlist, char *drive_name) {
2215
2216/**buffers **********************************************************/
2217char *tmp = NULL;
2218
2219/** int *************************************************************/
2220int partno, lastpart;
2221
2222/** float ***********************************************************/
2223float factor;
2224long long new_size;
2225
2226/** long *************************************************************/
2227long long newsizL = 0LL;
2228long long totalsizL = 0LL;
2229long long current_size_of_drive = 0LL; /* use KB interally for precision */
2230long long original_size_of_drive = 0LL; /* use KB interally for precision */
2231struct mountlist_reference *drivemntlist;
2232
2233/** structures *******************************************************/
2234
2235/** end **************************************************************/
2236
2237assert(mountlist != NULL);
2238assert_string_is_neither_NULL_nor_zerolength(drive_name);
2239
2240if (strlen(drive_name) >= strlen(RAID_DEVICE_STUB)) {
2241 if (strncmp(drive_name, RAID_DEVICE_STUB, strlen(RAID_DEVICE_STUB))
2242 == 0) {
2243 return;
2244 }
2245}
2246
2247current_size_of_drive = (long long) get_phys_size_of_drive(drive_name) * 1024LL;
2248
2249if (current_size_of_drive <= 0LL) {
2250 log_it("Not resizing to match %s - can't find drive", drive_name);
2251 return;
2252}
2253log_to_screen("Expanding entries to suit drive %s (%lld MB)", drive_name, current_size_of_drive / 1024);
2254
2255drivemntlist = malloc(sizeof(struct mountlist_reference));
2256drivemntlist->el = malloc(sizeof(struct mountlist_line *) * MAX_MOUNTLIST_ENTRIES);
2257
2258if (!drivemntlist) {
2259 fatal_error("Cannot malloc temporary mountlist\n");
2260}
2261create_mountlist_for_drive(mountlist, drive_name, drivemntlist);
2262
2263for (partno = 0; partno < drivemntlist->entries; partno++) {
2264 if (drivemntlist->el[partno]->size > 0LL) {
2265 /* Keep KB here */
2266 original_size_of_drive += drivemntlist->el[partno]->size;
2267 }
2268}
2269
2270if (original_size_of_drive <= 0LL) {
2271 log_to_screen("Cannot resize %s's entries. Drive not found.", drive_name);
2272 return;
2273}
2274factor = ((float)current_size_of_drive/(float)original_size_of_drive);
2275log_to_screen("Disk %s was %lld MB; is now %lld MB; Proportionally resizing partitions (factor ~= %.5f)", drive_name, original_size_of_drive/1024, current_size_of_drive/1024, factor);
2276
2277lastpart = drivemntlist->entries - 1;
2278for (partno = 0; partno < drivemntlist->entries; partno++) {
2279 /* the 'atoi' thing is to make sure we don't try to resize _images_, whose formats will be numeric */
2280 if (!atoi(drivemntlist->el[partno]->format)) {
2281 new_size = (long long)((drivemntlist->el[partno]->size) * factor);
2282 } else {
2283 new_size = drivemntlist->el[partno]->size;
2284 }
2285
2286 if (!strcmp(drivemntlist->el[partno]->mountpoint, "image")) {
2287 log_msg(1, "Skipping %s (%s) because it's an image",
2288 drivemntlist->el[partno]->device,
2289 drivemntlist->el[partno]->mountpoint);
2290 }
2291 newsizL = new_size;
2292
2293 /* Do not apply the factor if partition was of negative size */
2294 if (newsizL < 0LL) {
2295 newsizL = drivemntlist->el[partno]->size;
2296 }
2297 totalsizL += newsizL;
2298
2299 log_to_screen("Changing %s from %lld KB to %lld KB", drivemntlist->el[partno]->device, drivemntlist->el[partno]->size, newsizL);
2300 drivemntlist->el[partno]->size = newsizL;
2301}
2302// Ensures over-allocation alert and prompt for interactive mode does not happen
2303if (totalsizL > current_size_of_drive) {
2304 mr_asprintf(tmp, "Last partition size calculated would be over-allocated, reducing %s from %lld KB to %lld KB.", drivemntlist->el[lastpart]->device, drivemntlist->el[lastpart]->size, drivemntlist->el[lastpart]->size - (totalsizL - current_size_of_drive));
2305 drivemntlist->el[drivemntlist->entries-1]->size -= (totalsizL - current_size_of_drive);
2306} else if (totalsizL < current_size_of_drive) {
2307 mr_asprintf(tmp, "Last partition size calculated would be under-allocated, increasing %s from %lld KB to %lld KB.",drivemntlist->el[lastpart]->device, drivemntlist->el[lastpart]->size, drivemntlist->el[lastpart]->size + (current_size_of_drive - totalsizL));
2308 drivemntlist->el[drivemntlist->entries-1]->size += (current_size_of_drive - totalsizL);
2309}
2310log_to_screen(tmp);
2311mr_free(tmp);
2312log_to_screen("final_size = %lld MB", current_size_of_drive / 1024);
2313}
2314
2315
2316/**
2317 * Resize all partitions in @p mountlist proportionately (each one
2318 * grows or shrinks by the same percentage) to fit them into the new
2319 * drives (presumably different from the old ones).
2320 * @param mountlist The mountlist to resize the drives in.
2321 */
2322void resize_mountlist_proportionately_to_suit_new_drives(struct mountlist_itself *mountlist) {
2323
2324/** buffers *********************************************************/
2325struct list_of_disks *drivelist;
2326
2327/** int *************************************************************/
2328int driveno;
2329
2330/** end *************************************************************/
2331
2332drivelist = malloc(sizeof(struct list_of_disks));
2333assert(mountlist != NULL);
2334
2335if (g_mountlist_fname[0] == '\0') {
2336 log_it("resize_mountlist_prop...() - warning - mountlist fname is blank");
2337 log_it("That does NOT affect the functioning of this subroutine.");
2338 log_it("--- Hugo, 2002/11/20");
2339}
2340log_it("Resizing mountlist");
2341make_list_of_drives_in_mountlist(mountlist, drivelist);
2342log_it("Back from MLoDiM");
2343for (driveno = 0; driveno < drivelist->entries; driveno++) {
2344 resize_drive_proportionately_to_suit_new_drives(mountlist, drivelist->el[driveno].device);
2345}
2346log_to_screen("Mountlist adjusted to suit current hard drive(s)");
2347paranoid_free(drivelist);
2348}
2349
2350/**
2351 * Create a mountlist_reference structure for @p drive_name in @p mountlist.
2352 * @param mountlist The complete mountlist to get the drive references from.
2353 * @param drive_name The drive to put in @p drivemntlist.
2354 * @param drivemntlist The mountlist_reference structure to put the drive's entries in.
2355 * @note @p drivemntlist and @p drivemntlist->el must be allocated by the caller.
2356 * @author Ralph Grewe
2357 */
2358void create_mountlist_for_drive(struct mountlist_itself *mountlist, char *drive_name, struct mountlist_reference *drivemntlist) {
2359
2360int partno;
2361char *tmp_drive_name, *c;
2362
2363assert(mountlist != NULL);
2364assert(drive_name != NULL);
2365assert(drivemntlist != NULL);
2366
2367log_msg(1, "Creating list of partitions for drive %s", drive_name);
2368
2369tmp_drive_name = strdup(drive_name);
2370if (!tmp_drive_name)
2371 fatal_error("Out of memory");
2372
2373/* devfs devices? */
2374c = strrchr(tmp_drive_name, '/');
2375if (c && strncmp(c, "/disc", 5) == 0) {
2376 /* yup its devfs, change the "disc" to "part" so the existing code works */
2377 strcpy(c + 1, "part");
2378}
2379drivemntlist->entries = 0;
2380for (partno = 0; partno < mountlist->entries; partno++) {
2381 if (strncmp(mountlist->el[partno].device, tmp_drive_name, strlen(tmp_drive_name)) == 0) {
2382 drivemntlist->el[drivemntlist->entries] = &mountlist->el[partno];
2383 drivemntlist->entries++;
2384 }
2385}
2386if (tmp_drive_name)
2387 free(tmp_drive_name);
2388}
2389
2390/* @} - end of prepGroup */
Note: See TracBrowser for help on using the repository browser.