source: branches/3.1/mondo/src/mondorestore/mondo-prep.c @ 3193

Last change on this file since 3193 was 3193, checked in by Bruno Cornec, 7 years ago
  • Finish with backports from 3.1 for now. Still some work to do, but we will now make that version compile and work again and serve as a base

so the gettext patch can be added

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