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

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