source: MondoRescue/branches/stable/mondo/src/mondorestore/mondo-prep.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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