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

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