source: branches/2.2.10/mondo/src/mondorestore/mondo-prep.c @ 2323

Revision 2323, 72.9 KB checked in by bruno, 4 years ago (diff)

r3334@localhost: bruno | 2009-08-08 12:17:37 +0200

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