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

Last change on this file since 3148 was 3148, checked in by Bruno Cornec, 7 years ago

2nd phase for svn merge -r 2935:3146 ../3.0

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