From 3cf8a17c3bc34fc3b152a99a50a8c67f42264dc7 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 15 Jul 2005 02:07:22 +0000 Subject: [PATCH] Branch b1_4_newconfig2 b=6663 begin port of mountconf to new portals 1_4 --- lustre/include/linux/lustre_cfg.h | 17 +- lustre/utils/Makefile.am | 30 +- lustre/utils/mkfs_lustre.c | 925 ++++++++++++++++++++++++++++++++++++++ lustre/utils/mount_lustre.c | 540 ++++++++++++++++++++++ 4 files changed, 1499 insertions(+), 13 deletions(-) create mode 100644 lustre/utils/mkfs_lustre.c create mode 100644 lustre/utils/mount_lustre.c diff --git a/lustre/include/linux/lustre_cfg.h b/lustre/include/linux/lustre_cfg.h index b9dee63..411562f 100644 --- a/lustre/include/linux/lustre_cfg.h +++ b/lustre/include/linux/lustre_cfg.h @@ -233,12 +233,21 @@ static inline int lustre_cfg_sanity_check(void *buf, int len) } /* Passed by mount */ +#define LMD_FLG_FLOCK 0x0001 /* Enable flock */ +#define LMD_FLG_MNTCNF 0x1000 /* MountConf compat */ +#define LMD_FLG_CLIENT 0x2000 /* Mounting a client only; no real device */ + struct lustre_mount_data { uint32_t lmd_magic; - uint32_t lmd_version; - uint64_t lmd_nid; - char lmd_mds[64]; - char lmd_profile[64]; + uint32_t lmd_flags; /* mount flags */ + uint64_t lmd_nid; /* local nid */ + char lmd_mds[64]; /* FIXME to become lmd_mgmt, alt_mgmt */ + char lmd_profile[64]; /* FIXME to go away */ + char lmd_sv_source[128]; /* server source device */ +/* FIXME below must be determined in the kernel, not passed in from mount */ + uint32_t lmd_sv_disk_type; /* server disk type (MDT, OST, MGMT) */ + char lmd_sv_fstype[64]; /* server device fs type (ext3, ldiskfs) */ + char lmd_sv_fsopts[128]; /* server fs mount opts */ }; diff --git a/lustre/utils/Makefile.am b/lustre/utils/Makefile.am index 20c1542..c018bd1 100644 --- a/lustre/utils/Makefile.am +++ b/lustre/utils/Makefile.am @@ -8,13 +8,13 @@ AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1 LIBPTLCTL := $(top_builddir)/portals/utils/libptlctl.a sbin_scripts = lconf lmc llanalyze llstat.pl llobdstat.pl lactive \ - load_ldap.sh lrun lwizard -bin_scripts = lfind lstripe + load_ldap.sh lrun lwizard mount.lustre mkfs.lustre +bin_scripts = lfind lstripe llog_reader if UTILS -rootsbin_SCRIPTS = mount.lustre -sbin_PROGRAMS = lctl obdio obdbarrier lload wirecheck wiretest llmount -bin_PROGRAMS = lfs +rootsbin_SCRIPTS = mount.lustre mkfs.lustre +sbin_PROGRAMS = lctl obdio obdbarrier lload wirecheck wiretest mount.lustre mkfs.lustre +bin_PROGRAMS = lfs llog_reader lib_LIBRARIES = liblustreapi.a sbin_SCRIPTS = $(sbin_scripts) bin_SCRIPTS = $(bin_scripts) @@ -29,6 +29,9 @@ lfs_DEPENDENCIES := $(LIBPTLCTL) liblustreapi.a lload_LDADD := $(LIBREADLINE) $(LIBPTLCTL) lload_DEPENDENCIES := $(LIBPTLCTL) +llog_reader_LDADD := $(LIBREADLINE) $(LIBPTLCTL) +llog_reader_DEPENDENCIES := $(LIBPTLCTL) + liblustreapi_a_SOURCES = liblustreapi.c wirecheck_SOURCES = wirecheck.c @@ -42,9 +45,15 @@ obdio_SOURCES = obdio.c obdiolib.c obdiolib.h obdbarrier_SOURCES = obdbarrier.c obdiolib.c obdiolib.h lfs_SOURCES = lfs.c parser.c obd.c -llmount_SOURCES = llmount.c -llmount_LDADD = $(LIBREADLINE) $(LIBPTLCTL) -llmount_DEPENDENCIES := $(LIBPTLCTL) +mount_lustre_SOURCES = mount_lustre.c +mount_lustre_LDADD = $(LIBREADLINE) $(LIBPTLCTL) +mount_lustre_DEPENDENCIES := $(LIBPTLCTL) + +mkfs_lustre_SOURCES = mkfs_lustre.c lustre_cfg.c obd.c obdctl.h +mkfs_lustre_LDADD = $(LIBREADLINE) $(LIBPTLCTL) +mkfs_lustre_DEPENDENCIES := $(LIBPTLCTL) + +llog_reader_SOURCES = llog_reader.c EXTRA_DIST = $(bin_scripts) $(sbin_scripts) @@ -53,5 +62,8 @@ newwiretest: wirehdr.c wirecheck cp wirehdr.c wiretest.c ./wirecheck >> wiretest.c -mount.lustre$(EXEEXT): llmount +mount.lustre$(EXEEXT): mount_lustre + cp $< $@ + +mkfs.lustre$(EXEEXT): mkfs_lustre cp $< $@ diff --git a/lustre/utils/mkfs_lustre.c b/lustre/utils/mkfs_lustre.c new file mode 100644 index 0000000..669b8a1 --- /dev/null +++ b/lustre/utils/mkfs_lustre.c @@ -0,0 +1,925 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * Copyright (C) 2002 Cluster File Systems, Inc. + * Author: Lin Song Tao + * Author: Nathan Rutman + * + * This file is part of Lustre, http://www.lustre.org. + * + * Lustre is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * Lustre is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Lustre; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + + +#include +#include + +#include +#include + +#include "obdctl.h" +#include + +/* So obd.o will link */ +#include "parser.h" +command_t cmdlist[] = { + { 0, 0, 0, NULL } +}; + +/* FIXME */ +#define MAX_LOOP_DEVICES 256 + +static char *progname = "lmkfs"; +static int verbose = 1; + +/* for running system() */ +static char cmd[50]; +static char cmd_out[32][128]; +static char *ret_file = "/tmp/mkfs.log"; + +/* for init loop */ +static char loop_base[20]; + +void usage(FILE *out) +{ + fprintf(out, "usage: %s [options] \n", progname); + + fprintf(out, + "\t:type of Lustre service (mds, ost or mgt)\n" + "\t:block device or file (e.g /dev/hda or /tmp/ost1)\n" + "\t-h|--help: print this usage message\n" + "\tmkfs.lustre options:\n" + "\t\t--mgmtnode=[,]:nid of mgmt node [and the failover mgmt node]\n" + "\t\t--failover=\n" + "\t\t--device_size=#N(KB):device size \n" + "\t\t--stripe_count=#N:number of stripe\n" + "\t\t--stripe_size=#N(KB):stripe size\n" + "\t\t--stripe_index=#N:stripe index for ost\n" + "\t\t--smfsopts \n" + "\t\t--ext3opts \n"); + exit(out != stdout); +} + +inline unsigned int +dev_major (unsigned long long int __dev) +{ + return ((__dev >> 8) & 0xfff) | ((unsigned int) (__dev >> 32) & ~0xfff); +} + +inline unsigned int +dev_minor (unsigned long long int __dev) +{ + return (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff); +} + +#define vprint if (verbose) printf + +int get_os_version() +{ + static int version = 0; + + if (!version) { + int fd; + char release[4] = ""; + + fd = open("/proc/sys/kernel/osrelease", O_RDONLY); + if (fd < 0) + fprintf(stderr, "Warning: Can't resolve kernel version," + " assuming 2.6\n"); + else { + read(fd, release, 4); + close(fd); + } + if (strncmp(release, "2.4.", 4) == 0) + version = 24; + else + version = 26; + } + return version; +} + +//Ugly implement. FIXME +int run_command(char *cmd, char out[32][128]) +{ + int i = 0,ret = 0; + FILE *rfile = NULL; + + vprint("cmd: %s\n", cmd); + + strcat(cmd, " >"); + strcat(cmd, ret_file); + strcat(cmd, " 2>&1"); + + ret = system(cmd); + + rfile = fopen(ret_file, "r"); + if (rfile == NULL){ + fprintf(stderr,"Could not open %s \n",ret_file); + exit(2); + } + + memset(out, 0, sizeof(out)); + while (fgets(out[i], 128, rfile) != NULL) { + i++; + if (i >= 32) { + fprintf(stderr,"WARNING losing some outputs when run %s", + cmd); + break; + } + } + fclose(rfile); + + return ret; +} + +void init_loop_base() +{ + if (!access("/dev/loop0",F_OK|R_OK)) + strcpy(loop_base,"/dev/loop\0"); + else if (!access("/dev/loop/0",F_OK|R_OK)) + strcpy(loop_base,"/dev/loop/\0"); + else { + fprintf(stderr,"can't access loop devices\n"); + exit(1); + } + return; +} + +/* Find loop device assigned to the file */ +int find_assigned_loop(char* file, char* loop_device) +{ + int i,ret; + char l_device[20]; + char *loop_file; + + for (i = 0; i < MAX_LOOP_DEVICES; i++) { + sprintf(l_device, "%s%d", loop_base, i); + if (access(l_device, F_OK | R_OK)) + break; + + sprintf(cmd, "losetup %s", l_device); + ret = run_command(cmd, cmd_out); + /* losetup gets 0 for set-up device */ + if (!ret) { + loop_file = strrchr(cmd_out[0], '(') + 1; + if (!strncmp(loop_file, file, strlen(file))){ + strcpy(loop_device, l_device); + return 1; + } + } + } + return 0; +} + +/* Setup file in first unused loop_device */ +int setup_loop(char* file, char* loop_device) +{ + int i,ret = 0; + char l_device[20]; + + for (i = 0; i < MAX_LOOP_DEVICES; i++) { + sprintf(l_device, "%s%d", loop_base, i); + if (access(l_device, F_OK | R_OK)) + break; + + sprintf(cmd, "losetup %s", l_device); + ret = run_command(cmd, cmd_out); + /* losetup gets 1 (256?) for good non-set-up device */ + if (ret) { + sprintf(cmd, "losetup %s %s", l_device, file); + ret = run_command(cmd, cmd_out); + if (ret) { + fprintf(stderr, "error %d on losetup: %s\n", + ret, strerror(ret)); + exit(8); + } + strcpy(loop_device, l_device); + return ret; + } + } + + fprintf(stderr,"out of loop device\n"); + return 1; +} + + +int is_block(char* devname) +{ + struct stat st; + int ret = 0; + + ret = access(devname, F_OK); + if (ret != 0) + return 0; + ret = stat(devname, &st); + if (ret != 0) { + fprintf(stderr,"can not stat %s\n",devname); + exit(4); + } + return S_ISBLK(st.st_mode); +} + +/* get the devsize from /proc/partitions with the major and minor number */ +int device_size_proc(char* device) +{ + int major,minor,i,ret; + char *ma, *mi, *sz; + struct stat st; + + ret = stat(device,&st); + if (ret != 0) { + fprintf(stderr,"can not stat %s\n",device); + exit(4); + } + major = dev_major(st.st_rdev); + minor = dev_minor(st.st_rdev); + + sprintf(cmd,"cat /proc/partitions "); + ret = run_command(cmd,cmd_out); + for (i=0; i<32; i++) { + if (strlen(cmd_out[i]) == 0) + break; + ma = strtok(cmd_out[i]," "); + mi = strtok(NULL," "); + if ( (major == atol(ma)) && (minor == atol(mi)) ) { + sz = strtok(NULL," "); + return atol(sz); + } + } + + return 0; //FIXME : no entries in /proc/partitions +} + +int write_local_files(struct lustre_disk_data *data) +{ + struct lr_server_data lsd; + char mntpt[] = "/tmp/mntXXXXXX"; + char filepnm[sizeof(mntpt) + 15]; + FILE *filep; + int ret = 0; + + /* mount this device temporarily as ext3 in order to write this file */ + if (!mkdtemp(mntpt)) { + fprintf(stderr, "Can't create temp mount point %s: %s\n", + mntpt, strerror(errno)); + return errno; + } + + if (data->flags & FSFLG_IS_LOOP) + sprintf(cmd, "mount -o loop %s %s", data->device, mntpt); + else + sprintf(cmd, "mount -t ext3 %s %s", data->device, mntpt); + ret = run_command(cmd, cmd_out); + if (ret) { + fprintf(stderr, "Unable to mount %s\n", + data->device); + goto out_rmdir; + } + + /* Save the disk mount options into a file. llmount must pre-read + this file to get the real mount options. I suppose you could try + to parse this out of the *-conf logs instead, but bleah. */ + sprintf(filepnm, "%s/%s", mntpt, MOUNTOPTS_FILE_NAME); + filep = fopen(filepnm, "w"); + if (!filep) { + fprintf(stderr,"Unable to create %s file\n", filepnm); + goto out_umnt; + } + + ret = fprintf(filep, "%x\n", data->magic); + if (ret <= 0) { + fprintf(stderr, "Can't write options file (%d)\n", + ferror(filep)); + goto out_close; + } + fprintf(filep, "%s\n", data->fs_type_string); + fprintf(filep, "%s\n", data->mountfsopts); + fprintf(filep, "%x\n", data->disk_type); + fwrite(&data->mgt.host, sizeof(data->mgt.host), 1, filep); + fclose(filep); + + if IS_MDS(data) { + uint32_t stripe_size = data->u.mds.stripe_size; + uint32_t stripe_count = data->u.mds.stripe_count; + uint32_t stripe_pattern = data->u.mds.stripe_pattern; + sprintf(filepnm, "%s/%s", mntpt, STRIPE_FILE); + filep = fopen(filepnm, "w"); + if (!filep) { + fprintf(stderr,"Unable to create %s file\n", filepnm); + goto out_umnt; + } + + ret = fwrite(&stripe_size, sizeof(stripe_size), 1, filep); + if (ret <= 0) { + fprintf(stderr, "Can't write options file (%d)\n", + ferror(filep)); + goto out_close; + } + ret = fwrite(&stripe_count, sizeof(stripe_count), 1, filep); + ret = fwrite(&stripe_pattern, sizeof(stripe_pattern), 1, filep); + + fclose(filep); + } + + /* Create the inital last_rcvd file */ + sprintf(filepnm, "%s/%s", mntpt, LAST_RCVD); + filep = fopen(filepnm, "w"); + if (!filep) { + ret = errno; + fprintf(stderr,"Unable to create %s file\n", filepnm); + goto out_umnt; + } + memset(&lsd, 0, sizeof(lsd)); + strncpy(lsd.lsd_uuid, data->obduuid, sizeof(lsd.lsd_uuid)); + fwrite(&lsd, sizeof(lsd), 1, filep); + ret = 0; + +out_close: + fclose(filep); +out_umnt: + sprintf(cmd, "umount %s", mntpt); + run_command(cmd, cmd_out); +out_rmdir: + rmdir(mntpt); + return ret; +} + +/* build fs according to type + FIXME:dangerous */ +int make_lustre_backfs(struct lustre_disk_data *data) +{ + int i,ret=0; + int block_count = 0; + char mkfs_cmd[256]; + char buf[40]; + + if (data->device_size != 0) { + if (data->device_size < 8096){ + fprintf(stderr, "size of filesystem must be larger " + "than 8MB, but is set to %dKB\n", + data->device_size); + return EINVAL; + } + block_count = data->device_size / 4; /* block size is 4096 */ + } + + //FIXME: no ext2 here + if ((data->fs_type == FS_EXT3) || (data->fs_type == FS_LDISKFS)) { + if (data->device_size == 0){ + sprintf(cmd, "sfdisk -s %s", data->device); + ret = run_command(cmd, cmd_out); + if (ret == 0) + data->device_size = atol(cmd_out[0]); + else + data->device_size = + device_size_proc(data->device); + } + if (data->journal_size == 0) { //FIXME: kick the jdev in prototype + if (data->device_size > 1024 * 1024) + data->journal_size = (data->device_size / + 102400) * 4; + if (data->journal_size > 400) + data->journal_size = 400; + } + if (data->journal_size > 0) { + sprintf(buf, " -J size=%d", data->journal_size); + strcat(data->mkfsopts, buf); + } + if (data->inode_size > 0) { + sprintf(buf, " -i %d ", data->inode_size); + strcat(data->mkfsopts, buf); + } + + sprintf(mkfs_cmd, "mkfs.ext2 -j -b 4096 -L %s ", data->obdname); + //FIXME: losing the jdev in this phases + } else if (data->fs_type == FS_REISERFS) { + if (data->journal_size > 0) { + sprintf(buf, " --journal_size %d", data->journal_size); + strcat(data->mkfsopts, buf); + } + sprintf(mkfs_cmd, "mkreiserfs -ff "); + } else { + fprintf(stderr,"unsupported fs type: %s\n", + data->fs_type_string); + return EINVAL; + } + + vprint("formatting backing filesystem %s on %s\n", + data->fs_type_string, data->device); + vprint("\tdevice label %s\n", data->fsname); + vprint("\tdevice size %dMB\n", data->device_size / 1024); + vprint("\tjournal size %d\n", data->journal_size); + vprint("\tinode size %d\n", data->inode_size); + vprint("\t4k blocks %d\n", block_count); + vprint("\toptions %s\n", data->mkfsopts); + + /* mkfs_cmd's trailing space is important! */ + strcat(mkfs_cmd, data->mkfsopts); + strcat(mkfs_cmd, data->device); + if (block_count != 0) { + sprintf(buf, " %d", block_count); + strcat(mkfs_cmd, buf); + } + + vprint("mkfs_cmd = %s\n", mkfs_cmd); + ret = run_command(mkfs_cmd, cmd_out); + if (ret != 0) { + fprintf(stderr, "Unable to build fs: %s \n", data->device); + for (i = 0; i < 32; i++) { + if (strlen(cmd_out[i]) == 0) + break; + fprintf(stderr, cmd_out[i]); + } + return EIO; + } + + if ((data->fs_type == FS_EXT3) || (data->fs_type == FS_LDISKFS)) { + sprintf(cmd, "tune2fs -O dir_index %s", data->device); + ret = run_command(cmd, cmd_out); + if (ret) { + fprintf(stderr,"Unable to enable htree: %s\n", + data->device); + exit(4); + } + } + + return ret; +} + +int setup_loop_device(struct lustre_disk_data *data) +{ + char loop_device[20] = ""; + int ret = 0; + + init_loop_base(); + +#if 0 /* Do we need this? */ + if (find_assigned_loop(data->device, loop_device)) { + fprintf(stderr,"WARNING file %s already mapped to %s\n", + data->device, loop_device); + return 1; + } +#endif + + sprintf(cmd, "dd if=/dev/zero bs=1k count=0 seek=%d of=%s", + data->device_size, data->device); + ret = run_command(cmd, cmd_out); + if (ret != 0){ + fprintf(stderr, "Unable to create backing store: %d\n", ret); + return ret; + } + + ret = setup_loop(data->device, loop_device); + + if (ret == 0) + /* Our device is now the loop device, not the file name */ + strcpy(data->device, loop_device); + else + fprintf(stderr, "Loop device setup failed %d\n", ret); + + return ret; +} + +static int jt_setup() +{ + int ret; + ret = access("/dev/portals", F_OK); + if (ret) + system("mknod /dev/portals c 10 240"); + ret = access("/dev/obd", F_OK); + if (ret) + system("mknod /dev/obd c 10 241"); + + ptl_initialize(0, NULL); + ret = obd_initialize(0, NULL); + if (ret) { + fprintf(stderr,"Can't obd initialize\n"); + return 2; + } + return 0; +} + + +static void jt_print(char *cmd_name, int argc, char **argv) +{ + int i = 0; + printf("%-20.20s: ", cmd_name); + while (i < argc) { + printf("%s ", argv[i]); + i++; + } + printf("\n"); +} + +static int _do_jt(int (*cmd)(int argc, char **argv), char *cmd_name, ...) +{ + va_list ap; + char *jt_cmds[10]; + char *s; + int i = 0; + int ret; + + va_start(ap, cmd_name); + while (i < 10) { + s = va_arg(ap, char *); + if (!s) + break; + jt_cmds[i] = malloc(strlen(s) + 1); + strcpy(jt_cmds[i], s); + i++; + } + va_end(ap); + + if (verbose) + jt_print(cmd_name, i, jt_cmds); + + ret = (*cmd)(i, jt_cmds); + if (ret) + fprintf(stderr, "%s: jt_cmd %s: (%d) %s\n", + progname, jt_cmds[0], ret, strerror(abs(ret))); + + while (i) + free(jt_cmds[--i]); + + return ret; +} + +#define do_jt(cmd, a...) if ((ret = _do_jt(cmd, #cmd, ## a))) goto out +#define do_jt_noret(cmd, a...) _do_jt(cmd, #cmd, ## a) + +int lustre_log_setup(struct lustre_disk_data *data) +{ + char confname[] = "confobd"; + char name[128]; + int ret = 0; + + vprint("Creating Lustre logs\n"); + + if (jt_setup()) + return 2; + + /* Set up our confobd for writing logs */ + ret = do_jt_noret(jt_lcfg_attach, "attach", "confobd", confname, + "conf_uuid", 0); + if (ret) + return ENODEV; + ret = do_jt_noret(jt_lcfg_device, "cfg_device", confname, 0); + if (ret) + return ENODEV; + do_jt(jt_lcfg_setup, "setup", data->device, data->fs_type_string, + data->mountfsopts, 0); + do_jt(jt_obd_device, "device", "confobd", 0); + + sprintf(name, "%s-conf", data->obdname); + + if (IS_OST(data)) { + do_jt(jt_cfg_clear_log, "clear_log", name, 0); + do_jt(jt_cfg_record, "record", name, 0); + do_jt(jt_lcfg_attach, "attach", "obdfilter", data->obdname, + data->obduuid, 0); + do_jt(jt_lcfg_device, "cfg_device", data->obdname, 0); + do_jt(jt_lcfg_setup, "setup", data->device, + data->fs_type_string, + "n", /*data->u.ost.host.failover_addr*/ + data->mountfsopts, 0); + do_jt(jt_cfg_endrecord, "endrecord", 0); + do_jt(jt_cfg_dump_log, "dump_log", name, 0); + + do_jt(jt_cfg_clear_log, "clear_log", "OSS-conf", 0); + do_jt(jt_cfg_record, "record", "OSS-conf", 0); + do_jt(jt_lcfg_attach, "attach", "ost", "OSS", "OSS_UUID", 0); + do_jt(jt_lcfg_device, "cfg_device", "OSS", 0); + do_jt(jt_lcfg_setup, "setup", 0); + do_jt(jt_cfg_endrecord, "endrecord", 0); + } + + if (IS_MDS(data)) { + /* write mds-conf log */ + do_jt(jt_cfg_clear_log, "clear_log", name, 0); + do_jt(jt_cfg_record, "record", name, 0); + do_jt(jt_lcfg_attach, "attach", "mdt", "MDT", "MDT_UUID", 0); + do_jt(jt_lcfg_device, "cfg_device", "MDT", 0); + do_jt(jt_lcfg_setup, "setup", 0); + do_jt(jt_lcfg_attach, "attach", "mds", data->obdname, + data->obduuid, 0); + do_jt(jt_lcfg_device, "cfg_device", data->obdname, 0); + do_jt(jt_lcfg_setup, "setup", data->device, + data->fs_type_string, + data->obdname, data->mountfsopts, 0); + do_jt(jt_cfg_endrecord, "endrecord", 0); + + /* TEMPORARY - needs to be moved into mds_setup for 1st mount */ +#if 0 + /* write mds startup log */ + do_jt(jt_cfg_clear_log, "clear_log", data->obdname, 0); + do_jt(jt_cfg_record, "record", data->obdname, 0); + /*add_uuid NID_uml2_UUID uml2 tcp + network tcp + add_peer uml2 uml2 988*/ + /*attach lov lov_conf_mdsA f0591_lov_conf_mdsA_224a85b5fc + lov_setup lovA_UUID 0 1048576 0 0 ost1_UUID + mount_option mdsA lov_conf_mdsA + */ + do_jt(jt_lcfg_attach, "attach", "lov", "lov_c", "lov_c_uuid", 0); + do_jt(jt_lcfg_lov_setup, "lov_setup", "lovA_uuid", + "0" /*data->u.mds.stripe_count*/, + "1048576" /*data->u.mds.stripe_size*/, + "0" /* stripe_off FIXME */, + "0" /* stripe_pattern */, 0); + do_jt(jt_lcfg_mount_option,"mount_option", data->obdname, "lov_c", 0); + do_jt(jt_cfg_endrecord, "endrecord", 0); +#endif + } + +out: + if (ret) + /* Assume we erred while writing a record */ + do_jt_noret(jt_cfg_endrecord, "endrecord", 0); + /* Clean up the confobd when we're done writing logs */ + do_jt_noret(jt_lcfg_device, "cfg_device", confname, 0); + do_jt_noret(jt_obd_cleanup, "cleanup", 0); + do_jt_noret(jt_obd_detach, "detach", 0); + + do_jt_noret(obd_finalize, "finalize", 0); + + return ret; +} + +static int load_module(char *module_name) +{ + char buf[256]; + int rc; + + vprint("loading %s\n", module_name); + sprintf(buf, "/sbin/modprobe %s", module_name); + rc = system(buf); + if (rc) { + fprintf(stderr, "%s: failed to modprobe %s (%d)\n", + progname, module_name, rc); + fprintf(stderr, "Check /etc/modules.conf\n"); + } + return rc; +} + +/* Make the mds/ost obd name based on the filesystem name */ +static void make_obdname(struct lustre_disk_data *data) +{ + int maxlen = sizeof(data->obdname) - 1; + + if (IS_MDS(data)) { + snprintf(data->obdname, maxlen, "MDS%s", data->fsname); + } else if (IS_OST(data)) { + char number[5]; + snprintf(data->obdname, maxlen, "OST%s", data->fsname); + /* FIXME if we're not given an index, we might + have to change our name later -- can't have two ost's + with the same name. Rewrite ost log?? */ + snprintf(number, 5, "%04x", data->u.ost.stripe_index); + /* Truncate label if need be to get the whole index number */ + data->obdname[maxlen - 4] = 0; + strcat(data->obdname, number); + } else { + snprintf(data->obdname, maxlen, "MGT%s", data->fsname); + } + + /* This uuid must match the client's concept of the server uuid in + confobd_update_logs*/ + snprintf(data->obduuid, sizeof(data->obduuid), "%s_UUID", data->obdname); +} + +void set_defaults(struct lustre_disk_data *data) +{ + data->magic = LDD_MAGIC; + + if (get_os_version() == 24) { + data->fs_type = FS_EXT3; + strcpy(data->fs_type_string, "ext3"); + } else { + data->fs_type = FS_LDISKFS; + strcpy(data->fs_type_string, "ldiskfs"); + } + + strcpy(data->fsname, "lustre"); +} + +int main(int argc , char *const argv[]) +{ + struct lustre_disk_data data; + static struct option long_opt[] = { + {"help", 0, 0, 'h'}, + {"fsname",1, 0, 'n'}, + {"mgtnode", 1, 0, 'm'}, + {"failover", 1, 0, 'f'}, + {"device_size", 1, 0, 'd'}, + {"stripe_count", 1, 0, 'c'}, + {"stripe_size", 1, 0, 's'}, + {"stripe_index", 1, 0, 'i'}, + {"smfsopts", 1, 0, 'S'}, + {"ext3opts", 1, 0, 'e'}, + {0, 0, 0, 0} + }; + char *optstring = "hn:m:f:d:c:s:i:S:e:"; + char opt; + int ret = 0; + + if (argc < 3) + usage(stderr); + + progname = argv[0]; + memset(&data, 0, sizeof(data)); + set_defaults(&data); + + if (!strcasecmp(argv[1], "mds")){ + data.disk_type |= MDS_DISK_TYPE; + data.disk_type |= MGT_DISK_TYPE; + } + else if (!strcasecmp(argv[1], "ost")) { + data.disk_type |= OST_DISK_TYPE; + data.u.ost.stripe_index = -1; + } else if (!strcasecmp(argv[1], "mgt")) { + data.disk_type |= MGT_DISK_TYPE; + } else { + fprintf(stderr, "%s: need to know disk type :{mds,ost,mgt}\n", + progname); + usage(stderr); + } + + optind++; + + while ((opt = getopt_long(argc,argv,optstring,long_opt,NULL)) != EOF) { + switch (opt) { + case 'h': + usage(stdout); + break; + case 'n': + if (optarg[0] != 0) + strncpy(data.fsname, optarg, + sizeof(data.fsname) - 1); + break; + case 'm': + if IS_MGT(&data){ + fprintf(stderr, "%s: wrong option for mgt" + "%s \n", progname, optarg); + usage(stderr); + } + if IS_MDS(&data){ + /* FIXME after the network is done */ + strcpy(data.u.mds.mgt_node.host, optarg); + data.disk_type &= ~MGT_DISK_TYPE; + } + if IS_OST(&data) + strcpy(data.u.ost.mgt_node.host,optarg); + break; + case 'f': /* FIXME after the network is done */ + if IS_MDS(&data) + strcpy(data.u.mds.host.failover_addr,optarg); + if IS_OST(&data) + strcpy(data.u.ost.host.failover_addr,optarg); + if IS_MGT(&data) + strcpy(data.mgt.host.failover_addr,optarg); + break; + case 'd': + data.device_size = atol (optarg); + break; + case 'c': + if IS_MDS(&data) { + int stripe_count = atol(optarg); + data.u.mds.stripe_count = stripe_count; + if (stripe_count > 77) + data.inode_size = 4096; + else if (stripe_count > 35) + data.inode_size = 2048; + else + data.inode_size = 1024; + } + if IS_MGT(&data) + data.mgt.stripe_count = atol(optarg); + else { + fprintf(stderr,"%s: wrong option for ost %s\n", + progname,optarg); + usage(stderr); + } + break; + case 's': + if IS_MDS(&data) + data.u.mds.stripe_size = atol(optarg)*1024; + else { + fprintf(stderr, "%s: wrong option for mgt%s\n", + progname,optarg); + usage(stderr); + } + break; + case 'i': + if IS_OST(&data) + data.u.ost.stripe_index = atol(optarg); + else { + fprintf(stderr, + "%s: wrong option for mds/mgt%s\n", + progname,optarg); + usage(stderr); + } + break; + case 'S': + data.fs_type = FS_SMFS; + strcpy(data.fs_type_string, "smfs"); + strncpy(data.mkfsopts, optarg, sizeof(data.mkfsopts)-1); + break; + case 'e': + data.fs_type = FS_EXT3; + strcpy(data.fs_type_string, "ext3"); + strncpy(data.mkfsopts, optarg, sizeof(data.mkfsopts)-1); + break; + default: + fprintf(stderr, "%s: unknown option '%c'\n", + progname, opt); + usage(stderr); + break; + } + }//while + + strcpy(data.device, argv[optind]); + + if (IS_OST(&data) && (data.u.ost.stripe_index == -1)) { + fprintf(stderr, "You must set --stripe_index=?? for now.\n"); + return EINVAL; + } + + if (IS_MDS(&data) && (data.u.mds.stripe_size == 0)) + data.u.mds.stripe_size = 1024 * 1024; + + /* These are the mount options stored in the log files. + llmount must use the same options to start the confobd. */ + if ((data.fs_type == FS_EXT3) || (data.fs_type == FS_LDISKFS)) { + sprintf(data.mountfsopts, "errors=remount-ro"); + if (IS_MDS(&data)) + strcat(data.mountfsopts, ",iopen_nopriv"); + if ((IS_OST(&data)) && (get_os_version() == 24)) + strcat(data.mountfsopts, ",asyncdel"); + } else if (data.fs_type == FS_SMFS) { + sprintf(data.mountfsopts, "type=ext3,dev=%s", data.device); + } else { + fprintf(stderr, "%s: unknown fs type %d '%s'\n", + progname, data.fs_type, data.fs_type_string); + return EINVAL; + } + + make_obdname(&data); + + if (load_module("confobd")) + return ENOSYS; + + if ((data.fs_type == FS_SMFS) || !is_block(data.device)) { + data.flags |= FSFLG_IS_LOOP; + ret = setup_loop_device(&data); + if (ret) + return ret; + } + + ret = make_lustre_backfs(&data); + if (ret != 0) { + fprintf(stderr, "mkfs failed\n"); + goto out; + } + + ret = write_local_files(&data); + if (ret != 0) { + fprintf(stderr, "failed to write local files\n"); + goto out; + } + + ret = lustre_log_setup(&data); + if (ret != 0) { + fprintf(stderr, "failed to write setup logs\n"); + goto out; + } + +out: + if (data.flags & FSFLG_IS_LOOP) { + sprintf(cmd, "losetup -d %s", data.device); + ret = run_command(cmd, cmd_out); + } + + return ret; +} diff --git a/lustre/utils/mount_lustre.c b/lustre/utils/mount_lustre.c new file mode 100644 index 0000000..291d29f --- /dev/null +++ b/lustre/utils/mount_lustre.c @@ -0,0 +1,540 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * Copyright (C) 2002 Cluster File Systems, Inc. + * Author: Robert Read + * Author: Nathan Rutman + * + * This file is part of Lustre, http://www.lustre.org. + * + * Lustre is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * Lustre is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Lustre; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "obdctl.h" +#include +#include + +int verbose; +int nomtab; +int fake; +int force; +static char *progname = NULL; + +void usage(FILE *out) +{ + fprintf(out, "usage: %s :// " + "[-fhnv] [-o mntopt]\n", progname); + fprintf(out, "\t: nid of MDS (config) node\n" + "\t: name of MDS service (e.g. mds1)\n" + "\t: name of client config (e.g. client)\n" + "\t: filesystem mountpoint (e.g. /mnt/lustre)\n" + "\t-f|--fake: fake mount (updates /etc/mtab)\n" + "\t--force: force mount even if already in /etc/mtab\n" + "\t-h|--help: print this usage message\n" + "\t-n|--nomtab: do not update /etc/mtab after mount\n" + "\t-v|--verbose: print verbose config settings\n"); + exit(out != stdout); +} + +int get_os_version() +{ + static int version = 0; + + if (!version) { + int fd; + char release[4] = ""; + + fd = open("/proc/sys/kernel/osrelease", O_RDONLY); + if (fd < 0) + fprintf(stderr, "Warning: Can't resolve kernel version," + " assuming 2.6\n"); + else { + read(fd, release, 4); + close(fd); + } + if (strncmp(release, "2.4.", 4) == 0) + version = 24; + else + version = 26; + } + return version; +} + +static int load_module(char *module_name) +{ + char buf[256]; + int rc; + + if (verbose) + printf("loading %s\n", module_name); + sprintf(buf, "/sbin/modprobe %s", module_name); + rc = system(buf); + if (rc) { + fprintf(stderr, "%s: failed to modprobe %s: %s\n", + progname, module_name, strerror(errno)); + fprintf(stderr, "Check /etc/modules.conf\n"); + } + return rc; +} + +static int load_modules(struct lustre_mount_data *lmd) +{ + int rc = 0; + + if (lmd_is_client(lmd)) { + rc = load_module("lustre"); + } else { + if (lmd->u.srv.disk_type & MDS_DISK_TYPE) + rc = load_module("mds"); + if (rc) return rc; + if (lmd->u.srv.disk_type & OST_DISK_TYPE) + rc = load_module("oss"); + } + return rc; +} + +static int check_mtab_entry(char *spec, char *mtpt, char *type) +{ + FILE *fp; + struct mntent *mnt; + + if (force) + return (0); + + fp = setmntent(MOUNTED, "r"); + if (fp == NULL) + return(0); + + while ((mnt = getmntent(fp)) != NULL) { + if (strcmp(mnt->mnt_fsname, spec) == 0 && + strcmp(mnt->mnt_dir, mtpt) == 0 && + strcmp(mnt->mnt_type, type) == 0) { + fprintf(stderr, "%s: according to %s %s is " + "already mounted on %s\n", + progname, MOUNTED, spec, mtpt); + return(1); /* or should we return an error? */ + } + } + endmntent(fp); + + return(0); +} + +static int +update_mtab_entry(char *spec, char *mtpt, char *type, char *opts, + int flags, int freq, int pass) +{ + FILE *fp; + struct mntent mnt; + int rc = 0; + + mnt.mnt_fsname = spec; + mnt.mnt_dir = mtpt; + mnt.mnt_type = type; + mnt.mnt_opts = opts ? opts : ""; + mnt.mnt_freq = freq; + mnt.mnt_passno = pass; + + fp = setmntent(MOUNTED, "a+"); + if (fp == NULL) { + fprintf(stderr, "%s: setmntent(%s): %s:", + progname, MOUNTED, strerror (errno)); + rc = 16; + } else { + if ((addmntent(fp, &mnt)) == 1) { + fprintf(stderr, "%s: addmntent: %s:", + progname, strerror (errno)); + rc = 16; + } + endmntent(fp); + } + + return rc; +} + +int +init_options(struct lustre_mount_data *lmd) +{ + memset(lmd, 0, sizeof(*lmd)); + //gethostname(lmd->lmd_hostname, sizeof lmd->lmd_hostname); + //lmd->lmd_server_nid = PTL_NID_ANY; + //ptl_parse_nid(&lmd->lmd_nid, lmd->lmd_hostname); + //lmd->lmd_port = 988; /* XXX define LUSTRE_DEFAULT_PORT */ + //lmd->lmd_nal = SOCKNAL; + //ptl_parse_ipaddr(&lmd->lmd_ipaddr, lmd->lmd_hostname); + //lmd->u.cli.lmd_async = 0; + lmd->lmd_magic = LMD_MAGIC; + lmd->lmd_nid = PTL_NID_ANY; + return 0; +} + +int +print_options(struct lustre_mount_data *lmd) +{ + printf("nid: %s\n", libcfs_nid2str(lmd->lmd_nid)); + + if (lmd_is_client(lmd)) { + printf("CLIENT\n"); + printf("mds: %s\n", lmd->u.cli.lmd_mds); + printf("profile: %s\n", lmd->u.cli.lmd_profile); + } else { + printf("SERVER\n"); + printf("service type: %x\n", lmd->u.srv.disk_type); + printf("device: %s\n", lmd->u.srv.lmd_source); + printf("fs type: %s\n", lmd->u.srv.lmd_fstype); + printf("fs opts: %s\n", lmd->u.srv.lmd_fsopts); + } + + return 0; +} + +/***************************************************************************** + * + * This part was cribbed from util-linux/mount/mount.c. There was no clear + * license information, but many other files in the package are identified as + * GNU GPL, so it's a pretty safe bet that was their intent. + * + ****************************************************************************/ +struct opt_map { + const char *opt; /* option name */ + int skip; /* skip in mtab option string */ + int inv; /* true if flag value should be inverted */ + int mask; /* flag mask value */ +}; + +static const struct opt_map opt_map[] = { + { "defaults", 0, 0, 0 }, /* default options */ + { "rw", 1, 1, MS_RDONLY }, /* read-write */ + { "ro", 0, 0, MS_RDONLY }, /* read-only */ + { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */ + { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */ + { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */ + { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */ + { "dev", 0, 1, MS_NODEV }, /* interpret device files */ + { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */ + { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */ + { "auto", 0, 0, 0 }, /* Can be mounted using -a */ + { "noauto", 0, 0, 0 }, /* Can only be mounted explicitly */ + { "nousers", 0, 1, 0 }, /* Forbid ordinary user to mount */ + { "nouser", 0, 1, 0 }, /* Forbid ordinary user to mount */ + { "noowner", 0, 1, 0 }, /* Device owner has no special privs */ + { "_netdev", 0, 0, 0 }, /* Device accessible only via network */ + { NULL, 0, 0, 0 } +}; +/****************************************************************************/ + +static int parse_one_option(const char *check, int *flagp) +{ + const struct opt_map *opt; + + for (opt = &opt_map[0]; opt->opt != NULL; opt++) { + if (strcmp(check, opt->opt) == 0) { + if (opt->inv) + *flagp &= ~(opt->mask); + else + *flagp |= opt->mask; + return 1; + } + } + return 0; +} + +int parse_options(char *options, struct lustre_mount_data *lmd, int *flagp) +{ + int val; + char *opt, *opteq; + + *flagp = 0; + /* parsing ideas here taken from util-linux/mount/nfsmount.c */ + for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) { + if ((opteq = strchr(opt, '='))) { + val = atoi(opteq + 1); + *opteq = '\0'; + if (0) { + /* All the network options have gone :)) */ + } else { + fprintf(stderr, "%s: unknown option '%s'\n", + progname, opt); + usage(stderr); + } + } else { + if (parse_one_option(opt, flagp)) + continue; + + fprintf(stderr, "%s: unknown option '%s'\n", + progname, opt); + usage(stderr); + } + } + return 0; +} + +int read_mount_options(char *source, char *target, + struct lustre_mount_data *lmd) +{ + char cmd[512]; + char opfilenm[255]; + FILE *opfile; + __u32 lddmagic; + int ret; + + if ( strlen(lmd->u.srv.lmd_source) == 0) { + strcpy(lmd->u.srv.lmd_source, source); + sprintf(cmd, "mount -t ext3 %s %s", lmd->u.srv.lmd_source, target); + } + else + sprintf(cmd, "mount -o loop %s %s", lmd->u.srv.lmd_source, target); + + ret = system(cmd); + if (ret) { + fprintf(stderr, "Unable to mount %s\n", lmd->u.srv.lmd_source); + return errno; + } + + /* mounted, now read the options file */ + sprintf(opfilenm, "%s/%s", target, MOUNTOPTS_FILE_NAME); + opfile = fopen(opfilenm, "r"); + if (!opfile) { + fprintf(stderr,"Unable to open options file %s: %s\n", + opfilenm, strerror(errno)); + ret = errno; + goto out_umnt; + } + + ret = fscanf(opfile, "%x\n", &lddmagic); + if (ret < 1) { + fprintf(stderr, "Can't read options file %s\n", opfilenm); + goto out_close; + } + if (lddmagic != LDD_MAGIC) { + fprintf(stderr, "Bad magic in options file %s\n", opfilenm); + goto out_close; + } + + fscanf(opfile, "%s\n", lmd->u.srv.lmd_fstype); + fscanf(opfile, "%s\n", lmd->u.srv.lmd_fsopts); + fscanf(opfile, "%x\n", &lmd->u.srv.disk_type); + //fread(&data->mgt.host, sizeof(data->mgt.host), 1, opfile); + ret = 0; + +out_close: + fclose(opfile); +out_umnt: + sprintf(cmd, "umount %s", target); + system(cmd); + return ret; +} + +int +build_data(char *source, char *target, char *options, + struct lustre_mount_data *lmd, int *flagp) +{ + char buf[1024]; + char *nid = NULL; + char *mgmt = NULL; + char *s; + int rc; + + if (lmd_bad_magic(lmd)) + return 4; + + if (strlen(source) >= sizeof(buf)) { + fprintf(stderr, "%s: nid:/fsname argument too long\n", + progname); + return 1; + } + strcpy(buf, source); + + if ((s = strchr(buf, ':'))) { + /* Client */ + if (verbose) + printf("CLIENT\n"); + lmd->lmd_type = + + nid = buf; + *s = '\0'; + + while (*++s == '/') + ; + mgmt = s; + + rc = parse_options(options, lmd, flagp); + if (rc) + return rc; + + lmd->lmd_nid = libcfs_str2nid(nid); + if (lmd->lmd_nid == PTL_NID_ANY) { + fprintf(stderr, "%s: can't parse nid '%s'\n", + progname, libcfs_nid2str(lmd->lmd_nid)); + return 1; + } + + if (strlen(mgmt) + 4 > sizeof(lmd->lmd_mgmt)) { + fprintf(stderr, "%s: mgmt name too long\n", progname); + return(1); + } + strcpy(lmd->lmd_mds, mgmt); + } else { + /* Server */ + if (verbose) + printf("SERVER\n"); + lmd->lmd_magic = LMD_SERVER_MAGIC; + + if (strlen(source) + 1 > sizeof(lmd->u.srv.lmd_source)) { + fprintf(stderr, "%s: source name too long\n", progname); + return(1); + } + + /* We have to keep the loop= option in the mtab file + in order for umount to free the loop device. The strtok + in parse_options terminates the options list at the first + comma, so we're saving a local copy here. */ + strcpy(buf, options); + rc = parse_options(options, lmd, flagp); + fprintf(stderr, "source = %s \n",lmd->u.srv.lmd_source); + print_options(lmd); + if (rc) + return rc; + strcpy(options, buf); + + rc = read_mount_options(source, target, lmd); + if (rc) + return rc; + } + + if (verbose) + print_options(lmd); + return 0; +} + +int main(int argc, char *const argv[]) +{ + char *source, *target, *options = ""; + int i, nargs = 3, opt, rc, flags; + struct lustre_mount_data lmd; + static struct option long_opt[] = { + {"fake", 0, 0, 'f'}, + {"force", 0, 0, 1}, + {"help", 0, 0, 'h'}, + {"nomtab", 0, 0, 'n'}, + {"options", 1, 0, 'o'}, + {"verbose", 0, 0, 'v'}, + {0, 0, 0, 0} + }; + + progname = strrchr(argv[0], '/'); + progname = progname ? progname + 1 : argv[0]; + + while ((opt = getopt_long(argc, argv, "fhno:v", + long_opt, NULL)) != EOF){ + switch (opt) { + case 1: + ++force; + printf("force: %d\n", force); + nargs++; + break; + case 'f': + ++fake; + printf("fake: %d\n", fake); + nargs++; + break; + case 'h': + usage(stdout); + break; + case 'n': + ++nomtab; + printf("nomtab: %d\n", nomtab); + nargs++; + break; + case 'o': + options = optarg; + nargs++; + break; + case 'v': + ++verbose; + printf("verbose: %d\n", verbose); + nargs++; + break; + default: + fprintf(stderr, "%s: unknown option '%c'\n", + progname, opt); + usage(stderr); + break; + } + } + + if (optind + 2 > argc) { + fprintf(stderr, "%s: too few arguments\n", progname); + usage(stderr); + } + + source = argv[optind]; + target = argv[optind + 1]; + + if (verbose) { + for (i = 0; i < argc; i++) + printf("arg[%d] = %s\n", i, argv[i]); + printf("source = %s, target = %s\n", source, target); + } + + if (!force && check_mtab_entry(source, target, "lustre")) + exit(32); + + init_options(&lmd); + rc = build_data(source, target, options, &lmd, &flags); + if (rc) { + exit(1); + } + + rc = access(target, F_OK); + if (rc) { + rc = errno; + fprintf(stderr, "%s: %s inaccessible: %s\n", progname, target, + strerror(errno)); + return 1; + } + + if ((rc = load_modules(&lmd))) { + return rc; + } + + if (!fake) + rc = mount(source, target, "lustre", flags, (void *)&lmd); + if (rc) { + fprintf(stderr, "%s: mount(%s, %s) failed: %s\n", source, + target, progname, strerror(errno)); + if (errno == ENODEV) + fprintf(stderr, "Are the lustre modules loaded?\n" + "Check /etc/modules.conf and /proc/filesystems\n"); + rc = 32; + } else if (!nomtab) { + rc = update_mtab_entry(source, target, "lustre", options,0,0,0); + } + + return rc; +} -- 1.8.3.1