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

Last change on this file since 3147 was 3147, checked in by Bruno Cornec, 7 years ago
  • First pass on svn merge -r 2935:3146 ../3.0
  • Property svn:keywords set to Id
File size: 72.6 KB
Line 
1/***************************************************************************
2$Id: mondo-prep.c 3147 2013-06-19 06:34:46Z 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 3147 2013-06-19 06:34:46Z 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
1424    /** end *************************************************************/
1425
1426    assert(mountlist != NULL);
1427    assert_string_is_neither_NULL_nor_zerolength(drivename);
1428
1429    log_it("Partitioning drive %s", drivename);
1430
1431#if __FreeBSD__
1432    log_it("(Not opening fdisk now; that's the Linux guy's job)");
1433    pout_to_fdisk = NULL;
1434#else
1435    make_hole_for_file(FDISK_LOG);
1436    mr_asprintf(tmp, "parted2fdisk %s >> %s 2>> %s", drivename, FDISK_LOG, FDISK_LOG);
1437    pout_to_fdisk = popen(tmp, "w");
1438    mr_free(tmp);
1439    if (!pout_to_fdisk) {
1440        log_to_screen("Cannot call parted2fdisk to configure %s", drivename);
1441        return (1);
1442    }
1443#endif
1444    for (current_devno = 1; current_devno < 99; current_devno++) {
1445        device_str = build_partition_name(drivename, current_devno);
1446        lino = find_device_in_mountlist(mountlist, device_str);
1447
1448        if (lino < 0) {
1449            // device not found in mountlist
1450#if __FreeBSD__
1451            // If this is the first partition (just as a sentinel value),
1452            // then see if the user has picked 'dangerously-dedicated' mode.
1453            // If so, then we just call label_drive_or_slice() and return.
1454            char c;
1455            if (current_devno == 1) {
1456                // try DangerouslyDedicated mode
1457                for (c = 'a'; c <= 'z'; c++) {
1458                    mr_asprintf(subdev_str, "%s%c", drivename, c);
1459                    if (find_device_in_mountlist(mountlist, subdev_str) > 0) {
1460                        fbsd_part = TRUE;
1461                    }
1462                    mr_free(subdev_str);
1463                }
1464                if (fbsd_part) {
1465                    r = label_drive_or_slice(mountlist,
1466                                                 drivename,
1467                                                 0);
1468                    mr_asprintf(command, "disklabel -B %s", basename(drivename));
1469                    if (system(command)) {
1470                        log_to_screen("Warning! Unable to make the drive bootable.");
1471                    }
1472                    mr_free(command);
1473                    mr_free(device_str);
1474                    return r;
1475                }
1476            }
1477            for (c = 'a'; c <= 'z'; c++) {
1478                mr_asprintf(subdev_str, "%s%c", device_str, c);
1479                if (find_device_in_mountlist(mountlist, subdev_str) > 0) {
1480                    fbsd_part = TRUE;
1481                }
1482                    mr_free(subdev_str);
1483            }
1484            // Now we check the subpartitions of the current partition.
1485            if (fbsd_part) {
1486                int i, line;
1487
1488                mr_asprintf(format, "ufs");
1489                partsize = 0;
1490                for (i = 'a'; i < 'z'; ++i) {
1491                    mr_asprintf(subdev_str, "%s%c", device_str, i);
1492                    line = find_device_in_mountlist(mountlist, subdev_str);
1493                    mr_free(subdev_str);
1494
1495                    if (line > 0) {
1496                        // We found one! Add its size to the total size.
1497                        partsize += mountlist->el[line].size;
1498                    }
1499                }
1500            } else {
1501                mr_free(device_str);
1502                continue;
1503            }
1504#else
1505            mr_free(device_str);
1506            continue;
1507#endif
1508        }
1509
1510        /* OK, we've found partition /dev/hdxN in mountlist; let's prep it */
1511        /* For FreeBSD, that is     /dev/adXsY */
1512
1513        log_it("Found partition %s in mountlist", device_str);
1514        if (!previous_devno) {
1515
1516            log_it("Wiping %s's partition table", drivename);
1517#if __FreeBSD__
1518            // FreeBSD doesn't let you write to blk devices in <512byte chunks.
1519            file = open(drivename, O_WRONLY);
1520            if (file != -1) {
1521                log_to_screen("Warning - unable to open %s for wiping it's partition table", drivename);
1522            }
1523
1524            for (i = 0; i < 512; i++) {
1525                if (!write(file, "\0", 1)) {
1526                    log_to_screen("Warning - unable to write to %s", drivename);
1527                }
1528            }
1529            sync();
1530#else
1531            log_it("New, kernel-friendly partition remover");
1532            for (i = 20; i > 0; i--) {
1533                fprintf(pout_to_fdisk, "d\n%d\n", i);
1534                fflush(pout_to_fdisk);
1535            }
1536#endif
1537            if (current_devno > 1) {
1538                previous_devno =
1539                    make_dummy_partitions(pout_to_fdisk, drivename,
1540                                            current_devno);
1541            }
1542        }
1543#ifdef __FreeBSD__
1544        if (!fbsd_part) {
1545            mr_free(format);
1546#endif
1547
1548            mr_asprintf(format, "%s", mountlist->el[lino].format);
1549            partsize = mountlist->el[lino].size;
1550
1551#ifdef __FreeBSD__
1552        }
1553#endif
1554
1555#ifndef __IA64__
1556        if (current_devno == 5 && previous_devno == 4) {
1557            log_to_screen("You must leave at least one partition spare as the Extended partition.");
1558            mr_free(device_str);
1559            mr_free(format);
1560            return (1);
1561        }
1562#endif
1563
1564        retval += partition_device(pout_to_fdisk, drivename, current_devno, previous_devno, format, partsize);
1565        mr_free(format);
1566
1567#ifdef __FreeBSD__
1568        if ((current_devno <= 4) && fbsd_part) {
1569            mr_asprintf(tmp, "disklabel -B %s", basename(device_str));
1570            retval += label_drive_or_slice(mountlist, device_str, 0);
1571            if (system(tmp)) {
1572                log_to_screen("Warning! Unable to make the slice bootable.");
1573            }
1574            mr_free(tmp);
1575        }
1576#endif
1577        mr_free(device_str);
1578
1579        previous_devno = current_devno;
1580    }
1581
1582    if (pout_to_fdisk) {
1583        // mark relevant partition as bootable
1584        tmp1 = call_program_and_get_last_line_of_output ("make-me-bootable "MINDI_CACHE"/mountlist.txt dummy",TRUE);
1585        mr_asprintf(tmp, "a\n%s\n", tmp1);
1586        mr_free(tmp1);
1587
1588        fput_string_one_char_at_a_time(pout_to_fdisk, tmp);
1589        mr_free(tmp);
1590
1591        // close fdisk
1592        fput_string_one_char_at_a_time(pout_to_fdisk, "w\n");
1593        sync();
1594        paranoid_pclose(pout_to_fdisk);
1595        paranoid_system("sync");
1596        log_msg(0,"------------------- fdisk.log looks like this ------------------");
1597        mr_asprintf(tmp, "cat %s >> %s", FDISK_LOG, MONDO_LOGFILE);
1598        system(tmp);
1599        mr_free(tmp);
1600
1601        log_msg(0,
1602                "------------------- end of fdisk.log... word! ------------------");
1603        mr_asprintf(tmp, "tail -n6 %s | grep -F \"16: \"", FDISK_LOG);
1604        if (!run_program_and_log_output(tmp, 5)) {
1605            g_partition_table_locked_up++;
1606        }
1607        mr_free(tmp);
1608
1609        mr_asprintf(tmp, "partprobe %s", drivename);
1610        if (!run_program_and_log_output(tmp, 5)) {
1611            g_partition_table_locked_up--;
1612        }
1613        mr_free(tmp);
1614
1615        if (g_partition_table_locked_up > 0) {
1616            log_to_screen("A flaw in the Linux kernel has locked the partition table. Even calling partprobe did not suceed :-(");
1617        }
1618    }
1619    return (retval);
1620}
1621
1622/**
1623 * Create partition number @p partno on @p drive with @p fdisk.
1624 * @param drive The drive to create the partition on.
1625//  * @param partno The partition number of the new partition (1-4 are primary, >=5 is logical).
1626 * @param prev_partno The partition number of the most recently prepped partition.
1627 * @param format The filesystem type of this partition (used to set the type).
1628 * @param partsize The size of the partition in @e bytes.
1629 * @return 0 for success, nonzero for failure.
1630 */
1631int partition_device(FILE * pout_to_fdisk, const char *drive, int partno,
1632                     int prev_partno, const char *format,
1633                     long long partsize)
1634{
1635    /** int **************************************************************/
1636    int retval = 0;
1637    int res = 0;
1638
1639    /** buffers **********************************************************/
1640    char *program = NULL;
1641    char *partition_name = NULL;
1642    char *tmp = NULL;
1643    char *output = NULL;
1644
1645    /** pointers **********************************************************/
1646    char *p;
1647    char *part_table_fmt = NULL;
1648    FILE *fout;
1649
1650    /** end ***************************************************************/
1651
1652    assert_string_is_neither_NULL_nor_zerolength(drive);
1653    assert(format != NULL);
1654
1655    log_it("partition_device('%s', %d, %d, '%s', %lld) --- starting",
1656             drive, partno, prev_partno, format, partsize);
1657
1658    if (!strncmp(drive, RAID_DEVICE_STUB, strlen(RAID_DEVICE_STUB))) {
1659        log_it("Not partitioning %s - it is a virtual drive", drive);
1660        return (0);
1661    }
1662    partition_name = build_partition_name(drive, partno);
1663    if (partsize <= 0) {
1664        mr_asprintf(tmp, "Partitioning device %s (max size)", partition_name);
1665    } else {
1666        mr_asprintf(tmp, "Partitioning device %s (%lld MB)", partition_name, (long long) partsize / 1024);
1667    }
1668    update_progress_form(tmp);
1669    log_it(tmp);
1670    mr_free(tmp);
1671
1672    if (is_this_device_mounted(partition_name)) {
1673        log_to_screen("%s is mounted, and should not be partitioned", partition_name);
1674        mr_free(partition_name);
1675        return (1);
1676    }
1677
1678
1679    p = (char *) strrchr(partition_name, '/');
1680
1681    /* BERLIOS: should not be called each time */
1682    part_table_fmt = which_partition_format(drive);
1683    mr_asprintf(output, "");
1684    /* make it a primary/extended/logical */
1685    if (partno <= 4) {
1686        mr_strcat(output, "n\np\n%d\n", partno);
1687    } else {
1688        /* MBR needs an extended partition if more than 4 partitions */
1689        if (strcmp(part_table_fmt, "MBR") == 0) {
1690            if (partno == 5) {
1691                if (prev_partno >= 4) {
1692                    log_to_screen("You need to leave at least one partition free, for 'extended/logical'");
1693                    mr_free(partition_name);
1694                    mr_free(output);
1695                    mr_free(part_table_fmt);
1696                    return (1);
1697                } else {
1698                    mr_strcat(output, "n\ne\n%d\n\n\n", prev_partno + 1);
1699                }
1700            }
1701            mr_strcat(output, "n\nl\n");
1702        } else {
1703            /* GPT allows more than 4 primary partitions */
1704            mr_strcat(output, "n\np\n%d\n", partno);
1705        }
1706    }
1707    mr_free(part_table_fmt);
1708
1709    mr_strcat(output, "\n");    /*start block (ENTER for next free blk */
1710    if (partsize > 0) {
1711        if (!strcmp(format, "7")) {
1712            log_msg(1, "Adding 512K, just in case");
1713            partsize += 512;
1714        }
1715        mr_strcat(output, "+%lldK", (long long) (partsize));
1716    }
1717    mr_strcat(output, "\n");
1718    log_it("PARTSIZE = +%ld",(long)partsize);
1719
1720    log_it("---fdisk command---");
1721    log_it(output);
1722    log_it("---end of fdisk---");
1723
1724    mr_asprintf(program, "parted2fdisk %s >> %s 2>> %s", drive, MONDO_LOGFILE, MONDO_LOGFILE);
1725    if (pout_to_fdisk) {
1726        log_msg(1, "Doing the new all-in-one fdisk thing");
1727        log_msg(1, "output = '%s'", output);
1728        fput_string_one_char_at_a_time(pout_to_fdisk, output);
1729        fput_string_one_char_at_a_time(pout_to_fdisk, "\n\np\n");
1730        tmp = last_line_of_file(FDISK_LOG);
1731        if (strstr(tmp, " (m ")) {
1732            log_msg(1, "Successfully created partition %d on %s", partno, drive);
1733        } else {
1734            log_msg(1, "last line = %s", tmp);
1735            log_msg(1, "Failed to create partition %d on %s; sending 'Enter'...", partno, drive);
1736        }
1737        mr_free(tmp);
1738
1739        if (!retval) {
1740            log_msg(1, "Trying to set partition %d type now on %s", partno, drive);
1741            retval =
1742                set_partition_type(pout_to_fdisk, drive, partno, format,
1743                                     partsize);
1744            if (retval) {
1745                log_msg(1, "Failed. Trying again...");
1746                retval =
1747                    set_partition_type(pout_to_fdisk, drive, partno,
1748                                         format, partsize);
1749            }
1750        }
1751        if (retval) {
1752            log_msg(1, "...but failed to set type");
1753        }
1754    } else {
1755        mr_strcat(output, "w\n\n");
1756        if (g_fprep) {
1757            fprintf(g_fprep, "echo \"%s\" | %s\n", output, program);
1758        }
1759        /* write to disk; close fdisk's stream */
1760        if (!(fout = popen(program, "w"))) {
1761            log_OS_error("can't popen-out to program");
1762        } else {
1763            fputs(output, fout);
1764            paranoid_pclose(fout);
1765        }
1766        if (!does_partition_exist(drive, partno) && partsize > 0) {
1767            log_it("Vaccum-packing");
1768            g_current_progress--;
1769            res =
1770                partition_device(pout_to_fdisk, drive, partno, prev_partno,
1771                                 format, -1);
1772            if (res) {
1773                log_it("Failed to vacuum-pack %s", partition_name);
1774
1775                retval++;
1776            } else {
1777                retval = 0;
1778            }
1779        }
1780        if (does_partition_exist(drive, partno)) {
1781            retval =
1782                set_partition_type(pout_to_fdisk, drive, partno, format,
1783                                     partsize);
1784            if (retval) {
1785                log_it("Partitioned %s but failed to set its type", partition_name);
1786            } else {
1787                if (partsize > 0) {
1788                    log_to_screen("Partition %s created+configured OK", partition_name);
1789                } else {
1790                    log_it("Returning from a successful vacuum-pack");
1791                }
1792            }
1793        } else {
1794            mr_asprintf(tmp, "Failed to partition %s", partition_name);
1795            if (partsize > 0) {
1796                log_to_screen(tmp);
1797            } else {
1798                log_it(tmp);
1799            }
1800            mr_free(tmp);
1801            retval++;
1802        }
1803    }
1804    mr_free(program);
1805    mr_free(partition_name);
1806    paranoid_free(output);
1807
1808    g_current_progress++;
1809    log_it("partition_device() --- leaving");
1810    return (retval);
1811}
1812
1813
1814
1815/**
1816 * Create all partitions listed in @p mountlist.
1817 * @param mountlist The mountlist to use to guide the partitioning.
1818 * @return The number of errors encountered (0 for success).
1819 * @note This sets the partition types but doesn't actually do the formatting.
1820 * Use format_everything() for that.
1821 */
1822int partition_everything(struct mountlist_itself *mountlist)
1823{
1824    /** int ************************************************************/
1825    int lino;
1826    int retval = 0;
1827    int i;
1828    int res;
1829
1830    /** buffer *********************************************************/
1831    struct list_of_disks *drivelist;
1832    /*  struct mountlist_itself new_mtlist, *mountlist; */
1833
1834    /** end ************************************************************/
1835
1836    drivelist = malloc(sizeof(struct list_of_disks));
1837    assert(mountlist != NULL);
1838
1839    log_it("partition_everything() --- starting");
1840    mvaddstr_and_log_it(g_currentY, 0, "Partitioning hard drives        ");
1841    /*  mountlist=orig_mtlist; */
1842    if (mountlist_contains_raid_devices(mountlist)) {
1843        /*      mountlist=&new_mtlist; */
1844        /*      extrapolate_mountlist_to_include_raid_partitions(mountlist,orig_mtlist); */
1845        log_msg(0,
1846                "Mountlist, including the partitions incorporated in RAID devices:-");
1847        for (i = 0; i < mountlist->entries; i++) {
1848            log_it(mountlist->el[i].device);
1849        }
1850        log_msg(0, "End of mountlist.");
1851    }
1852    log_msg(0, "Stopping all LVMs, just in case");
1853    if (!g_text_mode) {
1854        newtSuspend();
1855    }
1856    do_my_funky_lvm_stuff(TRUE, FALSE); // just remove old partitions
1857    if (!g_text_mode) {
1858        newtResume();
1859    }
1860    log_msg(0, "Stopping all software RAID devices, just in case");
1861    stop_all_raid_devices(mountlist);
1862    log_msg(0, "Done.");
1863
1864    open_progress_form("Partitioning devices",
1865                         "I am now going to partition all your drives.",
1866                         "This should not take more than five minutes.", "",
1867                         mountlist->entries);
1868
1869    make_list_of_drives_in_mountlist(mountlist, drivelist);
1870
1871    /* partition each drive */
1872    for (lino = 0; lino < drivelist->entries; lino++) {
1873        res = partition_drive(mountlist, drivelist->el[lino].device);
1874        retval += res;
1875    }
1876    close_progress_form();
1877    if (retval) {
1878        mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1879        log_to_screen("Errors occurred during the partitioning of your hard drives.");
1880    } else {
1881        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1882        paranoid_system("rm -f /tmp/fdisk*.log 2> /dev/null");
1883    }
1884    newtSuspend();
1885    paranoid_system("clear");
1886    newtResume();
1887    paranoid_free(drivelist);
1888    return (retval);
1889}
1890
1891
1892
1893
1894
1895
1896/**
1897 * Set the type of partition number @p partno on @p drive to @p format.
1898 * @param drive The drive to change the type of a partition on.
1899 * @param partno The partition number on @p drive to change the type of.
1900 * @param format The filesystem type this partition will eventually contain.
1901 * @param partsize The size of this partition, in @e bytes (used for vfat
1902 * type calculations).
1903 * @return 0 for success, nonzero for failure.
1904 */
1905int set_partition_type(FILE * pout_to_fdisk, const char *drive, int partno,
1906                         const char *format, long long partsize)
1907{
1908    /** buffers *********************************************************/
1909    char *partition = NULL;
1910    char *command = NULL;
1911    char *output = NULL;
1912    char *tmp = NULL;
1913    char *tmp1 = NULL;
1914    char *partcode = NULL;
1915
1916    /** pointers *********************************************************/
1917    char *p;
1918    FILE *fout;
1919
1920    /** int **************************************************************/
1921    int res = 0;
1922
1923    /** end **************************************************************/
1924
1925    assert_string_is_neither_NULL_nor_zerolength(drive);
1926    assert(format != NULL);
1927
1928    partition = build_partition_name(drive, partno);
1929    p = (char *) strrchr(partition, '/');
1930    if (strcmp(format, "swap") == 0) {
1931        mr_asprintf(partcode, "82");
1932    } else if (strcmp(format, "vfat") == 0) {
1933        if (partsize / 1024 > 8192) {
1934            mr_asprintf(partcode, "c");
1935        } else {
1936            mr_asprintf(partcode, "b");
1937        }
1938    } else if (strcmp(format, "ext2") == 0
1939                 || strcmp(format, "reiserfs") == 0
1940                 || strcmp(format, "ext3") == 0 
1941                 || strcmp(format, "ext4") == 0 
1942                 || strcmp(format, "xfs") == 0
1943                 || strcmp(format, "jfs") == 0
1944                     || strcmp(format, "btrfs") == 0) {
1945        mr_asprintf(partcode, "83");
1946    } else if (strcmp(format, "minix") == 0) {
1947        mr_asprintf(partcode, "81");
1948    } else if (strcmp(format, "vmfs3") == 0) {
1949        mr_asprintf(partcode, "fb");
1950    } else if (strcmp(format, "vmkcore") == 0) {
1951        mr_asprintf(partcode, "fc");
1952    } else if (strcmp(format, "raid") == 0) {
1953        mr_asprintf(partcode, "fd");
1954    } else if (strcmp(format, "ntfs") == 0) {
1955        mr_asprintf(partcode, "7");
1956    } else if ((strcmp(format, "ufs") == 0)
1957                 || (strcmp(format, "ffs") == 0)) { /* raid autodetect */
1958        mr_asprintf(partcode, "a5");
1959    } else if (strcmp(format, "lvm") == 0) {
1960        mr_asprintf(partcode, "8e");
1961    } else if (format[0] == '\0') { /* LVM physical partition */
1962        mr_asprintf(partcode, "");
1963    } else if (strlen(format) >= 1 && strlen(format) <= 2) {
1964        mr_asprintf(partcode, "%s", format);
1965    } else {
1966        /* probably an image */
1967        mr_asprintf(tmp, "Unknown format ('%s') - using supplied string anyway", format);
1968        mvaddstr_and_log_it(g_currentY++, 0, tmp);
1969        mr_free(tmp);
1970#ifdef __FreeBSD__
1971        mr_asprintf(partcode, "%s", format);    // was a5
1972#else
1973        mr_asprintf(partcode, "%s", format);    // was 83
1974#endif
1975    }
1976    log_msg(1, tmp, "Setting %s's type to %s (%s)", partition, format, partcode);
1977    mr_free(partition);
1978
1979    if (partcode != NULL && strcmp(partcode, "83")) {   /* no need to set type if 83: 83 is default */
1980
1981        if (pout_to_fdisk) {
1982            res = 0;
1983            fput_string_one_char_at_a_time(pout_to_fdisk, "t\n");
1984            tmp1 = last_line_of_file(FDISK_LOG);
1985            if (partno > 1 || strstr(tmp1, " (1-4)")) {
1986                log_msg(5, "Specifying partno (%d) - yay", partno);
1987                mr_asprintf(tmp, "%d\n", partno);
1988                fput_string_one_char_at_a_time(pout_to_fdisk, tmp);
1989                mr_free(tmp);
1990
1991                mr_free(tmp1);
1992                tmp1 = last_line_of_file(FDISK_LOG);
1993                log_msg(5, "A - last line = '%s'", tmp1);
1994            }
1995            mr_free(tmp1);
1996
1997            mr_asprintf(tmp, "%s\n", partcode);
1998            fput_string_one_char_at_a_time(pout_to_fdisk, tmp);
1999            mr_free(tmp);
2000
2001            tmp1 = last_line_of_file(FDISK_LOG);
2002            log_msg(5, "B - last line = '%s'", tmp1);
2003            mr_free(tmp1);
2004
2005            fput_string_one_char_at_a_time(pout_to_fdisk, "\n");
2006            tmp1 = last_line_of_file(FDISK_LOG);
2007            log_msg(5, "C - last line = '%s'", tmp1);
2008            mr_free(tmp1);
2009
2010            tmp1 = last_line_of_file(FDISK_LOG);
2011            if (!strstr(tmp1, " (m ")) {
2012                log_msg(1, "last line = '%s'; part type set failed", tmp1);
2013                res++;
2014                fput_string_one_char_at_a_time(pout_to_fdisk, "\n");
2015            }
2016            mr_free(tmp1);
2017
2018            fput_string_one_char_at_a_time(pout_to_fdisk, "p\n");
2019        } else {
2020            mr_asprintf(output, "t\n%d\n%s\nw\n", partno, partcode);
2021            mr_asprintf(command, "parted2fdisk %s >> %s 2>> %s", drive, MONDO_LOGFILE, MONDO_LOGFILE);
2022            log_msg(5, "output = '%s'", output);
2023            log_msg(5, "partno=%d; partcode=%s", partno, partcode);
2024            log_msg(5, "command = '%s'", command);
2025            fout = popen(command, "w");
2026            if (!fout) {
2027                log_OS_error(command);
2028                res = 1;
2029            } else {
2030                res = 0;
2031                fprintf(fout, "%s", output);
2032                paranoid_pclose(fout);
2033            }
2034            paranoid_free(output);
2035            mr_free(command);
2036        }
2037    }
2038
2039    mr_free(partcode);
2040    return (res);
2041}
2042
2043
2044int start_raid_device(char *raid_device)
2045{
2046    /** int *************************************************************/
2047    int res;
2048    int retval = 0;
2049
2050    /** buffers *********************************************************/
2051    char *program = NULL;
2052
2053    /** end *************************************************************/
2054
2055    assert_string_is_neither_NULL_nor_zerolength(raid_device);
2056
2057#ifdef __FreeBSD__
2058    if (is_this_device_mounted(raid_device)) {
2059        log_it("Can't start %s when it's mounted!", raid_device);
2060        return 1;
2061    }
2062    mr_asprintf(program, "vinum start -f %s", raid_device);
2063#else
2064    mr_asprintf(program, "raidstart %s", raid_device);
2065#endif
2066    log_msg(1, "program = %s", program);
2067    res = run_program_and_log_output(program, 1);
2068    if (g_fprep) {
2069        fprintf(g_fprep, "%s\n", program);
2070    }
2071    mr_free(program);
2072
2073    if (res) {
2074        log_msg(1, "Warning - failed to start RAID device %s",
2075                raid_device);
2076    }
2077    retval += res;
2078    sleep(1);
2079    return (retval);
2080}
2081
2082
2083
2084/**
2085 * Stop @p raid_device using @p raidstop.
2086 * @param raid_device The software RAID device to stop.
2087 * @return 0 for success, nonzero for failure.
2088 */
2089int stop_raid_device(char *raid_device)
2090{
2091    /** int *************************************************************/
2092    int res;
2093    int retval = 0;
2094
2095    /** buffers *********************************************************/
2096    char *program = NULL;
2097
2098    /** end *************************************************************/
2099
2100    assert_string_is_neither_NULL_nor_zerolength(raid_device);
2101
2102#ifdef __FreeBSD__
2103    if (is_this_device_mounted(raid_device)) {
2104        log_it("Can't stop %s when it's mounted!", raid_device);
2105        return 1;
2106    }
2107    mr_asprintf(program, "vinum stop -f %s", raid_device);
2108#else
2109        // use raidstop if it exists, otherwise use mdadm
2110        if (run_program_and_log_output("which raidstop", FALSE)) {
2111        mr_asprintf(program, "mdadm -S %s", raid_device);
2112    } else {
2113        mr_asprintf(program, "raidstop %s", raid_device);
2114    }
2115#endif
2116    log_msg(1, "program = %s", program);
2117    res = run_program_and_log_output(program, 1);
2118    if (g_fprep) {
2119        fprintf(g_fprep, "%s\n", program);
2120    }
2121    mr_free(program);
2122
2123    if (res) {
2124        log_msg(1, "Warning - failed to stop RAID device %s", raid_device);
2125    }
2126    retval += res;
2127    return (retval);
2128}
2129
2130
2131int start_all_raid_devices(struct mountlist_itself *mountlist)
2132{
2133    int i;
2134    int retval = 0;
2135    int res;
2136
2137    for (i = 0; i < mountlist->entries; i++) {
2138        if (!strncmp
2139            (mountlist->el[i].device, RAID_DEVICE_STUB,
2140             strlen(RAID_DEVICE_STUB))) {
2141            log_msg(1, "Starting %s", mountlist->el[i].device);
2142            res = start_raid_device(mountlist->el[i].device);
2143            retval += res;
2144        }
2145    }
2146    if (retval) {
2147        log_msg(1, "Started all s/w raid devices OK");
2148    } else {
2149        log_msg(1, "Failed to start some/all s/w raid devices");
2150    }
2151    return (retval);
2152}
2153
2154/**
2155 * Stop all software RAID devices listed in @p mountlist.
2156 * @param mountlist The mountlist to stop the RAID devices in.
2157 * @return The number of errors encountered (0 for success).
2158 * @bug @p mountlist is not used.
2159 */
2160int stop_all_raid_devices(struct mountlist_itself *mountlist)
2161{
2162    /** int *************************************************************/
2163    int retval = 0;
2164
2165    /** char ************************************************************/
2166    char *incoming = NULL;
2167#ifndef __FreeBSD__
2168    char *dev;
2169    char *p;
2170    int res;
2171#endif
2172
2173    /** pointers ********************************************************/
2174    FILE *fin;
2175    char *q;
2176    int i;
2177
2178    /** end ****************************************************************/
2179
2180    assert(mountlist != NULL);
2181
2182    for (i = 0; i < 3; i++) {
2183#ifdef __FreeBSD__
2184        fin = popen("vinum list | grep '^[PVS]' | sed 's/S/1/;s/P/2/;s/V/3/' | sort | cut -d' ' -f2", "r");
2185        if (!fin) {
2186            return (1);
2187        }
2188        for (mr_getline(incoming, fin); !feof(fin); mr_getline(incoming, fin)) {
2189            retval += stop_raid_device(incoming);
2190            mr_free(incoming);
2191        }
2192        mr_free(incoming);
2193#else
2194        fin = fopen("/proc/mdstat", "r");
2195        if (!fin) {
2196            log_OS_error("/proc/mdstat");
2197            return (1);
2198        }
2199        for (mr_getline(incoming, fin); !feof(fin); mr_getline(incoming, fin)) {
2200            for (p = incoming; *p != '\0' && (*p != 'm' || *(p + 1) != 'd' || !isdigit(*(p + 2))); p++);
2201            if (*p != '\0') {
2202                mr_asprintf(dev, "/dev/%s", p);
2203                for (p = dev; *p > 32; p++);
2204                *p = '\0';
2205                retval += stop_raid_device(dev);
2206                mr_free(dev);
2207            }
2208            mr_free(incoming);
2209        }
2210        mr_free(incoming);
2211#endif
2212    }
2213    paranoid_fclose(fin);
2214    if (retval) {
2215        log_msg(1, "Warning - unable to stop some RAID devices");
2216    }
2217    sync();
2218    sync();
2219    sync();
2220    sleep(1);
2221    return (retval);
2222}
2223
2224
2225
2226/**
2227 * Decide which command we need to use to format a device of type @p format.
2228 * @param format The filesystem type we are about to format.
2229 * @param program Where to put the binary name for this format.
2230 * @return 0 for success, nonzero for failure.
2231 */
2232char *which_format_command_do_i_need(char *format)
2233{
2234    /** int *************************************************************/
2235    int res = 0;
2236
2237    /** buffers *********************************************************/
2238    char *program = NULL;
2239
2240    /** end ***************************************************************/
2241
2242    assert_string_is_neither_NULL_nor_zerolength(format);
2243
2244    if (strcmp(format, "swap") == 0) {
2245#ifdef __FreeBSD__
2246        mr_asprintf(program, "true");
2247#else
2248        mr_asprintf(program, "mkswap");
2249#endif
2250    } else if (strcmp(format, "vfat") == 0) {
2251        mr_asprintf(program, "format-and-kludge-vfat");
2252#ifndef __FreeBSD__
2253    } else if (strcmp(format, "reiserfs") == 0) {
2254        mr_asprintf(program, "mkreiserfs -ff");
2255    } else if (strcmp(format, "xfs") == 0) {
2256        mr_asprintf(program, "mkfs.xfs -f -q");
2257    } else if (strcmp(format, "jfs") == 0) {
2258        mr_asprintf(program, "mkfs.jfs");
2259    } else if (strcmp(format, "ext3") == 0) {
2260        mr_asprintf(program, "mkfs -t ext3 -F -q");
2261    } else if (strcmp(format, "ext4") == 0) {
2262        mr_asprintf(program, "mkfs -t ext4 -F -q");
2263    } else if (strcmp(format, "btrfs") == 0) {
2264              strcpy(program, "mkfs.btrfs");
2265    } else if (strcmp(format, "minix") == 0) {
2266        mr_asprintf(program, "mkfs.minix");
2267    } else if (strcmp(format, "vmfs") == 0) {
2268        mr_asprintf(program, "mkfs -t vmfs");
2269    } else if (strcmp(format, "ntfs") == 0) { 
2270        /*
2271         * mkfs.ntfs treats the '-c' switch as 'specify cluster size'
2272         * so the default "mkfs -t %s -c" command structure fails
2273         */ 
2274        mr_asprintf(program, "mkfs -t ntfs");
2275    } else if (strcmp(format, "ocfs2") == 0) {
2276        /*
2277         * 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.
2278         *
2279         */
2280        mr_asprintf(program, "mkfs -t ocfs2 -F");
2281#endif
2282    } else if (strcmp(format, "ext2") == 0) {
2283        mr_asprintf(program, "mke2fs -F -q");
2284    } else {
2285#ifdef __FreeBSD__
2286        mr_asprintf(program, "newfs_%s", format);
2287#else
2288        mr_asprintf(program, "mkfs -t %s -c", format);  // -c checks for bad blocks
2289#endif
2290        log_it("Unknown format (%s) - assuming '%s' will do", format, program);
2291        res = 0;
2292    }
2293    return (program);
2294}
2295
2296
2297/**
2298 * Resize a drive's entries in @p mountlist proportionately to fit its new size.
2299 * There are a few problems with this function:
2300 * - It won't work if there was any unallocated space on the user's hard drive
2301 *   when it was backed up.
2302 * - It won't work if the user's hard drive lies about its size (more common
2303 *   than you'd think).
2304 *
2305 * @param mountlist The mountlist to use for resizing @p drive_name.
2306 * @param drive_name The drive to resize.
2307 */
2308void resize_drive_proportionately_to_suit_new_drives(struct mountlist_itself
2309                                                     *mountlist,
2310                                                     char *drive_name)
2311{
2312
2313    /** int *************************************************************/
2314    int partno, lastpart;
2315
2316    /** float ***********************************************************/
2317    float factor;
2318    long long new_size;
2319
2320    /** long *************************************************************/
2321    long long newsizL = 0LL;
2322    long long totalsizL = 0LL;
2323    long long current_size_of_drive = 0LL;  /* use KB interally for precision */
2324    long long original_size_of_drive = 0LL; /* use KB interally for precision */
2325    struct mountlist_reference *drivemntlist;
2326
2327    /** structures *******************************************************/
2328
2329    /** end **************************************************************/
2330
2331    assert(mountlist != NULL);
2332    assert_string_is_neither_NULL_nor_zerolength(drive_name);
2333
2334    if (strlen(drive_name) >= strlen(RAID_DEVICE_STUB)) {
2335        if (strncmp(drive_name, RAID_DEVICE_STUB, strlen(RAID_DEVICE_STUB))
2336            == 0) {
2337            return;
2338        }
2339    }
2340
2341    current_size_of_drive = (long long) get_phys_size_of_drive(drive_name) * 1024LL;
2342
2343    if (current_size_of_drive <= 0LL) {
2344        log_it("Not resizing to match %s - can't find drive", drive_name);
2345        return;
2346    }
2347    log_to_screen("Expanding entries to suit drive %s (%lld MB)", drive_name, current_size_of_drive / 1024);
2348
2349    drivemntlist = malloc(sizeof(struct mountlist_reference));
2350    drivemntlist->el = malloc(sizeof(struct mountlist_line *) * MAX_MOUNTLIST_ENTRIES);
2351
2352    if (!drivemntlist) {
2353        fatal_error("Cannot malloc temporary mountlist\n");
2354    }
2355    create_mountlist_for_drive(mountlist, drive_name, drivemntlist);
2356
2357    for (partno = 0; partno < drivemntlist->entries; partno++) {
2358        if (drivemntlist->el[partno]->size > 0LL) {
2359            /* Keep KB here */
2360            original_size_of_drive += drivemntlist->el[partno]->size;
2361        }
2362    }
2363
2364    if (original_size_of_drive <= 0LL) {
2365        log_to_screen("Cannot resize %s's entries. Drive not found.", drive_name);
2366        return;
2367    }
2368    factor = ((float)current_size_of_drive/(float)original_size_of_drive);
2369    mr_asprintf(tmp, "Disk %s was %lld MB; is now %lld MB; Proportionally resizing partitions (factor ~= %.5f)",
2370            drive_name, original_size_of_drive/1024, current_size_of_drive/1024, factor);
2371    log_to_screen(tmp);
2372    mr_free(tmp);
2373
2374    lastpart = drivemntlist->entries - 1;
2375    for (partno = 0; partno < drivemntlist->entries; partno++) {
2376        /* the 'atoi' thing is to make sure we don't try to resize _images_, whose formats will be numeric */
2377        if (!atoi(drivemntlist->el[partno]->format)) {
2378            new_size = (long long)((drivemntlist->el[partno]->size) * factor);
2379        } else {
2380            new_size = drivemntlist->el[partno]->size;
2381        }
2382
2383        if (!strcmp(drivemntlist->el[partno]->mountpoint, "image")) {
2384            log_msg(1, "Skipping %s (%s) because it's an image",
2385                    drivemntlist->el[partno]->device,
2386                    drivemntlist->el[partno]->mountpoint);
2387        }
2388        newsizL = new_size;
2389
2390        /* Do not apply the factor if partition was of negative size */
2391        if (newsizL < 0LL) {
2392            newsizL = drivemntlist->el[partno]->size;
2393        }
2394        totalsizL += newsizL;
2395
2396        log_to_screen("Changing %s from %lld KB to %lld KB", drivemntlist->el[partno]->device, drivemntlist->el[partno]->size, newsizL);
2397        drivemntlist->el[partno]->size = newsizL;
2398    }
2399    // Ensures over-allocation alert and prompt for interactive mode does not happen
2400    if (totalsizL > current_size_of_drive) {
2401        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));
2402        drivemntlist->el[drivemntlist->entries-1]->size -= (totalsizL - current_size_of_drive);
2403    } else if (totalsizL < current_size_of_drive) {
2404        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));
2405        drivemntlist->el[drivemntlist->entries-1]->size += (current_size_of_drive - totalsizL);
2406    }
2407    log_to_screen("final_size = %lld MB", current_size_of_drive / 1024);
2408}
2409
2410
2411/**
2412 * Resize all partitions in @p mountlist proportionately (each one
2413 * grows or shrinks by the same percentage) to fit them into the new
2414 * drives (presumably different from the old ones).
2415 * @param mountlist The mountlist to resize the drives in.
2416 */
2417void resize_mountlist_proportionately_to_suit_new_drives(struct mountlist_itself
2418                                                         *mountlist)
2419{
2420    /** buffers *********************************************************/
2421    struct list_of_disks *drivelist;
2422
2423    /** int *************************************************************/
2424    int driveno;
2425
2426    /** end *************************************************************/
2427
2428    drivelist = malloc(sizeof(struct list_of_disks));
2429    assert(mountlist != NULL);
2430
2431    log_it("Resizing mountlist");
2432    make_list_of_drives_in_mountlist(mountlist, drivelist);
2433    log_it("Back from MLoDiM");
2434    for (driveno = 0; driveno < drivelist->entries; driveno++) {
2435        resize_drive_proportionately_to_suit_new_drives(mountlist,
2436                                                        drivelist->
2437                                                        el[driveno].
2438                                                        device);
2439    }
2440    log_to_screen("Mountlist adjusted to suit current hard drive(s)");
2441    paranoid_free(drivelist);
2442}
2443
2444/**
2445 * Create a mountlist_reference structure for @p drive_name in @p mountlist.
2446 * @param mountlist The complete mountlist to get the drive references from.
2447 * @param drive_name The drive to put in @p drivemntlist.
2448 * @param drivemntlist The mountlist_reference structure to put the drive's entries in.
2449 * @note @p drivemntlist and @p drivemntlist->el must be allocated by the caller.
2450 * @author Ralph Grewe
2451 */
2452void create_mountlist_for_drive(struct mountlist_itself *mountlist,
2453                                char *drive_name,
2454                                struct mountlist_reference *drivemntlist)
2455{
2456    int partno;
2457    char *tmp_drive_name, *c;
2458
2459    assert(mountlist != NULL);
2460    assert(drive_name != NULL);
2461    assert(drivemntlist != NULL);
2462
2463    log_msg(1, "Creating list of partitions for drive %s", drive_name);
2464
2465    tmp_drive_name = strdup(drive_name);
2466    if (!tmp_drive_name)
2467        fatal_error("Out of memory");
2468
2469    /* devfs devices? */
2470    c = strrchr(tmp_drive_name, '/');
2471    if (c && strncmp(c, "/disc", 5) == 0) {
2472        /* yup its devfs, change the "disc" to "part" so the existing code works */
2473        strcpy(c + 1, "part");
2474    }
2475    drivemntlist->entries = 0;
2476    for (partno = 0; partno < mountlist->entries; partno++) {
2477        if (strncmp
2478            (mountlist->el[partno].device, tmp_drive_name,
2479             strlen(tmp_drive_name)) == 0) {
2480            drivemntlist->el[drivemntlist->entries] =
2481                &mountlist->el[partno];
2482            drivemntlist->entries++;
2483        }
2484    }
2485    if (tmp_drive_name)
2486        free(tmp_drive_name);
2487}
2488
2489/* @} - end of prepGroup */
Note: See TracBrowser for help on using the repository browser.