Whamcloud - gitweb
Branch b1_4_mountconf
[fs/lustre-release.git] / lustre / utils / mkfs_lustre.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *   Copyright (C) 2002 Cluster File Systems, Inc.
5  *   Author: Lin Song Tao <lincent@clusterfs.com>
6  *   Author: Nathan Rutman <nathan@clusterfs.com>
7  *
8  *   This file is part of Lustre, http://www.lustre.org.
9  *
10  *   Lustre is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   Lustre is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with Lustre; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  */
24
25 #define _GNU_SOURCE
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <stdarg.h>
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/mount.h>
35
36 #include <string.h>
37 #include <getopt.h>
38
39 #include <linux/types.h>
40 #define NO_SYS_VFS 1
41 #include <linux/fs.h> // for BLKGETSIZE64
42 #include <linux/lustre_disk.h>
43 #include <lnet/lnetctl.h>
44 #include "obdctl.h"
45
46 /* So obd.o will link */
47 #include "parser.h"
48 command_t cmdlist[] = {
49         { 0, 0, 0, NULL }
50 };
51
52 #define MAX_LOOP_DEVICES 16
53 #define L_BLOCK_SIZE 4096
54
55 static char *progname;
56 static int verbose = 1;
57 static int print_only = 0;
58
59
60 void usage(FILE *out)
61 {
62         fprintf(out, "usage: %s <target types> [options] <device>\n", progname);
63
64         fprintf(out, 
65                 "\t<device>:block device or file (e.g /dev/sda or /tmp/ost1)\n"
66                 "\ttarget types:\n"
67                 "\t\t--ost: object storage, mutually exclusive with mdt\n"
68                 "\t\t--mdt: metadata storage, mutually exclusive with ost\n"
69                 "\t\t--mgs: configuration management service - one per site\n"
70                 "\toptions:\n"
71                 "\t\t--mgsnid=<nid>[,<...>] : NID(s) of a remote mgs node\n"
72                 "\t\t\trequired for all targets other than the mgs node\n"
73                 "\t\t--fsname=<filesystem_name> : default is 'lustre'\n"
74 #if 0 /* FIXME implement 1.6.x */
75                 "\t\t--configdev=<altdevice|file>: store configuration info\n"
76                 "\t\t\tfor this device on an alternate device\n"
77 #endif
78                 "\t\t--failover=<nid>[,<...>] : list of NIDs for the failover\n"
79                 "\t\t\tpartners for this target\n" 
80                 "\t\t--backfstype=<fstype> : backing fs type (ext3, ldiskfs)\n"
81                 "\t\t--device-size=#N(KB) : device size for loop devices\n"
82                 "\t\t--stripe-count=#N : default number of stripes\n"
83                 "\t\t--stripe-size=#N(KB) : default stripe size\n"
84                 "\t\t--index=#N : target index\n"
85                 "\t\t--mountfsoptions=<opts> : permanent mount options\n"
86 #ifndef TUNEFS
87                 "\t\t--mkfsoptions=<opts> : format options\n"
88                 "\t\t--reformat: overwrite an existing disk\n"
89 #else
90                 "\t\t--nomgs: turn off MGS service on this MDT\n"
91 #endif
92                 "\t\t--print: just report what we would do; don't write to "
93                 "disk\n"
94                 "\t\t--timeout=<secs> : system timeout period\n"
95                 "\t\t--verbose\n"
96                 "\t\t--quiet\n");
97         return;
98 }
99
100 #define vprint if (verbose > 0) printf
101
102 static void fatal(void)
103 {
104         verbose = 0;
105         fprintf(stderr, "\n%s FATAL: ", progname);
106 }
107
108 /*================ utility functions =====================*/
109
110 inline unsigned int 
111 dev_major (unsigned long long int __dev)
112 {
113         return ((__dev >> 8) & 0xfff) | ((unsigned int) (__dev >> 32) & ~0xfff);
114 }
115
116 inline unsigned int
117 dev_minor (unsigned long long int __dev)
118 {
119         return (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff);
120 }
121
122 int get_os_version()
123 {
124         static int version = 0;
125
126         if (!version) {
127                 int fd;
128                 char release[4] = "";
129
130                 fd = open("/proc/sys/kernel/osrelease", O_RDONLY);
131                 if (fd < 0) 
132                         fprintf(stderr, "%s: Warning: Can't resolve kernel "
133                                 "version, assuming 2.6\n", progname);
134                 else {
135                         read(fd, release, 4);
136                         close(fd);
137                 }
138                 if (strncmp(release, "2.4.", 4) == 0) 
139                         version = 24;
140                 else 
141                         version = 26;
142         }
143         return version;
144 }
145
146 int run_command(char *cmd)
147 {
148        int rc;
149
150        if (verbose > 1)
151                printf("cmd: %s\n", cmd);
152        
153        strcat(cmd, " 2>&1");
154   
155        rc = system(cmd);
156        return rc;
157 }
158
159
160 /*============ disk dev functions ===================*/
161
162 /* Setup a file in the first unused loop_device */
163 int loop_setup(struct mkfs_opts *mop)
164 {
165         char loop_base[20];
166         char l_device[64];
167         int i,ret = 0;
168
169         /* Figure out the loop device names */
170         if (!access("/dev/loop0", F_OK | R_OK))
171                 strcpy(loop_base, "/dev/loop\0");
172         else if (!access("/dev/loop/0", F_OK | R_OK))
173                 strcpy(loop_base, "/dev/loop/\0");
174         else {
175                 fprintf(stderr, "%s: can't access loop devices\n", progname);
176                 return 1;
177         }
178
179         /* Find unused loop device */
180         for (i = 0; i < MAX_LOOP_DEVICES; i++) {
181                 char cmd[128];
182                 sprintf(l_device, "%s%d", loop_base, i);
183                 if (access(l_device, F_OK | R_OK)) 
184                         break;
185                 sprintf(cmd, "losetup %s > /dev/null", l_device);
186                 ret = run_command(cmd);
187                 /* losetup gets 1 (ret=256) for non-set-up device */
188                 if (ret) {
189                         /* Set up a loopback device to our file */
190                         sprintf(cmd, "losetup %s %s", l_device, mop->mo_device);
191                         ret = run_command(cmd);
192                         if (ret) {
193                                 fprintf(stderr, "%s: error %d on losetup: %s\n",
194                                         progname, ret, strerror(ret));
195                                 return ret;
196                         }
197                         strcpy(mop->mo_loopdev, l_device);
198                         return ret;
199                 }
200         }
201         
202         fprintf(stderr, "%s: out of loop devices!\n", progname);
203         return EMFILE;
204 }       
205
206 int loop_cleanup(struct mkfs_opts *mop)
207 {
208         char cmd[128];
209         int ret = 1;
210         if ((mop->mo_flags & MO_IS_LOOP) && *mop->mo_loopdev) {
211                 sprintf(cmd, "losetup -d %s", mop->mo_loopdev);
212                 ret = run_command(cmd);
213         }
214         return ret;
215 }
216
217 /* Determine if a device is a block device (as opposed to a file) */
218 int is_block(char* devname)
219 {
220         struct stat st;
221         int ret = 0;
222
223         ret = access(devname, F_OK);
224         if (ret != 0) 
225                 return 0;
226         ret = stat(devname, &st);
227         if (ret != 0) {
228                 fprintf(stderr, "%s: cannot stat %s\n", progname, devname);
229                 return -1;
230         }
231         return S_ISBLK(st.st_mode);
232 }
233
234 __u64 get_device_size(char* device) 
235 {
236         int ret, fd;
237         __u64 size = 0;
238
239         fd = open(device, O_RDONLY);
240         if (fd < 0) {
241                 fprintf(stderr, "%s: cannot open %s: %s\n", 
242                         progname, device, strerror(errno));
243                 return 0;
244         }
245
246         /* size in bytes. bz5831 */
247         ret = ioctl(fd, BLKGETSIZE64, (void*)&size);
248         close(fd);
249         if (ret < 0) {
250                 fprintf(stderr, "%s: size ioctl failed: %s\n", 
251                         progname, strerror(errno));
252                 return 0;
253         }
254         
255         vprint("device size = "LPU64"MB\n", size >> 20);
256         /* return value in KB */
257         return size >> 10;
258 }
259
260 int loop_format(struct mkfs_opts *mop)
261 {
262         int ret = 0;
263        
264         if (mop->mo_device_sz == 0) {
265                 fatal();
266                 fprintf(stderr, "loop device requires a --device-size= "
267                         "param\n");
268                 return EINVAL;
269         }
270
271         ret = creat(mop->mo_device, S_IRUSR|S_IWUSR);
272         ret = truncate(mop->mo_device, mop->mo_device_sz * 1024);
273         if (ret != 0) {
274                 ret = errno;
275                 fprintf(stderr, "%s: Unable to create backing store: %d\n", 
276                         progname, ret);
277         }
278
279         return ret;
280 }
281
282 /* Check whether the file exists in the device */
283 static int file_in_dev(char *file_name, char *dev_name)
284 {
285         FILE *fp;
286         char debugfs_cmd[256];
287         unsigned int inode_num;
288         int i;
289
290         /* Construct debugfs command line. */
291         memset(debugfs_cmd, 0, sizeof(debugfs_cmd));
292         sprintf(debugfs_cmd,
293                 "debugfs -c -R 'stat %s' %s 2>&1 | egrep '(Inode|unsupported)'",
294                 file_name, dev_name);
295
296         fp = popen(debugfs_cmd, "r");
297         if (!fp) {
298                 fprintf(stderr, "%s: %s\n", progname, strerror(errno));
299                 return 0;
300         }
301
302         if (fscanf(fp, "Inode: %u", &inode_num) == 1) { /* exist */
303                 pclose(fp);
304                 return 1;
305         }
306         i = fread(debugfs_cmd, 1, sizeof(debugfs_cmd), fp);
307         if (i) {
308                 /* Filesystem has unsupported feature */
309                 vprint("%.*s", i, debugfs_cmd);
310                 /* in all likelihood, the "unsupported feature" is
311                   'extents', which older debugfs does not understand.  
312                   Use e2fsprogs-1.38-cfs1 or later, available from 
313                   ftp://ftp.lustre.org/pub/lustre/other/e2fsprogs/ */
314                 return -1;
315         }
316         pclose(fp);
317         return 0;
318 }
319
320 /* Check whether the device has already been fomatted by mkfs.lustre */
321 static int is_lustre_target(struct mkfs_opts *mop)
322 {
323         int rc;
324         /* Check whether there exist MOUNT_DATA_FILE,
325            LAST_RCVD or CATLIST in the device. */
326         vprint("checking for existing Lustre data\n");
327         
328         if ((rc = file_in_dev(MOUNT_DATA_FILE, mop->mo_device))
329             || (rc = file_in_dev(LAST_RCVD, mop->mo_device))
330             || (rc = file_in_dev(CATLIST, mop->mo_device))) { 
331                 vprint("found Lustre data\n");
332                 /* in the -1 case, 'extents' means this really IS a lustre
333                    target */
334                 return rc; 
335         }
336
337         return 0; /* The device is not a lustre target. */
338 }
339
340 /* Build fs according to type */
341 int make_lustre_backfs(struct mkfs_opts *mop)
342 {
343         char mkfs_cmd[256];
344         char buf[40];
345         char *dev;
346         int ret = 0;
347         int block_count = 0;
348
349         if (mop->mo_device_sz != 0) {
350                 if (mop->mo_device_sz < 8096){
351                         fprintf(stderr, "%s: size of filesystem must be larger "
352                                 "than 8MB, but is set to %lldKB\n",
353                                 progname, mop->mo_device_sz);
354                         return EINVAL;
355                 }
356                 block_count = mop->mo_device_sz / (L_BLOCK_SIZE >> 10);
357         }       
358         
359         if ((mop->mo_ldd.ldd_mount_type == LDD_MT_EXT3) ||
360             (mop->mo_ldd.ldd_mount_type == LDD_MT_LDISKFS)) { 
361                 __u64 device_sz = mop->mo_device_sz;
362
363                 /* we really need the size */
364                 if (device_sz == 0) {
365                         device_sz = get_device_size(mop->mo_device);
366                         if (device_sz == 0) 
367                                 return ENODEV;
368                 }
369
370                 /* Journal size in MB */
371                 if (strstr(mop->mo_mkfsopts, "-J") == NULL) {
372                         /* Choose our own default journal size */
373                         long journal_sz = 0, max_sz;
374                         if (device_sz > 1024 * 1024) /* 1GB */
375                                 journal_sz = (device_sz / 102400) * 4;
376                         /* man mkfs.ext3 */
377                         max_sz = (102400 * L_BLOCK_SIZE) >> 20; /* 400MB */
378                         if (journal_sz > max_sz)
379                                 journal_sz = max_sz;
380                         if (journal_sz) {
381                                 sprintf(buf, " -J size=%ld", journal_sz);
382                                 strcat(mop->mo_mkfsopts, buf);
383                         }
384                 }
385
386                 /* Default bytes_per_inode is block size */
387                 if (strstr(mop->mo_mkfsopts, "-i") == NULL) {
388                         long bytes_per_inode = 0;
389                                         
390                         if (IS_MDT(&mop->mo_ldd)) 
391                                 bytes_per_inode = 4096;
392
393                         /* Allocate fewer inodes on large OST devices.  Most
394                            filesystems can be much more aggressive than even 
395                            this. */
396                         if ((IS_OST(&mop->mo_ldd) && (device_sz > 1000000))) 
397                                 bytes_per_inode = 16384;
398                         
399                         if (bytes_per_inode > 0) {
400                                 sprintf(buf, " -i %ld", bytes_per_inode);
401                                 strcat(mop->mo_mkfsopts, buf);
402                         }
403                 }
404                 
405                 /* This is an undocumented mke2fs option. Default is 128. */
406                 if (strstr(mop->mo_mkfsopts, "-I") == NULL) {
407                         long inode_size = 0;
408                         if (IS_MDT(&mop->mo_ldd)) {
409                                 if (mop->mo_ldd.ldd_stripe_count > 77)
410                                         inode_size = 512; /* bz 7241 */
411                                 else if (mop->mo_ldd.ldd_stripe_count > 34)
412                                         inode_size = 2048;
413                                 else if (mop->mo_ldd.ldd_stripe_count > 13)
414                                         inode_size = 1024;
415                                 else 
416                                         inode_size = 512;
417                         } else if (IS_OST(&mop->mo_ldd)) {
418                                 /* now as we store fids in EA on OST we need 
419                                    to make inode bigger */
420                                 inode_size = 256;
421                         }
422
423                         if (inode_size > 0) {
424                                 sprintf(buf, " -I %ld", inode_size);
425                                 strcat(mop->mo_mkfsopts, buf);
426                         }
427                         
428                 }
429
430                 if (verbose < 2) {
431                         strcat(mop->mo_mkfsopts, " -q");
432                 }
433
434                 /* Enable hashed b-tree directory lookup in large dirs bz6224 */
435                 if (strstr(mop->mo_mkfsopts, "-O") == NULL) {
436                         strcat(mop->mo_mkfsopts, " -O dir_index");
437                 }
438
439                 sprintf(mkfs_cmd, "mkfs.ext2 -j -b %d -L %s ", L_BLOCK_SIZE,
440                         mop->mo_ldd.ldd_svname);
441
442         } else if (mop->mo_ldd.ldd_mount_type == LDD_MT_REISERFS) {
443                 long journal_sz = 0; /* FIXME default journal size */
444                 if (journal_sz > 0) { 
445                         sprintf(buf, " --journal_size %ld", journal_sz);
446                         strcat(mop->mo_mkfsopts, buf);
447                 }
448                 sprintf(mkfs_cmd, "mkreiserfs -ff ");
449
450         } else {
451                 fprintf(stderr,"%s: unsupported fs type: %d (%s)\n",
452                         progname, mop->mo_ldd.ldd_mount_type, 
453                         MT_STR(&mop->mo_ldd));
454                 return EINVAL;
455         }
456
457         /* Loop device? */
458         dev = mop->mo_device;
459         if (mop->mo_flags & MO_IS_LOOP) 
460                 dev = mop->mo_loopdev;
461         
462         vprint("formatting backing filesystem %s on %s\n",
463                MT_STR(&mop->mo_ldd), dev);
464         vprint("\ttarget name  %s\n", mop->mo_ldd.ldd_svname);
465         vprint("\t4k blocks     %d\n", block_count);
466         vprint("\toptions       %s\n", mop->mo_mkfsopts);
467
468         /* mkfs_cmd's trailing space is important! */
469         strcat(mkfs_cmd, mop->mo_mkfsopts);
470         strcat(mkfs_cmd, " ");
471         strcat(mkfs_cmd, dev);
472         if (block_count != 0) {
473                 sprintf(buf, " %d", block_count);
474                 strcat(mkfs_cmd, buf);
475         }
476
477         vprint("mkfs_cmd = %s\n", mkfs_cmd);
478         ret = run_command(mkfs_cmd);
479         if (ret) {
480                 fatal();
481                 fprintf(stderr, "Unable to build fs %s (%d)\n", dev, ret);
482                 goto out;
483         }
484
485 out:
486         return ret;
487 }
488
489 /* ==================== Lustre config functions =============*/
490
491 void print_ldd(char *str, struct lustre_disk_data *ldd)
492 {
493         int i = 0;
494         printf("\n   %s:\n", str);
495         printf("Target:     %s\n", ldd->ldd_svname);
496         printf("Index:      %d\n", ldd->ldd_svindex);
497         printf("UUID:       %s\n", (char *)ldd->ldd_uuid);
498         printf("Lustre FS:  %s\n", ldd->ldd_fsname);
499         printf("Mount type: %s\n", MT_STR(ldd));
500         printf("Flags:      %#x\n", ldd->ldd_flags);
501         printf("              (%s%s%s%s%s%s)\n",
502                IS_MDT(ldd) ? "MDT ":"", 
503                IS_OST(ldd) ? "OST ":"",
504                IS_MGS(ldd) ? "MGS ":"",
505                ldd->ldd_flags & LDD_F_NEED_INDEX ? "needs_index ":"",
506                ldd->ldd_flags & LDD_F_NEED_REGISTER ? "must_register ":"",
507                ldd->ldd_flags & LDD_F_UPGRADE14 ? "upgrade1.4 ":"");
508         printf("Persistent mount opts: %s\n", ldd->ldd_mount_opts);
509         printf("MGS nids: ");
510         for (i = 0; i < ldd->ldd_mgsnid_count; i++) {
511                 printf("%c %s", (i == 0) ? ' ' : ',',
512                        libcfs_nid2str(ldd->ldd_mgsnid[i]));
513         }
514         printf("\nFailover nids: ");
515         for (i = 0; i < ldd->ldd_failnid_count; i++) {
516                 printf("%c %s", (i == 0) ? ' ' : ',',
517                        libcfs_nid2str(ldd->ldd_failnid[i]));
518         }
519
520         printf("\n\n");
521 }
522
523 /* Write the server config files */
524 int write_local_files(struct mkfs_opts *mop)
525 {
526         char mntpt[] = "/tmp/mntXXXXXX";
527         char filepnm[128];
528         char *dev;
529         FILE *filep;
530         int ret = 0;
531
532         /* Mount this device temporarily in order to write these files */
533         vprint("mounting backing device\n");
534         if (!mkdtemp(mntpt)) {
535                 fprintf(stderr, "%s: Can't create temp mount point %s: %s\n",
536                         progname, mntpt, strerror(errno));
537                 return errno;
538         }
539
540         dev = mop->mo_device;
541         if (mop->mo_flags & MO_IS_LOOP) 
542                 dev = mop->mo_loopdev;
543         
544         ret = mount(dev, mntpt, MT_STR(&mop->mo_ldd), 0, NULL);
545         if (ret) {
546                 fprintf(stderr, "%s: Unable to mount %s: %d\n", 
547                         progname, mop->mo_device, ret);
548                 goto out_rmdir;
549         }
550
551         /* Set up initial directories */
552         sprintf(filepnm, "%s/%s", mntpt, MOUNT_CONFIGS_DIR);
553         ret = mkdir(filepnm, 0777);
554         if ((ret != 0) && (errno != EEXIST)) {
555                 fprintf(stderr, "%s: Can't make configs dir %s (%d)\n", 
556                         progname, filepnm, ret);
557                 goto out_umnt;
558         } else if (errno == EEXIST) {
559                 ret = 0;
560         }
561
562         /* Save the persistent mount data into a file. Lustre must pre-read
563            this file to get the real mount options. */
564         vprint("Writing %s\n", MOUNT_DATA_FILE);
565         sprintf(filepnm, "%s/%s", mntpt, MOUNT_DATA_FILE);
566         filep = fopen(filepnm, "w");
567         if (!filep) {
568                 fprintf(stderr, "%s: Unable to create %s file\n",
569                         progname, filepnm);
570                 goto out_umnt;
571         }
572         fwrite(&mop->mo_ldd, sizeof(mop->mo_ldd), 1, filep);
573         fclose(filep);
574         
575         /* COMPAT_146 */
576 #ifdef TUNEFS
577         /* Check for upgrade */
578         if ((mop->mo_ldd.ldd_flags & (LDD_F_UPGRADE14 | LDD_F_SV_TYPE_MGS)) 
579             == (LDD_F_UPGRADE14 | LDD_F_SV_TYPE_MGS)) {
580                 char cmd[128];
581                 char *term;
582                 vprint("Copying old logs\n");
583                 /* Copy the old client log to fsname-client */
584                 sprintf(filepnm, "%s/%s/%s-client", 
585                         mntpt, MOUNT_CONFIGS_DIR, mop->mo_ldd.ldd_fsname);
586                 sprintf(cmd, "cp %s/%s/client %s", mntpt, MDT_LOGS_DIR,
587                         filepnm);
588                 if (verbose > 1) 
589                         printf("cmd: %s\n", cmd);
590                 ret = run_command(cmd);
591                 if (ret) {
592                         fprintf(stderr, "%s: Can't copy 1.4 config %s/client "
593                                 "(%d)\n", progname, MDT_LOGS_DIR, ret);
594                         fprintf(stderr, "mount -t ext3 %s somewhere, "
595                                 "find the client log for fs %s and "
596                                 "copy it manually into %s/%s-client, "
597                                 "then umount.\n",
598                                 mop->mo_device, 
599                                 mop->mo_ldd.ldd_fsname, MOUNT_CONFIGS_DIR,
600                                 mop->mo_ldd.ldd_fsname);
601                         goto out_umnt;
602                 }
603                 /* Copy the old mdt log to fsname-MDT0000 (get old
604                    name from mdt_UUID) */
605                 ret = 1;
606                 strcpy(filepnm, mop->mo_ldd.ldd_uuid);
607                 term = strstr(filepnm, "_UUID");
608                 if (term) {
609                         *term = '\0';
610                         sprintf(cmd, "cp %s/%s/%s %s/%s/%s",
611                                 mntpt, MDT_LOGS_DIR, filepnm, 
612                                 mntpt, MOUNT_CONFIGS_DIR,
613                                 mop->mo_ldd.ldd_svname);
614                         if (verbose > 1) 
615                                 printf("cmd: %s\n", cmd);
616                         ret = run_command(cmd);
617                 }
618                 if (ret) {
619                         fprintf(stderr, "%s: Can't copy 1.4 config %s/%s "
620                                 "(%d)\n", progname, MDT_LOGS_DIR, filepnm, ret);
621                         fprintf(stderr, "mount -t ext3 %s somewhere, "
622                                 "find the MDT log for fs %s and "
623                                 "copy it manually into %s/%s, "
624                                 "then umount.\n",
625                                 mop->mo_device, 
626                                 mop->mo_ldd.ldd_fsname, MOUNT_CONFIGS_DIR,
627                                 mop->mo_ldd.ldd_svname);
628                         goto out_umnt;
629                 }
630         }
631 #endif
632         /* end COMPAT_146 */
633
634
635 out_umnt:
636         vprint("unmounting backing device\n");
637         umount(mntpt);    
638 out_rmdir:
639         rmdir(mntpt);
640         return ret;
641 }
642
643 int read_local_files(struct mkfs_opts *mop)
644 {
645         char mntpt[] = "/tmp/mntXXXXXX";
646         char filepnm[128];
647         char *dev;
648         FILE *filep;
649         int ret = 0;
650
651         /* Mount this device temporarily in order to read these files */
652         vprint("mounting backing device\n");
653         if (!mkdtemp(mntpt)) {
654                 fprintf(stderr, "%s: Can't create temp mount point %s: %s\n",
655                         progname, mntpt, strerror(errno));
656                 return errno;
657         }
658
659         dev = mop->mo_device;
660         if (mop->mo_flags & MO_IS_LOOP) 
661                 dev = mop->mo_loopdev;
662         
663         ret = mount(dev, mntpt, MT_STR(&mop->mo_ldd), 0, NULL);
664         if (ret) {
665                 fprintf(stderr, "%s: Unable to mount %s: %s\n", 
666                         progname, mop->mo_device, strerror(ret));
667                 goto out_rmdir;
668         }
669
670         sprintf(filepnm, "%s/%s", mntpt, MOUNT_DATA_FILE);
671         filep = fopen(filepnm, "r");
672         if (filep) {
673                 vprint("Reading %s\n", MOUNT_DATA_FILE);
674                 fread(&mop->mo_ldd, sizeof(mop->mo_ldd), 1, filep);
675         } else {
676                 /* COMPAT_146 */
677                 /* Try to read pre-1.6 config from last_rcvd */
678                 struct lr_server_data lsd;
679                 fprintf(stderr, "%s: Unable to read %s, trying last_rcvd\n",
680                         progname, MOUNT_DATA_FILE);
681                 sprintf(filepnm, "%s/%s", mntpt, LAST_RCVD);
682                 filep = fopen(filepnm, "r");
683                 if (!filep) {
684                         fprintf(stderr, "%s: Unable to read old data\n",
685                                 progname);
686                         ret = errno;
687                         goto out_umnt;
688                 }
689                 vprint("Reading %s\n", LAST_RCVD);
690                 fread(&lsd, sizeof(lsd), 1, filep);
691
692                 if (lsd.lsd_feature_compat & OBD_COMPAT_OST) {
693                         mop->mo_ldd.ldd_flags = LDD_F_SV_TYPE_OST;
694                         mop->mo_ldd.ldd_svindex = lsd.lsd_ost_index;
695                 } else if (lsd.lsd_feature_compat & OBD_COMPAT_MDT) {
696                         /* We must co-locate so mgs can see old logs.
697                            If user doesn't want this, they can copy the old
698                            logs manually and re-tunefs. */
699                         mop->mo_ldd.ldd_flags = 
700                                 LDD_F_SV_TYPE_MDT | LDD_F_SV_TYPE_MGS;
701                         mop->mo_ldd.ldd_svindex = lsd.lsd_mdt_index;
702                 } else  {
703                         /* If neither is set, we're pre-1.4.6, make a guess. */
704                         sprintf(filepnm, "%s/%s", mntpt, MDT_LOGS_DIR);
705                         if (lsd.lsd_ost_index > 0) {
706                                 mop->mo_ldd.ldd_flags = LDD_F_SV_TYPE_OST;
707                                 mop->mo_ldd.ldd_svindex = lsd.lsd_ost_index;
708                         } else {
709                                 if ((ret = access(filepnm, F_OK)) == 0) {
710                                         mop->mo_ldd.ldd_flags =
711                                         LDD_F_SV_TYPE_MDT | 
712                                         LDD_F_SV_TYPE_MGS;
713                                         /* Old MDT's are always index 0 
714                                            (pre CMD) */
715                                         mop->mo_ldd.ldd_svindex = 0;
716                                 } else {
717                                         /* The index won't be correct */
718                                         mop->mo_ldd.ldd_flags =
719                                         LDD_F_SV_TYPE_OST | LDD_F_NEED_INDEX;
720                                 }
721                         }
722                 }
723
724                 memcpy(mop->mo_ldd.ldd_uuid, lsd.lsd_uuid, 
725                        sizeof(mop->mo_ldd.ldd_uuid));
726                 mop->mo_ldd.ldd_flags |= LDD_F_UPGRADE14;
727         }
728         /* end COMPAT_146 */
729         fclose(filep);
730         
731 out_umnt:
732         vprint("unmounting backing device\n");
733         umount(mntpt);    
734 out_rmdir:
735         rmdir(mntpt);
736         return ret;
737 }
738
739
740 void set_defaults(struct mkfs_opts *mop)
741 {
742         mop->mo_ldd.ldd_magic = LDD_MAGIC;
743         mop->mo_ldd.ldd_config_ver = 1;
744         mop->mo_ldd.ldd_flags = LDD_F_NEED_INDEX | LDD_F_NEED_REGISTER;
745         mop->mo_ldd.ldd_mgsnid_count = 0;
746         strcpy(mop->mo_ldd.ldd_fsname, "lustre");
747         if (get_os_version() == 24) 
748                 mop->mo_ldd.ldd_mount_type = LDD_MT_EXT3;
749         else 
750                 mop->mo_ldd.ldd_mount_type = LDD_MT_LDISKFS;
751         
752         mop->mo_ldd.ldd_svindex = -1;
753         mop->mo_ldd.ldd_stripe_count = 1;
754         mop->mo_ldd.ldd_stripe_sz = 1024 * 1024;
755         mop->mo_ldd.ldd_stripe_pattern = 0;
756 }
757
758 static inline void badopt(const char *opt, char *type)
759 {
760         fprintf(stderr, "%s: '--%s' only valid for %s\n",
761                 progname, opt, type);
762         usage(stderr);
763 }
764
765 int parse_opts(int argc, char *const argv[], struct mkfs_opts *mop,
766                char *mountopts)
767 {
768         static struct option long_opt[] = {
769                 {"backfstype", 1, 0, 'b'},
770                 {"configdev", 1, 0, 'C'},
771                 {"device-size", 1, 0, 'd'},
772                 {"fsname",1, 0, 'n'},
773                 {"failover", 1, 0, 'f'},
774                 {"help", 0, 0, 'h'},
775                 {"mdt", 0, 0, 'M'},
776                 {"mgs", 0, 0, 'G'},
777                 {"mgsnid", 1, 0, 'm'},
778                 {"mkfsoptions", 1, 0, 'k'},
779                 {"mountfsoptions", 1, 0, 'o'},
780                 {"nomgs", 0, 0, 'N'},
781                 {"ost", 0, 0, 'O'},
782                 {"print", 0, 0, 'p'},
783                 {"quiet", 0, 0, 'q'},
784                 {"reformat", 0, 0, 'r'},
785                 {"startupwait", 1, 0, 'w'},
786                 {"stripe-count", 1, 0, 'c'},
787                 {"stripe-size", 1, 0, 's'},
788                 {"stripe-index", 1, 0, 'i'},
789                 {"index", 1, 0, 'i'},
790                 {"timeout", 1, 0, 't'},
791                 {"verbose", 0, 0, 'v'},
792                 {0, 0, 0, 0}
793         };
794         char *optstring = "b:C:d:n:f:hI:MGm:k:No:Opqrw:c:s:i:t:v";
795         char opt;
796         int longidx;
797
798         while ((opt = getopt_long(argc, argv, optstring, long_opt, &longidx)) != 
799                EOF) {
800                 switch (opt) {
801                 case 'b': {
802                         int i = 0;
803                         while (i < LDD_MT_LAST) {
804                                 if (strcmp(optarg, mt_str(i)) == 0) {
805                                         mop->mo_ldd.ldd_mount_type = i;
806                                         break;
807                                 }
808                                 i++;
809                         }
810                         break;
811                 }
812                 case 'c':
813                         if (IS_MDT(&mop->mo_ldd)) {
814                                 int stripe_count = atol(optarg);
815                                 if (stripe_count <= 0) {
816                                         fprintf(stderr, "%s: bad stripe count "
817                                                 "%d\n", progname, stripe_count);
818                                         return 1;
819                                 }
820                                 mop->mo_ldd.ldd_stripe_count = stripe_count;
821                         } else {
822                                 badopt(long_opt[longidx].name, "MDT");
823                                 return 1;
824                         }
825                         break;
826                 case 'C': /* Configdev */
827                         //FIXME
828                         printf("Configdev not implemented\n");
829                         return 1;
830                 case 'd':
831                         mop->mo_device_sz = atol(optarg); 
832                         break;
833                 case 'f': {
834                         int i = 0;
835                         char *s1 = optarg, *s2;
836                         while ((s2 = strsep(&s1, ","))) {
837                                 mop->mo_ldd.ldd_failnid[i++] =
838                                         libcfs_str2nid(s2);
839                                 if (i >= MTI_NIDS_MAX) {
840                                         fprintf(stderr, "%s: too many failover "
841                                                 "nids, ignoring %s...\n", 
842                                                 progname, s1);
843                                         break;
844                                 }
845                         }
846                         mop->mo_ldd.ldd_failnid_count = i;
847                         break;
848                 }
849                 case 'G':
850                         mop->mo_ldd.ldd_flags |= LDD_F_SV_TYPE_MGS;
851                         break;
852                 case 'h':
853                         usage(stdout);
854                         return 1;
855                 case 'i':
856                         if (IS_MDT(&mop->mo_ldd) || IS_OST(&mop->mo_ldd)) {
857                                 mop->mo_ldd.ldd_svindex = atoi(optarg);
858                                 mop->mo_ldd.ldd_flags &= ~LDD_F_NEED_INDEX;
859                         } else {
860                                 badopt(long_opt[longidx].name, "MDT,OST");
861                                 return 1;
862                         }
863                         break;
864                 case 'k':
865                         strncpy(mop->mo_mkfsopts, optarg, 
866                                 sizeof(mop->mo_mkfsopts) - 1);
867                         break;
868                 case 'm': {
869                         int i = 0;
870                         char *s1 = optarg, *s2;
871                         if (IS_MGS(&mop->mo_ldd)) {
872                                 badopt(long_opt[longidx].name, 
873                                        "non-MGS MDT,OST");
874                                 return 1;
875                         }
876                         while ((s2 = strsep(&s1, ","))) {
877                                 mop->mo_ldd.ldd_mgsnid[i++] =
878                                         libcfs_str2nid(s2);
879                                 if (i >= MTI_NIDS_MAX) {
880                                         fprintf(stderr, "%s: too many MGS nids,"
881                                                 " ignoring %s...\n", 
882                                                 progname, s1);
883                                         break;
884                                 }
885                         }
886                         mop->mo_ldd.ldd_mgsnid_count = i;
887                         break;
888                 }
889                 case 'M':
890                         mop->mo_ldd.ldd_flags |= LDD_F_SV_TYPE_MDT;
891                         break;
892                 case 'n':
893                         if (!(IS_MDT(&mop->mo_ldd) || IS_OST(&mop->mo_ldd))) {
894                                 badopt(long_opt[longidx].name, "MDT,OST");
895                                 return 1;
896                         }
897                         if (strlen(optarg) > 8) {
898                                 fprintf(stderr, "%s: filesystem name must be "
899                                         "<= 8 chars\n", progname);
900                                 return 1;
901                         }
902                         if (optarg[0] != 0) 
903                                 strncpy(mop->mo_ldd.ldd_fsname, optarg, 
904                                         sizeof(mop->mo_ldd.ldd_fsname) - 1);
905                         break;
906                 case 'N':
907                         mop->mo_ldd.ldd_flags &= ~LDD_F_SV_TYPE_MGS;
908                         break;
909                 case 'o':
910                         mountopts = optarg;
911                         break;
912                 case 'O':
913                         mop->mo_ldd.ldd_flags |= LDD_F_SV_TYPE_OST;
914                         break;
915                 case 'p':
916                         print_only++;
917                         break;
918                 case 'q':
919                         verbose--;
920                         break;
921                 case 'r':
922                         mop->mo_flags |= MO_FORCEFORMAT;
923                         break;
924                 case 's':
925                         if (IS_MDT(&mop->mo_ldd)) {
926                                 mop->mo_ldd.ldd_stripe_sz = atol(optarg) * 1024;
927                         } else {
928                                 badopt(long_opt[longidx].name, "MDT");
929                                 return 1;
930                         }
931                         break;
932                 case 't':
933                         mop->mo_ldd.ldd_timeout = atol(optarg);
934                         break;
935                 case 'v':
936                         verbose++;
937                         break;
938                 default:
939                         if (opt != '?') {
940                                 fatal();
941                                 fprintf(stderr, "Unknown option '%c'\n", opt);
942                         }
943                         usage(stderr);
944                         return 1;
945                 }
946         }//while
947         if (optind >= argc) {
948                 fatal();
949                 fprintf(stderr, "Bad arguments\n");
950                 usage(stderr);
951                 return 1;
952         }
953
954         return 0;
955 }
956
957 int main(int argc, char *const argv[])
958 {
959         struct mkfs_opts mop;
960         char *mountopts = NULL;
961         char default_mountopts[1024] = "";
962         int  ret = 0;
963
964         if ((progname = strrchr(argv[0], '/')) != NULL)
965                 progname++;
966         else
967                 progname = argv[0];
968
969         if (argc < 2) {
970                 usage(stderr);
971                 ret = 1;
972                 goto out;
973         }
974
975         memset(&mop, 0, sizeof(mop));
976         set_defaults(&mop);
977
978         /* device is last arg */
979         strcpy(mop.mo_device, argv[argc - 1]);
980         /* Are we using a loop device? */
981         ret = is_block(mop.mo_device);
982         if (ret < 0) 
983                 goto out;
984         if (ret == 0) 
985                 mop.mo_flags |= MO_IS_LOOP;
986
987 #ifdef TUNEFS
988         /* For tunefs, we must read in the old values before parsing any
989            new ones. */
990         /* Create the loopback file */
991         if (mop.mo_flags & MO_IS_LOOP) {
992                 ret = access(mop.mo_device, F_OK);
993                 if (ret == 0)  
994                         ret = loop_setup(&mop);
995                 if (ret) {
996                         fatal();
997                         fprintf(stderr, "Loop device setup for %s failed: %s\n", 
998                                 mop.mo_device, strerror(ret));
999                         goto out;
1000                 }
1001         }
1002         
1003         /* Check whether the disk has already been formatted by mkfs.lustre */
1004         ret = is_lustre_target(&mop);
1005         if (ret == 0) {
1006                 fatal();
1007                 fprintf(stderr, "Device %s has not been formatted with "
1008                         "mkfs.lustre\n", mop.mo_device);
1009                 goto out;
1010         }
1011
1012         ret = read_local_files(&mop);
1013         if (ret) {
1014                 fatal();
1015                 fprintf(stderr, "Failed to read previous Lustre data from %s\n",
1016                         mop.mo_device);
1017                 goto out;
1018         }
1019
1020         if (verbose > 0) 
1021                 print_ldd("Read previous values", &(mop.mo_ldd));
1022 #endif
1023
1024         ret = parse_opts(argc, argv, &mop, mountopts);
1025         if (ret) 
1026                 goto out;
1027
1028         if (!(IS_MDT(&mop.mo_ldd) || IS_OST(&mop.mo_ldd) || 
1029               IS_MGS(&mop.mo_ldd))) {
1030                 fatal();
1031                 fprintf(stderr, "must set target type :{mdt,ost,mgs}\n");
1032                 usage(stderr);
1033                 ret = 1;
1034                 goto out;
1035         }
1036
1037         if (IS_MDT(&mop.mo_ldd) && !IS_MGS(&mop.mo_ldd) && 
1038             mop.mo_ldd.ldd_mgsnid_count == 0) {
1039                 vprint("No management node specified, adding MGS to this "
1040                        "MDT\n");
1041                 mop.mo_ldd.ldd_flags |= LDD_F_SV_TYPE_MGS;
1042         }
1043
1044         if (!IS_MGS(&mop.mo_ldd) && (mop.mo_ldd.ldd_mgsnid_count == 0)) {
1045                 fatal();
1046                 fprintf(stderr, "Must specify either --mgs or --mgsnid\n");
1047                 usage(stderr);
1048                 goto out;
1049         }
1050
1051         /* These are the permanent mount options (always included) */ 
1052         switch (mop.mo_ldd.ldd_mount_type) {
1053         case LDD_MT_EXT3:
1054         case LDD_MT_LDISKFS: {
1055                 sprintf(default_mountopts, "errors=remount-ro");
1056                 if (IS_MDT(&mop.mo_ldd) || IS_MGS(&mop.mo_ldd))
1057                         strcat(default_mountopts,
1058                                ",iopen_nopriv,user_xattr");
1059                 if ((get_os_version() == 24) && IS_OST(&mop.mo_ldd))
1060                         strcat(default_mountopts, ",asyncdel");
1061                 /* Files created while extents are enabled cannot be read if
1062                    mounted with a kernel that doesn't include the CFS patches.*/
1063                 if (IS_OST(&mop.mo_ldd) && 
1064                     mop.mo_ldd.ldd_mount_type == LDD_MT_LDISKFS) {
1065                         strcat(default_mountopts, ",extents,mballoc");
1066                 }
1067  
1068                 break;
1069         }
1070         case LDD_MT_SMFS: {
1071                 mop.mo_flags |= MO_IS_LOOP;
1072                 sprintf(default_mountopts, "type=ext3,dev=%s",
1073                         mop.mo_device);
1074                 break;
1075         }
1076         default: {
1077                 fatal();
1078                 fprintf(stderr, "unknown fs type %d '%s'\n",
1079                         mop.mo_ldd.ldd_mount_type,
1080                         MT_STR(&mop.mo_ldd));
1081                 ret = EINVAL;
1082                 goto out;
1083         }
1084         }               
1085
1086 #ifndef TUNEFS /* mkfs.lustre */
1087         if (mountopts) 
1088                 /* Tack on user supplied opts */
1089                 sprintf(mop.mo_ldd.ldd_mount_opts, "%s,%s", 
1090                         default_mountopts, mountopts);
1091         else
1092                 strcpy(mop.mo_ldd.ldd_mount_opts, default_mountopts);
1093 #else   /* tunefs.lustre - if mountopts are specified, they override 
1094            whatever we had before, so no defaults. */
1095         if (mountopts) 
1096                 strcpy(mop.mo_ldd.ldd_mount_opts, mountopts);
1097         else if (*mop.mo_ldd.ldd_mount_opts == 0) 
1098                 /* no mount opts were set ever, use the defaults. */
1099                 strcpy(mop.mo_ldd.ldd_mount_opts, default_mountopts);
1100         /* otherwise, use the old. */
1101 #endif
1102
1103         ldd_make_sv_name(&(mop.mo_ldd));
1104
1105         if (verbose > 0)
1106                 print_ldd("Permanent disk data", &(mop.mo_ldd));
1107
1108         if (print_only) {
1109                 printf("exiting before disk write.\n");
1110                 goto out;
1111         }
1112
1113 #ifndef TUNEFS /* mkfs.lustre */
1114         /* Create the loopback file of the correct size */
1115         if (mop.mo_flags & MO_IS_LOOP) {
1116                 ret = access(mop.mo_device, F_OK);
1117                 /* Don't destroy the loopback file if no FORCEFORMAT */
1118                 if (ret || (mop.mo_flags & MO_FORCEFORMAT))
1119                         ret = loop_format(&mop);
1120                 if (ret == 0)  
1121                         ret = loop_setup(&mop);
1122                 if (ret) {
1123                         fatal();
1124                         fprintf(stderr, "Loop device setup failed: %s\n", 
1125                                 strerror(ret));
1126                         goto out;
1127                 }
1128         }
1129
1130         /* Check whether the disk has already been formatted by mkfs.lustre */
1131         if (!(mop.mo_flags & MO_FORCEFORMAT)) {
1132                 ret = is_lustre_target(&mop);
1133                 if (ret) {
1134                         fatal();
1135                         fprintf(stderr, "Device %s was previously formatted " 
1136                                 "for lustre. Use --reformat to reformat it, "
1137                                 "or tunefs.lustre to modify.\n",
1138                                 mop.mo_device);
1139                         goto out;
1140                 }
1141         }
1142
1143         /* Format the backing filesystem */
1144         ret = make_lustre_backfs(&mop);
1145         if (ret != 0) {
1146                 fatal();
1147                 fprintf(stderr, "mkfs failed %d\n", ret);
1148                 goto out;
1149         }
1150 #endif
1151
1152         ret = write_local_files(&mop);
1153         if (ret != 0) {
1154                 fatal();
1155                 fprintf(stderr, "failed to write local files\n");
1156                 goto out;
1157         }
1158
1159 out:
1160         loop_cleanup(&mop);      
1161         return ret;
1162 }