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

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