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

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