source: MondoRescue/branches/3.2/mondo/src/mondorestore/mondo-prep.c@ 3568

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