source: branches/2.2.9/mondo/src/mondorestore/mondo-prep.c @ 2290

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