X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Futils%2Fmount_utils_ldiskfs.c;h=54b62fe94740805d5311d3dfb437e22044a0d828;hb=33690b1276cf9730d680a789c7786f45e961fbf5;hp=097e08ec27832328526b72d0e560e44a3dc3a36e;hpb=7c5643233b91cf4729878cd8792255e5d12f481e;p=fs%2Flustre-release.git diff --git a/lustre/utils/mount_utils_ldiskfs.c b/lustre/utils/mount_utils_ldiskfs.c index 097e08e..54b62fe 100644 --- a/lustre/utils/mount_utils_ldiskfs.c +++ b/lustre/utils/mount_utils_ldiskfs.c @@ -27,7 +27,7 @@ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. * - * Copyright (c) 2011, Whamcloud, Inc. + * Copyright (c) 2012, 2015, Intel Corporation. */ /* * This file is part of Lustre, http://www.lustre.org/ @@ -47,12 +47,14 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include "mount_utils.h" #include #include #include #include #include #include +#include #include #include @@ -64,20 +66,25 @@ #include #include -#ifdef __linux__ -/* libcfs.h is not really needed here, but on SLES10/PPC, fs.h includes idr.h - * which requires BITS_PER_LONG to be defined */ -#include #ifndef BLKGETSIZE64 #include /* for BLKGETSIZE64 */ #endif #include -#endif #include #include #include #include -#include "mount_utils.h" + +#ifdef HAVE_SELINUX +#include +#endif + +#define MAX_HW_SECTORS_KB_PATH "queue/max_hw_sectors_kb" +#define MAX_SECTORS_KB_PATH "queue/max_sectors_kb" +#define SCHEDULER_PATH "queue/scheduler" +#define STRIPE_CACHE_SIZE "md/stripe_cache_size" + +#define DEFAULT_SCHEDULER "deadline" extern char *progname; @@ -86,8 +93,151 @@ extern char *progname; #define DUMMY_FILE_NAME_LEN 25 #define EXT3_DIRENT_SIZE DUMMY_FILE_NAME_LEN +static void append_unique(char *buf, char *prefix, char *key, char *val, + size_t maxbuflen); + +/* + * Concatenate context of the temporary mount point if selinux is enabled + */ +#ifdef HAVE_SELINUX +static void append_context_for_mount(char *mntpt, struct mkfs_opts *mop) +{ + security_context_t fcontext; + + if (getfilecon(mntpt, &fcontext) < 0) { + /* Continuing with default behaviour */ + fprintf(stderr, "%s: Get file context failed : %s\n", + progname, strerror(errno)); + return; + } + + if (fcontext != NULL) { + append_unique(mop->mo_ldd.ldd_mount_opts, + ",", "context", fcontext, + sizeof(mop->mo_ldd.ldd_mount_opts)); + freecon(fcontext); + } +} +#endif + +/* return canonicalized absolute pathname, even if the target file does not + * exist, unlike realpath */ +static char *absolute_path(char *devname) +{ + char buf[PATH_MAX + 1] = ""; + char *path; + char *ptr; + int len; + + path = malloc(sizeof(buf)); + if (path == NULL) + return NULL; + + if (devname[0] != '/') { + if (getcwd(buf, sizeof(buf) - 1) == NULL) { + free(path); + return NULL; + } + len = snprintf(path, sizeof(buf), "%s/%s", buf, devname); + if (len >= sizeof(buf)) { + free(path); + return NULL; + } + } else { + len = snprintf(path, sizeof(buf), "%s", devname); + if (len >= sizeof(buf)) { + free(path); + return NULL; + } + } + + /* truncate filename before calling realpath */ + ptr = strrchr(path, '/'); + if (ptr == NULL) { + free(path); + return NULL; + } + *ptr = '\0'; + if (buf != realpath(path, buf)) { + free(path); + return NULL; + } + /* add the filename back */ + len = snprintf(path, PATH_MAX, "%s/%s", buf, ptr+1); + if (len >= PATH_MAX) { + free(path); + return NULL; + } + return path; +} + +/* Determine if a device is a block device (as opposed to a file) */ +static int is_block(char *devname) +{ + struct stat st; + int ret = 0; + char *devpath; + + devpath = absolute_path(devname); + if (devpath == NULL) { + fprintf(stderr, "%s: failed to resolve path to %s\n", + progname, devname); + return -1; + } + + ret = access(devname, F_OK); + if (ret != 0) { + if (strncmp(devpath, "/dev/", 5) == 0) { + /* nobody sane wants to create a loopback file under + * /dev. Let's just report the device doesn't exist */ + fprintf(stderr, "%s: %s apparently does not exist\n", + progname, devpath); + ret = -1; + goto out; + } + ret = 0; + goto out; + } + ret = stat(devpath, &st); + if (ret != 0) { + fprintf(stderr, "%s: cannot stat %s\n", progname, devpath); + goto out; + } + ret = S_ISBLK(st.st_mode); +out: + free(devpath); + return ret; +} + +static int is_feature_enabled(const char *feature, const char *devpath) +{ + char cmd[PATH_MAX]; + FILE *fp; + char enabled_features[4096] = ""; + int ret = 1; + + snprintf(cmd, sizeof(cmd), "%s -R features %s 2>&1", + DEBUGFS, devpath); + + /* Using popen() instead of run_command() since debugfs does + * not return proper error code if command is not supported */ + fp = popen(cmd, "r"); + if (!fp) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return 0; + } + + ret = fread(enabled_features, 1, sizeof(enabled_features) - 1, fp); + enabled_features[ret] = '\0'; + pclose(fp); + + if (strstr(enabled_features, feature)) + return 1; + return 0; +} + /* Write the server config files */ -int write_local_files(struct mkfs_opts *mop) +int ldiskfs_write_ldd(struct mkfs_opts *mop) { char mntpt[] = "/tmp/mntXXXXXX"; char filepnm[128]; @@ -103,12 +253,21 @@ int write_local_files(struct mkfs_opts *mop) return errno; } + /* + * Append file context to mount options if SE Linux is enabled + */ + #ifdef HAVE_SELINUX + if (is_selinux_enabled() > 0) + append_context_for_mount(mntpt, mop); + #endif + dev = mop->mo_device; if (mop->mo_flags & MO_IS_LOOP) dev = mop->mo_loopdev; ret = mount(dev, mntpt, MT_STR(&mop->mo_ldd), 0, - mop->mo_ldd.ldd_mount_opts); + (mop->mo_mountopts == NULL) ? + "errors=remount-ro" : mop->mo_mountopts); if (ret) { fprintf(stderr, "%s: Unable to mount %s: %s\n", progname, dev, strerror(errno)); @@ -145,6 +304,7 @@ int write_local_files(struct mkfs_opts *mop) if (num < 1 && ferror(filep)) { fprintf(stderr, "%s: Unable to write to file (%s): %s\n", progname, filepnm, strerror(errno)); + fclose(filep); goto out_umnt; } fclose(filep); @@ -156,12 +316,30 @@ out_rmdir: return ret; } -int read_local_files(struct mkfs_opts *mop) +static int readcmd(char *cmd, char *buf, int len) +{ + FILE *fp; + int red; + + fp = popen(cmd, "r"); + if (!fp) + return errno; + + red = fread(buf, 1, len, fp); + pclose(fp); + + /* strip trailing newline */ + if (buf[red - 1] == '\n') + buf[red - 1] = '\0'; + + return (red == 0) ? -ENOENT : 0; +} + +int ldiskfs_read_ldd(char *dev, struct lustre_disk_data *mo_ldd) { char tmpdir[] = "/tmp/dirXXXXXX"; char cmd[PATH_MAX]; char filepnm[128]; - char *dev; FILE *filep; int ret = 0; int cmdsz = sizeof(cmd); @@ -173,8 +351,6 @@ int read_local_files(struct mkfs_opts *mop) return errno; } - dev = mop->mo_device; - /* TODO: it's worth observing the get_mountdata() function that is in mount_utils.c for getting the mountdata out of the filesystem */ @@ -193,27 +369,30 @@ int read_local_files(struct mkfs_opts *mop) if (filep) { size_t num_read; vprint("Reading %s\n", MOUNT_DATA_FILE); - num_read = fread(&mop->mo_ldd, sizeof(mop->mo_ldd), 1, filep); + num_read = fread(mo_ldd, sizeof(*mo_ldd), 1, filep); if (num_read < 1 && ferror(filep)) { fprintf(stderr, "%s: Unable to read from file %s: %s\n", progname, filepnm, strerror(errno)); - goto out_close; } + fclose(filep); } -out_close: - fclose(filep); snprintf(cmd, cmdsz, "rm -rf %s", tmpdir); run_command(cmd, cmdsz); if (ret) verrprint("Failed to read old data (%d)\n", ret); + + /* As long as we at least have the label, we're good to go */ + snprintf(cmd, sizeof(cmd), E2LABEL" %s", dev); + ret = readcmd(cmd, mo_ldd->ldd_svname, sizeof(mo_ldd->ldd_svname) - 1); + return ret; } /* Display the need for the latest e2fsprogs to be installed. make_backfs * indicates if the caller is make_lustre_backfs() or not. */ -void disp_old_e2fsprogs_msg(const char *feature, int make_backfs) +static void disp_old_e2fsprogs_msg(const char *feature, int make_backfs) { static int msg_displayed; @@ -230,7 +409,7 @@ void disp_old_e2fsprogs_msg(const char *feature, int make_backfs) E2FSPROGS, feature); #if !(HAVE_LDISKFSPROGS) fprintf(stderr, "Please install the latest version of e2fsprogs from\n" - "http://downloads.whamcloud.com/public/e2fsprogs/latest/\n" + "https://downloads.hpdd.intel.com/public/e2fsprogs/latest/\n" "to enable this feature.\n"); #endif if (make_backfs) @@ -262,7 +441,7 @@ static int file_in_dev(char *file_name, char *dev_name) pclose(fp); return 1; } - i = fread(debugfs_cmd, 1, sizeof(debugfs_cmd), fp); + i = fread(debugfs_cmd, 1, sizeof(debugfs_cmd) - 1, fp); if (i) { debugfs_cmd[i] = 0; fprintf(stderr, "%s", debugfs_cmd); @@ -321,8 +500,9 @@ static int is_e2fsprogs_feature_supp(const char *feature) fprintf(stderr, "%s: %s\n", progname, strerror(errno)); return 0; } - ret = fread(supp_features, 1, sizeof(supp_features), fp); - fclose(fp); + ret = fread(supp_features, 1, sizeof(supp_features) - 1, fp); + supp_features[ret] = '\0'; + pclose(fp); } if (ret > 0 && strstr(supp_features, strncmp(feature, "-O ", 3) ? feature : feature+3)) @@ -373,14 +553,15 @@ static void append_unique(char *buf, char *prefix, char *key, char *val, strscat(buf, key, maxbuflen); if (val != NULL) { - strscat(buf, "=", maxbuflen); + strscat(buf, "=\"", maxbuflen); strscat(buf, val, maxbuflen); + strscat(buf, "\"", maxbuflen); } } } -static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, - size_t maxbuflen, int user_spec) +static int enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, + size_t maxbuflen, int user_spec) { if (IS_OST(&mop->mo_ldd)) { append_unique(anchor, user_spec ? "," : " -O ", @@ -411,14 +592,24 @@ static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, /* The following options are only valid for ext4-based ldiskfs. * If --backfstype=ext3 is specified, do not enable them. */ if (mop->mo_ldd.ldd_mount_type == LDD_MT_EXT3) - return; + return 0; + + /* Enable quota by default */ + if (is_e2fsprogs_feature_supp("-O quota") == 0) { + append_unique(anchor, ",", "quota", NULL, maxbuflen); + } else { + fatal(); + fprintf(stderr, "\"-O quota\" must be supported by " + "e2fsprogs, please upgrade your e2fsprogs.\n"); + return EINVAL; + } /* Allow files larger than 2TB. Also needs LU-16, but not harmful. */ if (is_e2fsprogs_feature_supp("-O huge_file") == 0) append_unique(anchor, ",", "huge_file", NULL, maxbuflen); /* Enable large block addresses if the LUN is over 2^32 blocks. */ - if (mop->mo_device_sz / (L_BLOCK_SIZE >> 10) >= 0x100002000ULL && + if (mop->mo_device_kb / (L_BLOCK_SIZE >> 10) >= 0x100002000ULL && is_e2fsprogs_feature_supp("-O 64bit") == 0) append_unique(anchor, ",", "64bit", NULL, maxbuflen); @@ -430,13 +621,15 @@ static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, append_unique(anchor, ",", "flex_bg", NULL, maxbuflen); - if (IS_OST(&mop->mo_ldd)) { + if (IS_OST(&mop->mo_ldd) && + strstr(mop->mo_mkfsopts, "-G") == NULL) { snprintf(tmp_buf, sizeof(tmp_buf), " -G %u", (1 << 20) / L_BLOCK_SIZE); strscat(anchor, tmp_buf, maxbuflen); } } /* Don't add any more "-O" options here, see last comment above */ + return 0; } /** @@ -448,6 +641,7 @@ static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, */ static char *moveopts_to_end(char *start) { + size_t len; char save[512]; char *end, *idx; @@ -460,9 +654,13 @@ static char *moveopts_to_end(char *start) while (*end != ' ' && *end != '\0') ++end; + len = end - start; + if (len >= sizeof(save)) + len = sizeof(save) - 1; + /* save options */ - strncpy(save, start, end - start); - save[end - start] = '\0'; + strncpy(save, start, len); + save[len] = '\0'; /* move remaining options up front */ if (*end) @@ -479,9 +677,9 @@ static char *moveopts_to_end(char *start) } /* Build fs according to type */ -int make_lustre_backfs(struct mkfs_opts *mop) +int ldiskfs_make_lustre(struct mkfs_opts *mop) { - __u64 device_sz = mop->mo_device_sz, block_count = 0; + __u64 device_kb = mop->mo_device_kb, block_count = 0; char mkfs_cmd[PATH_MAX]; char buf[64]; char *start; @@ -490,26 +688,26 @@ int make_lustre_backfs(struct mkfs_opts *mop) size_t maxbuflen; if (!(mop->mo_flags & MO_IS_LOOP)) { - mop->mo_device_sz = get_device_size(mop->mo_device); + mop->mo_device_kb = get_device_size(mop->mo_device); - if (mop->mo_device_sz == 0) + if (mop->mo_device_kb == 0) return ENODEV; /* Compare to real size */ - if (device_sz == 0 || device_sz > mop->mo_device_sz) - device_sz = mop->mo_device_sz; + if (device_kb == 0 || device_kb > mop->mo_device_kb) + device_kb = mop->mo_device_kb; else - mop->mo_device_sz = device_sz; + mop->mo_device_kb = device_kb; } - if (mop->mo_device_sz != 0) { - if (mop->mo_device_sz < 8096){ + if (mop->mo_device_kb != 0) { + if (mop->mo_device_kb < 32384) { fprintf(stderr, "%s: size of filesystem must be larger " - "than 8MB, but is set to %lldKB\n", - progname, (long long)mop->mo_device_sz); + "than 32MB, but is set to %lldKB\n", + progname, (long long)mop->mo_device_kb); return EINVAL; } - block_count = mop->mo_device_sz / (L_BLOCK_SIZE >> 10); + block_count = mop->mo_device_kb / (L_BLOCK_SIZE >> 10); /* If the LUN size is just over 2^32 blocks, limit the * filesystem size to 2^32-1 blocks to avoid problems with * ldiskfs/mkfs not handling this size. Bug 22906 */ @@ -523,36 +721,58 @@ int make_lustre_backfs(struct mkfs_opts *mop) long inode_size = 0; /* Journal size in MB */ - if (strstr(mop->mo_mkfsopts, "-J") == NULL) { + if (strstr(mop->mo_mkfsopts, "-J") == NULL && + device_kb > 1024 * 1024) { /* Choose our own default journal size */ - long journal_sz = 0, max_sz; - if (device_sz > 1024 * 1024) /* 1GB */ - journal_sz = (device_sz / 102400) * 4; - /* cap journal size at 1GB */ - if (journal_sz > 1024L) - journal_sz = 1024L; - /* man mkfs.ext3 */ - max_sz = (102400 * L_BLOCK_SIZE) >> 20; /* 400MB */ - if (journal_sz > max_sz) - journal_sz = max_sz; - if (journal_sz) { - sprintf(buf, " -J size=%ld", journal_sz); + long journal_mb = 0, max_mb; + + /* cap journal size at 4GB for MDT, + * leave it at 400MB for OSTs. */ + if (IS_MDT(&mop->mo_ldd)) + max_mb = 4096; + else if (IS_OST(&mop->mo_ldd)) + max_mb = 400; + else /* Use mke2fs default size for MGS */ + max_mb = 0; + + /* Use at most 4% of device for journal */ + journal_mb = device_kb * 4 / (1024 * 100); + if (journal_mb > max_mb) + journal_mb = max_mb; + + if (journal_mb) { + sprintf(buf, " -J size=%ld", journal_mb); strscat(mop->mo_mkfsopts, buf, sizeof(mop->mo_mkfsopts)); } } - /* Inode size (for extended attributes). The LOV EA size is - * 32 (EA hdr) + 32 (lov_mds_md) + stripes * 24 (lov_ost_data), - * and we want some margin above that for ACLs, other EAs... */ + /* Inode size includes: + * ldiskfs inode size: 156 + * extended attributes size, including: + * ext4_xattr_header: 32 + * LOV EA size: 32(lov_mds_md) + + * stripes * 24(lov_ost_data) + + * 16(xattr_entry) + 3(lov) + * LMA EA size: 24(lustre_mdt_attrs) + + * 16(xattr_entry) + 3(lma) + * link EA size: 24(link_ea_header) + 18(link_ea_entry) + + * (filename) + 16(xattr_entry) + 4(link) + * and some margin for 4-byte alignment, ACLs and other EAs. + * + * If we say the average filename length is about 32 bytes, + * the calculation looks like: + * 156 + 32 + (32+24*N+19) + (24+19) + (24+18+~32+20) + other <= + * 512*2^m, {m=0,1,2,3} + */ if (strstr(mop->mo_mkfsopts, "-I") == NULL) { if (IS_MDT(&mop->mo_ldd)) { - if (mop->mo_stripe_count > 72) + if (mop->mo_stripe_count > 69) inode_size = 512; /* bz 7241 */ /* see also "-i" below for EA blocks */ - else if (mop->mo_stripe_count > 32) + else if (mop->mo_stripe_count > 26) inode_size = 2048; - else if (mop->mo_stripe_count > 10) + else if (mop->mo_stripe_count > 5) inode_size = 1024; else inode_size = 512; @@ -582,7 +802,7 @@ int make_lustre_backfs(struct mkfs_opts *mop) if (IS_MDT(&mop->mo_ldd)) { bytes_per_inode = inode_size + 1536; - if (mop->mo_stripe_count > 72) { + if (mop->mo_stripe_count > 69) { int extra = mop->mo_stripe_count * 24; extra = ((extra - 1) | 4095) + 1; bytes_per_inode += extra; @@ -594,18 +814,18 @@ int make_lustre_backfs(struct mkfs_opts *mop) * this, but it is impossible to know in advance. */ if (IS_OST(&mop->mo_ldd)) { /* OST > 16TB assume average file size 1MB */ - if (device_sz > (16ULL << 30)) + if (device_kb > (16ULL << 30)) bytes_per_inode = 1024 * 1024; /* OST > 4TB assume average file size 512kB */ - else if (device_sz > (4ULL << 30)) + else if (device_kb > (4ULL << 30)) bytes_per_inode = 512 * 1024; /* OST > 1TB assume average file size 256kB */ - else if (device_sz > (1ULL << 30)) + else if (device_kb > (1ULL << 30)) bytes_per_inode = 256 * 1024; /* OST > 10GB assume average file size 64kB, * plus a bit so that inodes will fit into a * 256x flex_bg without overflowing */ - else if (device_sz > (10ULL << 20)) + else if (device_kb > (10ULL << 20)) bytes_per_inode = 69905; } @@ -632,13 +852,15 @@ int make_lustre_backfs(struct mkfs_opts *mop) start = moveopts_to_end(start); maxbuflen = sizeof(mop->mo_mkfsopts) - (start - mop->mo_mkfsopts) - strlen(start); - enable_default_ext4_features(mop, start, maxbuflen, 1); + ret = enable_default_ext4_features(mop, start, maxbuflen, 1); } else { start = mop->mo_mkfsopts + strlen(mop->mo_mkfsopts), maxbuflen = sizeof(mop->mo_mkfsopts) - strlen(mop->mo_mkfsopts); - enable_default_ext4_features(mop, start, maxbuflen, 0); + ret = enable_default_ext4_features(mop, start, maxbuflen, 0); } + if (ret) + return ret; /* end handle -O mkfs options */ /* start handle -E mkfs options */ @@ -664,8 +886,8 @@ int make_lustre_backfs(struct mkfs_opts *mop) * descriptor blocks, but leave one block for the superblock. * Only useful for filesystems with < 2^32 blocks due to resize * limitations. */ - if (IS_OST(&mop->mo_ldd) && mop->mo_device_sz > 100 * 1024 && - mop->mo_device_sz * 1024 / L_BLOCK_SIZE <= 0xffffffffULL) { + if (IS_OST(&mop->mo_ldd) && mop->mo_device_kb > 100 * 1024 && + mop->mo_device_kb * 1024 / L_BLOCK_SIZE <= 0xffffffffULL) { unsigned group_blocks = L_BLOCK_SIZE * 8; unsigned desc_per_block = L_BLOCK_SIZE / 32; unsigned resize_blks; @@ -704,8 +926,8 @@ int make_lustre_backfs(struct mkfs_opts *mop) vprint("formatting backing filesystem %s on %s\n", MT_STR(&mop->mo_ldd), dev); - vprint("\ttarget name %s\n", mop->mo_ldd.ldd_svname); - vprint("\t4k blocks "LPU64"\n", block_count); + vprint("\ttarget name %s\n", mop->mo_ldd.ldd_svname); + vprint("\t4k blocks %ju\n", (uintmax_t)block_count); vprint("\toptions %s\n", mop->mo_mkfsopts); /* mkfs_cmd's trailing space is important! */ @@ -713,7 +935,8 @@ int make_lustre_backfs(struct mkfs_opts *mop) strscat(mkfs_cmd, " ", sizeof(mkfs_cmd)); strscat(mkfs_cmd, dev, sizeof(mkfs_cmd)); if (block_count != 0) { - sprintf(buf, " "LPU64, block_count); + snprintf(buf, sizeof(buf), " %ju", + (uintmax_t)block_count); strscat(mkfs_cmd, buf, sizeof(mkfs_cmd)); } @@ -727,8 +950,7 @@ int make_lustre_backfs(struct mkfs_opts *mop) } int ldiskfs_prepare_lustre(struct mkfs_opts *mop, - char *default_mountopts, int default_len, - char *always_mountopts, int always_len) + char *wanted_mountopts, size_t len) { struct lustre_disk_data *ldd = &mop->mo_ldd; int ret; @@ -741,85 +963,393 @@ int ldiskfs_prepare_lustre(struct mkfs_opts *mop, mop->mo_flags |= MO_IS_LOOP; } - strscat(default_mountopts, ",errors=remount-ro", default_len); if (IS_MDT(ldd) || IS_MGS(ldd)) - strscat(always_mountopts, ",user_xattr", always_len); + strscat(wanted_mountopts, ",user_xattr", len); return 0; } -/* return canonicalized absolute pathname, even if the target file does not - * exist, unlike realpath */ -static char *absolute_path(char *devname) +int ldiskfs_fix_mountopts(struct mkfs_opts *mop, char *mountopts, size_t len) { - char buf[PATH_MAX + 1]; - char *path; - char *ptr; + if (strstr(mountopts, "errors=") == NULL) + strscat(mountopts, ",errors=remount-ro", len); - path = malloc(PATH_MAX + 1); - if (path == NULL) - return NULL; + return 0; +} - if (devname[0] != '/') { - if (getcwd(buf, sizeof(buf) - 1) == NULL) - return NULL; - strcat(buf, "/"); - strcat(buf, devname); - } else { - strcpy(buf, devname); +static int read_file(const char *path, char *buf, int size) +{ + FILE *fd; + + fd = fopen(path, "r"); + if (fd == NULL) + return errno; + + if (fgets(buf, size, fd) == NULL) { + fprintf(stderr, "reading from %s: %s", path, strerror(errno)); + fclose(fd); + return 1; } - /* truncate filename before calling realpath */ - ptr = strrchr(buf, '/'); - if (ptr == NULL) { - free(path); - return NULL; + fclose(fd); + + /* strip trailing newline */ + size = strlen(buf); + if (buf[size - 1] == '\n') + buf[size - 1] = '\0'; + + return 0; +} + +static int write_file(const char *path, const char *buf) +{ + int fd, rc; + + fd = open(path, O_WRONLY); + if (fd < 0) + return errno; + + rc = write(fd, buf, strlen(buf)); + close(fd); + + return rc < 0 ? errno : 0; +} + +static int set_blockdev_scheduler(const char *path, const char *scheduler) +{ + char buf[PATH_MAX], *s, *e, orig_sched[50]; + int rc; + + /* Before setting the scheduler, we need to check to see if it's + * already set to "noop". If it is, we don't want to override + * that setting. If it's set to anything other than "noop", set + * the scheduler to what has been passed in. */ + + rc = read_file(path, buf, sizeof(buf)); + if (rc) { + if (verbose) + fprintf(stderr, "%s: cannot open '%s': %s\n", + progname, path, strerror(errno)); + return rc; } - *ptr = '\0'; - if (path != realpath(buf, path)) { - free(path); - return NULL; + + /* The expected format of buf: noop anticipatory deadline [cfq] */ + s = strchr(buf, '['); + e = strchr(buf, ']'); + + /* If the format is not what we expect. Play it safe and error out. */ + if (s == NULL || e == NULL) { + if (verbose) + fprintf(stderr, "%s: cannot parse scheduler " + "options for '%s'\n", progname, path); + return -EINVAL; } - /* add the filename back */ - strcat(path, "/"); - strcat(path, ptr + 1); - return path; + + snprintf(orig_sched, e - s, "%s", s + 1); + + if (strcmp(orig_sched, "noop") == 0 || + strcmp(orig_sched, scheduler) == 0) + return 0; + + rc = write_file(path, scheduler); + if (rc) { + if (verbose) + fprintf(stderr, "%s: cannot set scheduler on " + "'%s': %s\n", progname, path, + strerror(errno)); + return rc; + } else { + fprintf(stderr, "%s: change scheduler of %s from %s to %s\n", + progname, path, orig_sched, scheduler); + } + + return rc; } -/* Determine if a device is a block device (as opposed to a file) */ -int is_block(char* devname) +/* This is to tune the kernel for good SCSI performance. + * For that we set the value of /sys/block/{dev}/queue/max_sectors_kb + * to the value of /sys/block/{dev}/queue/max_hw_sectors_kb */ +static int set_blockdev_tunables(char *source, struct mount_opts *mop) { - struct stat st; - int ret = 0; - char *devpath; + glob_t glob_info = { 0 }; + struct stat stat_buf; + char *chk_major, *chk_minor; + char *savept = NULL, *dev; + char *ret_path; + char buf[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'}; + char real_path[PATH_MAX] = {'\0'}; + int i, rc = 0; + int major, minor; + char *slave = NULL; + + if (!source) + return -EINVAL; + + ret_path = realpath(source, real_path); + if (ret_path == NULL) { + if (verbose) + fprintf(stderr, "warning: %s: cannot resolve: %s\n", + source, strerror(errno)); + return -EINVAL; + } - devpath = absolute_path(devname); - if (devpath == NULL) { - fprintf(stderr, "%s: failed to resolve path to %s\n", - progname, devname); - return -1; + if (strncmp(real_path, "/dev/loop", 9) == 0) + return 0; + + if ((real_path[0] != '/') && (strpbrk(real_path, ",:") != NULL)) + return 0; + + snprintf(path, sizeof(path), "/sys/block%s", real_path + 4); + if (access(path, X_OK) == 0) + goto set_params; + + /* The name of the device say 'X' specified in /dev/X may not + * match any entry under /sys/block/. In that case we need to + * match the major/minor number to find the entry under + * sys/block corresponding to /dev/X */ + + /* Don't chop tail digit on /dev/mapper/xxx, LU-478 */ + if (strncmp(real_path, "/dev/mapper", 11) != 0) { + dev = real_path + strlen(real_path); + while (--dev > real_path && isdigit(*dev)) + *dev = 0; + + if (strncmp(real_path, "/dev/md_", 8) == 0) + *dev = 0; } - ret = access(devname, F_OK); - if (ret != 0) { - if (strncmp(devpath, "/dev/", 5) == 0) { - /* nobody sane wants to create a loopback file under - * /dev. Let's just report the device doesn't exist */ - fprintf(stderr, "%s: %s apparently does not exist\n", - progname, devpath); - ret = -1; - goto out; + rc = stat(real_path, &stat_buf); + if (rc) { + if (verbose) + fprintf(stderr, "warning: %s, device %s stat failed\n", + strerror(errno), real_path); + return rc; + } + + major = major(stat_buf.st_rdev); + minor = minor(stat_buf.st_rdev); + rc = glob("/sys/block/*", GLOB_NOSORT, NULL, &glob_info); + if (rc) { + if (verbose) + fprintf(stderr, "warning: failed to read entries under " + "/sys/block\n"); + globfree(&glob_info); + return rc; + } + + for (i = 0; i < glob_info.gl_pathc; i++){ + snprintf(path, sizeof(path), "%s/dev", glob_info.gl_pathv[i]); + + rc = read_file(path, buf, sizeof(buf)); + if (rc) + continue; + + if (buf[strlen(buf) - 1] == '\n') + buf[strlen(buf) - 1] = '\0'; + + chk_major = strtok_r(buf, ":", &savept); + chk_minor = savept; + if (chk_major != NULL && major == atoi(chk_major) && + chk_minor != NULL && minor == atoi(chk_minor)) + break; + } + + if (i == glob_info.gl_pathc) { + if (verbose) + fprintf(stderr,"warning: device %s does not match any " + "entry under /sys/block\n", real_path); + globfree(&glob_info); + return -EINVAL; + } + + /* Chop off "/dev" from path we found */ + path[strlen(glob_info.gl_pathv[i])] = '\0'; + globfree(&glob_info); + +set_params: + if (strncmp(real_path, "/dev/md", 7) == 0) { + snprintf(real_path, sizeof(real_path), "%s/%s", path, + STRIPE_CACHE_SIZE); + + rc = read_file(real_path, buf, sizeof(buf)); + if (rc) { + if (verbose) + fprintf(stderr, "warning: opening %s: %s\n", + real_path, strerror(errno)); + return 0; } - ret = 0; - goto out; + + if (atoi(buf) >= mop->mo_md_stripe_cache_size) + return 0; + + if (strlen(buf) - 1 > 0) { + snprintf(buf, sizeof(buf), "%d", + mop->mo_md_stripe_cache_size); + rc = write_file(real_path, buf); + if (rc != 0 && verbose) + fprintf(stderr, "warning: opening %s: %s\n", + real_path, strerror(errno)); + } + /* Return since raid and disk tunables are different */ + return rc; } - ret = stat(devpath, &st); - if (ret != 0) { - fprintf(stderr, "%s: cannot stat %s\n", progname, devpath); - goto out; + + snprintf(real_path, sizeof(real_path), "%s/%s", path, + MAX_HW_SECTORS_KB_PATH); + rc = read_file(real_path, buf, sizeof(buf)); + if (rc) { + if (verbose) + fprintf(stderr, "warning: opening %s: %s\n", + real_path, strerror(errno)); + /* No MAX_HW_SECTORS_KB_PATH isn't necessary an + * error for some device. */ + goto subdevs; } - ret = S_ISBLK(st.st_mode); -out: - free(devpath); + + if (strlen(buf) - 1 > 0) { + char oldbuf[32] = "", *end = NULL; + unsigned long long oldval, newval; + + snprintf(real_path, sizeof(real_path), "%s/%s", path, + MAX_SECTORS_KB_PATH); + rc = read_file(real_path, oldbuf, sizeof(oldbuf)); + /* Only set new parameter if different from the old one. */ + if (rc != 0 || strcmp(oldbuf, buf) == 0) { + /* No MAX_SECTORS_KB_PATH isn't necessary an + * error for some device. */ + goto subdevs; + } + + newval = strtoull(buf, &end, 0); + if (newval == 0 || newval == ULLONG_MAX || end == buf) + goto subdevs; + + /* Don't increase IO request size limit past 16MB. It is about + * PTLRPC_MAX_BRW_SIZE, but that isn't in a public header. + * Note that even though the block layer allows larger values, + * setting max_sectors_kb = 32768 causes crashes (LU-6974). */ + if (newval > 16 * 1024) { + newval = 16 * 1024; + snprintf(buf, sizeof(buf), "%llu", newval); + } + + oldval = strtoull(oldbuf, &end, 0); + /* Don't shrink the current limit. */ + if (oldval != ULLONG_MAX && newval <= oldval) + goto subdevs; + + rc = write_file(real_path, buf); + if (rc != 0) { + if (verbose) + fprintf(stderr, "warning: writing to %s: %s\n", + real_path, strerror(errno)); + /* No MAX_SECTORS_KB_PATH isn't necessary an + * error for some device. */ + goto subdevs; + } + fprintf(stderr, "%s: increased %s from %s to %s\n", + progname, real_path, oldbuf, buf); + } + +subdevs: + /* Purposely ignore errors reported from set_blockdev_scheduler. + * The worst that will happen is a block device with an "incorrect" + * scheduler. */ + snprintf(real_path, sizeof(real_path), "%s/%s", path, SCHEDULER_PATH); + set_blockdev_scheduler(real_path, DEFAULT_SCHEDULER); + + /* if device is multipath device, tune its slave devices */ + glob_info.gl_pathc = 0; + glob_info.gl_offs = 0; + snprintf(real_path, sizeof(real_path), "%s/slaves/*", path); + rc = glob(real_path, GLOB_NOSORT, NULL, &glob_info); + + for (i = 0; rc == 0 && i < glob_info.gl_pathc; i++) { + slave = basename(glob_info.gl_pathv[i]); + snprintf(real_path, sizeof(real_path), "/dev/%s", slave); + rc = set_blockdev_tunables(real_path, mop); + } + + if (rc == GLOB_NOMATCH) { + /* no slave device is not an error */ + rc = 0; + } else if (rc && verbose) { + if (slave == NULL) { + fprintf(stderr, "warning: %s, failed to read" + " entries under %s/slaves\n", + strerror(errno), path); + } else { + fprintf(stderr, "unable to set tunables for" + " slave device %s (slave would be" + " unable to handle IO request from" + " master %s)\n", + real_path, source); + } + } + globfree(&glob_info); + + return rc; +} + +int ldiskfs_tune_lustre(char *dev, struct mount_opts *mop) +{ + return set_blockdev_tunables(dev, mop); +} + +int ldiskfs_label_lustre(struct mount_opts *mop) +{ + char label_cmd[PATH_MAX]; + int rc; + + snprintf(label_cmd, sizeof(label_cmd), + TUNE2FS" -f -L '%s' '%s' >/dev/null 2>&1", + mop->mo_ldd.ldd_svname, mop->mo_source); + rc = run_command(label_cmd, sizeof(label_cmd)); + + return rc; +} + +/* Enable quota accounting */ +int ldiskfs_enable_quota(struct mkfs_opts *mop) +{ + char *dev; + char cmd[512]; + int cmdsz = sizeof(cmd), ret; + + if (is_e2fsprogs_feature_supp("-O quota") != 0) { + fprintf(stderr, "%s: \"-O quota\" is is not supported by " + "current e2fsprogs\n", progname); + return EINVAL; + } + + dev = mop->mo_device; + if (mop->mo_flags & MO_IS_LOOP) + dev = mop->mo_loopdev; + + /* Quota feature is already enabled? */ + if (is_feature_enabled("quota", dev)) { + vprint("Quota feature is already enabled.\n"); + return 0; + } + + /* Turn on quota feature by "tune2fs -O quota" */ + snprintf(cmd, cmdsz, "%s -O quota %s", TUNE2FS, dev); + ret = run_command(cmd, cmdsz); + if (ret) + fprintf(stderr, "command:%s (%d)", cmd, ret); + return ret; } +int ldiskfs_init(void) +{ + /* Required because full path to DEBUGFS is not specified */ + setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0); + + return 0; +} + +void ldiskfs_fini(void) +{ + return; +} +