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

Last change on this file since 2211 was 2211, checked in by Bruno Cornec, 11 years ago

r3089@localhost: bruno | 2009-05-18 06:41:05 +0200

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