Whamcloud - gitweb
LU-14583 llapi: handle symlinks in llapi_file_get_stripe()
[fs/lustre-release.git] / lustre / utils / liblustreapi.c
index 92cb3d3..14263ac 100644 (file)
@@ -27,7 +27,6 @@
  */
 /*
  * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
  *
  * lustre/utils/liblustreapi.c
  *
 static int llapi_msg_level = LLAPI_MSG_MAX;
 const char *liblustreapi_cmd;
 
-char *mdt_hash_name[] = { "none",
-                         LMV_HASH_NAME_ALL_CHARS,
-                         LMV_HASH_NAME_FNV_1A_64,
-                         LMV_HASH_NAME_CRUSH,
-};
-
 struct lustre_foreign_type lu_foreign_types[] = {
        {.lft_type = LU_FOREIGN_TYPE_NONE, .lft_name = "none"},
-       {.lft_type = LU_FOREIGN_TYPE_DAOS, .lft_name = "daos"},
+       {.lft_type = LU_FOREIGN_TYPE_SYMLINK, .lft_name = "symlink"},
        /* must be the last element */
        {.lft_type = LU_FOREIGN_TYPE_UNKNOWN, .lft_name = NULL}
        /* array max dimension must be <= UINT32_MAX */
@@ -969,6 +962,8 @@ static inline void param2lmu(struct lmv_user_md *lmu,
        lmu->lum_stripe_count = param->lsp_stripe_count;
        lmu->lum_stripe_offset = param->lsp_stripe_offset;
        lmu->lum_hash_type = param->lsp_stripe_pattern;
+       lmu->lum_max_inherit = param->lsp_max_inherit;
+       lmu->lum_max_inherit_rr = param->lsp_max_inherit_rr;
        if (param->lsp_pool != NULL)
                strncpy(lmu->lum_pool_name, param->lsp_pool, LOV_MAXPOOLNAME);
        if (param->lsp_is_specific) {
@@ -1093,6 +1088,9 @@ int llapi_dir_create(const char *name, mode_t mode,
        data.ioc_inlbuf2 = (char *)lmu;
        data.ioc_inllen2 = lmu_size;
        data.ioc_type = mode;
+       if (param->lsp_is_create)
+               /* borrow obdo1.o_flags to store this flag */
+               data.ioc_obdo1.o_flags = OBD_FL_OBDMDEXISTS;
        rc = llapi_ioctl_pack(&data, &buf, sizeof(rawbuf));
        if (rc) {
                llapi_error(LLAPI_MSG_ERROR, rc,
@@ -1241,44 +1239,6 @@ int llapi_dir_create_pool(const char *name, int mode, int stripe_offset,
        return llapi_dir_create(name, mode, &param);
 }
 
-int llapi_direntry_remove(char *dname)
-{
-       char *dirpath = NULL;
-       char *namepath = NULL;
-       char *dir;
-       char *filename;
-       int fd = -1;
-       int rc = 0;
-
-       dirpath = strdup(dname);
-       namepath = strdup(dname);
-       if (!dirpath || !namepath)
-               return -ENOMEM;
-
-       filename = basename(namepath);
-
-       dir = dirname(dirpath);
-
-       fd = open(dir, O_DIRECTORY | O_RDONLY);
-       if (fd < 0) {
-               rc = -errno;
-               llapi_error(LLAPI_MSG_ERROR, rc, "unable to open '%s'",
-                           filename);
-               goto out;
-       }
-
-       if (ioctl(fd, LL_IOC_REMOVE_ENTRY, filename))
-               llapi_error(LLAPI_MSG_ERROR, errno,
-                           "error on ioctl %#lx for '%s' (%d)",
-                           (long)LL_IOC_LMV_SETSTRIPE, filename, fd);
-out:
-       free(dirpath);
-       free(namepath);
-       if (fd != -1)
-               close(fd);
-       return rc;
-}
-
 /*
  * Find the fsname, the full path, and/or an open fd.
  * Either the fsname or path must not be NULL
@@ -1289,7 +1249,7 @@ int get_root_path(int want, char *fsname, int *outfd, char *path, int index)
        char buf[PATH_MAX], mntdir[PATH_MAX];
        char *ptr, *ptr_end;
        FILE *fp;
-       int idx = 0, len = 0, mntlen, fd;
+       int idx = 0, mntlen = 0, fd;
        int rc = -ENODEV;
        int fsnamelen, mountlen;
 
@@ -1345,16 +1305,19 @@ int get_root_path(int want, char *fsname, int *outfd, char *path, int index)
                        rc = 0;
                        break;
                /* Otherwise find the longest matching path */
-               } else if ((strlen(path) >= mntlen) && (mntlen >= len) &&
+               } else if ((strlen(path) >= mntlen) &&
                           (strncmp(mnt.mnt_dir, path, mntlen) == 0)) {
+                       /* check the path format */
+                       if (strlen(path) > mntlen && path[mntlen] != '/')
+                               continue;
                        strncpy(mntdir, mnt.mnt_dir, sizeof(mntdir) - 1);
                        mntdir[sizeof(mntdir) - 1] = '\0';
-                       len = mntlen;
                        if ((want & WANT_FSNAME) && fsname != NULL) {
                                strncpy(fsname, ptr, mountlen);
                                fsname[mountlen] = '\0';
                        }
                        rc = 0;
+                       break;
                }
        }
        endmntent(fp);
@@ -1362,8 +1325,8 @@ int get_root_path(int want, char *fsname, int *outfd, char *path, int index)
        /* Found it */
        if (rc == 0) {
                if ((want & WANT_PATH) && path != NULL) {
-                       strncpy(path, mntdir, PATH_MAX);
-                       path[strlen(mntdir)] = '\0';
+                       strncpy(path, mntdir, mntlen);
+                       path[mntlen] = '\0';
                }
                if (want & WANT_FD) {
                        fd = open(mntdir, O_RDONLY | O_DIRECTORY | O_NONBLOCK);
@@ -1765,7 +1728,7 @@ err:
        return rc;
 }
 
-typedef int (semantic_func_t)(char *path, DIR *parent, DIR **d,
+typedef int (semantic_func_t)(char *path, int p, int *d,
                              void *data, struct dirent64 *de);
 
 #define OBD_NOT_FOUND           (-1)
@@ -1835,7 +1798,7 @@ static int common_param_init(struct find_param *param, char *path)
        return 0;
 }
 
-static int cb_common_fini(char *path, DIR *parent, DIR **dirp, void *data,
+static int cb_common_fini(char *path, int p, int *dp, void *data,
                          struct dirent64 *de)
 {
        struct find_param *param = data;
@@ -1845,26 +1808,27 @@ static int cb_common_fini(char *path, DIR *parent, DIR **dirp, void *data,
 }
 
 /* set errno upon failure */
-static DIR *opendir_parent(const char *path)
+static int open_parent(const char *path)
 {
        char *path_copy;
        char *parent_path;
-       DIR *parent;
+       int parent;
 
        path_copy = strdup(path);
        if (path_copy == NULL)
-               return NULL;
+               return -1;
 
        parent_path = dirname(path_copy);
-       parent = opendir(parent_path);
+       parent = open(parent_path, O_RDONLY|O_NDELAY|O_DIRECTORY);
        free(path_copy);
 
        return parent;
 }
 
-static int cb_get_dirstripe(char *path, DIR *d, struct find_param *param)
+static int cb_get_dirstripe(char *path, int *d, struct find_param *param)
 {
        int ret;
+       bool did_nofollow = false;
 
 again:
        param->fp_lmv_md->lum_stripe_count = param->fp_lmv_stripe_count;
@@ -1873,7 +1837,36 @@ again:
        else
                param->fp_lmv_md->lum_magic = LMV_MAGIC_V1;
 
-       ret = ioctl(dirfd(d), LL_IOC_LMV_GETSTRIPE, param->fp_lmv_md);
+       ret = ioctl(*d, LL_IOC_LMV_GETSTRIPE, param->fp_lmv_md);
+
+       /* if ENOTTY likely to be a fake symlink, so try again after
+        * new open() with O_NOFOLLOW, but only once to prevent any
+        * loop like for the path of a file/dir not on Lustre !!
+        */
+       if (ret < 0 && errno == ENOTTY && !did_nofollow) {
+               int fd, ret2;
+
+               did_nofollow = true;
+               fd = open(path, O_RDONLY | O_NOFOLLOW);
+               if (fd < 0) {
+                       /* restore original errno */
+                       errno = ENOTTY;
+                       return ret;
+               }
+
+               /* close original fd and set new */
+               close(*d);
+               *d = fd;
+               ret2 = ioctl(fd, LL_IOC_LMV_GETSTRIPE, param->fp_lmv_md);
+               if (ret2 < 0 && errno != E2BIG) {
+                       /* restore original errno */
+                       errno = ENOTTY;
+                       return ret;
+               }
+               /* LMV is ok or need to handle E2BIG case now */
+               ret = ret2;
+       }
+
        if (errno == E2BIG && ret != 0) {
                int stripe_count;
                int lmv_size;
@@ -2000,21 +1993,44 @@ int get_lmd_info_fd(const char *path, int parent_fd, int dir_fd,
                 * LL_IOC_LOV_GETSTRIPE returns only struct lov_user_md.
                 */
                if (type == GET_LMD_INFO)
-                       cmd = use_old_ioctl ? LL_IOC_MDC_GETINFO_OLD :
-                                             LL_IOC_MDC_GETINFO;
+                       cmd = use_old_ioctl ? LL_IOC_MDC_GETINFO_V1 :
+                                             LL_IOC_MDC_GETINFO_V2;
                else
                        cmd = LL_IOC_LOV_GETSTRIPE;
 
 retry_getinfo:
                ret = ioctl(dir_fd, cmd, lmdbuf);
-               if (ret < 0 && errno == ENOTTY && cmd == LL_IOC_MDC_GETINFO) {
-                       cmd = LL_IOC_MDC_GETINFO_OLD;
+               if (ret < 0 && errno == ENOTTY &&
+                   cmd == LL_IOC_MDC_GETINFO_V2) {
+                       cmd = LL_IOC_MDC_GETINFO_V1;
                        use_old_ioctl = true;
                        goto retry_getinfo;
                }
 
-               if (cmd == LL_IOC_MDC_GETINFO_OLD && !ret)
+               if (cmd == LL_IOC_MDC_GETINFO_V1 && !ret)
                        ret = convert_lmdbuf_v1v2(lmdbuf, lmdlen);
+
+               if (ret < 0 && errno == ENOTTY && type == GET_LMD_STRIPE) {
+                       int dir_fd2;
+
+                       /* retry ioctl() after new open() with O_NOFOLLOW
+                        * just in case it could be a fake symlink
+                        * need using a new open() as dir_fd is being closed
+                        * by caller
+                        */
+
+                       dir_fd2 = open(path, O_RDONLY | O_NDELAY | O_NOFOLLOW);
+                       if (dir_fd2 < 0) {
+                               /* return original error */
+                               errno = ENOTTY;
+                       } else {
+                               ret = ioctl(dir_fd2, cmd, lmdbuf);
+                               /* pass new errno or success back to caller */
+
+                               close(dir_fd2);
+                       }
+               }
+
        } else if (parent_fd >= 0) {
                const char *fname = strrchr(path, '/');
 
@@ -2039,21 +2055,21 @@ retry_getinfo:
                        errno = EINVAL;
                else {
                        if (type == GET_LMD_INFO)
-                               cmd = use_old_ioctl ? IOC_MDC_GETFILEINFO_OLD :
-                                                     IOC_MDC_GETFILEINFO;
+                               cmd = use_old_ioctl ? IOC_MDC_GETFILEINFO_V1 :
+                                                     IOC_MDC_GETFILEINFO_V2;
                        else
                                cmd = IOC_MDC_GETFILESTRIPE;
 
 retry_getfileinfo:
                        ret = ioctl(parent_fd, cmd, lmdbuf);
                        if (ret < 0 && errno == ENOTTY &&
-                           cmd == IOC_MDC_GETFILEINFO) {
-                               cmd = IOC_MDC_GETFILEINFO_OLD;
+                           cmd == IOC_MDC_GETFILEINFO_V2) {
+                               cmd = IOC_MDC_GETFILEINFO_V1;
                                use_old_ioctl = true;
                                goto retry_getfileinfo;
                        }
 
-                       if (cmd == IOC_MDC_GETFILEINFO_OLD && !ret)
+                       if (cmd == IOC_MDC_GETFILEINFO_V1 && !ret)
                                ret = convert_lmdbuf_v1v2(lmdbuf, lmdlen);
                }
        }
@@ -2097,55 +2113,103 @@ retry_getfileinfo:
        return ret;
 }
 
-static int get_lmd_info(char *path, DIR *parent, DIR *dir, void *lmdbuf,
-                       int lmdlen, enum get_lmd_info_type type)
-{
-       int parent_fd = -1;
-       int dir_fd = -1;
-
-       if (parent)
-               parent_fd = dirfd(parent);
-       if (dir)
-               dir_fd = dirfd(dir);
-
-       return get_lmd_info_fd(path, parent_fd, dir_fd, lmdbuf, lmdlen, type);
-}
-
-static int llapi_semantic_traverse(char *path, int size, DIR *parent,
+static int llapi_semantic_traverse(char *path, int size, int parent,
                                   semantic_func_t sem_init,
                                   semantic_func_t sem_fini, void *data,
                                   struct dirent64 *de)
 {
        struct find_param *param = (struct find_param *)data;
        struct dirent64 *dent;
-       int len, ret;
-       DIR *d, *p = NULL;
+       int len, ret, d, p = -1;
+       DIR *dir = NULL;
 
        ret = 0;
        len = strlen(path);
 
-       d = opendir(path);
-       if (!d && errno != ENOTDIR) {
+       d = open(path, O_RDONLY|O_NDELAY|O_DIRECTORY);
+       /* if an invalid fake dir symlink, opendir() will return EINVAL
+        * instead of ENOTDIR. If a valid but dangling faked or real file/dir
+        * symlink ENOENT will be returned. For a valid/resolved fake or real
+        * file symlink ENOTDIR will be returned as for a regular file.
+        * opendir() will be successful for a  valid and resolved fake or real
+        * dir simlink or a regular dir.
+        */
+       if (d == -1 && errno != ENOTDIR && errno != EINVAL && errno != ENOENT) {
                ret = -errno;
                llapi_error(LLAPI_MSG_ERROR, ret, "%s: Failed to open '%s'",
                            __func__, path);
                return ret;
-       } else if (!d && !parent) {
-               /* ENOTDIR. Open the parent dir. */
-               p = opendir_parent(path);
-               if (!p) {
-                       ret = -errno;
-                       goto out;
+       } else if (d == -1) {
+               if (errno == ENOENT || errno == EINVAL) {
+                       int old_errno = errno;
+
+                       /* try to open with O_NOFOLLOW this will help
+                        * differentiate fake vs real symlinks
+                        * it is ok to not use O_DIRECTORY with O_RDONLY
+                        * and it will prevent the need to deal with ENOTDIR
+                        * error, instead of ELOOP, being returned by recent
+                        * kernels for real symlinks
+                        */
+                       d = open(path, O_RDONLY|O_NDELAY|O_NOFOLLOW);
+                       /* if a dangling real symlink should return ELOOP, or
+                        * again ENOENT if really non-existing path, or E...??
+                        * So return original error. If success or ENOTDIR, path
+                        * is likely to be a fake dir/file symlink, so continue
+                        */
+                       if (d == -1) {
+                               ret =  -old_errno;
+                               goto out;
+                       }
+
+               }
+
+               /* ENOTDIR */
+               if (parent == -1 && d == -1) {
+                       /* Open the parent dir. */
+                       p = open_parent(path);
+                       if (p == -1) {
+                               ret = -errno;
+                               goto out;
+                       }
+               }
+       } else { /* d != -1 */
+               int d2;
+
+               /* try to reopen dir with O_NOFOLLOW just in case of a foreign
+                * symlink dir
+                */
+               d2 = open(path, O_RDONLY|O_NDELAY|O_NOFOLLOW);
+               if (d2 != -1) {
+                       close(d);
+                       d = d2;
+               } else {
+                       /* continue with d */
+                       errno = 0;
                }
        }
 
-       if (sem_init && (ret = sem_init(path, parent ?: p, &d, data, de)))
-               goto err;
+       if (sem_init) {
+               ret = sem_init(path, (parent != -1) ? parent : p, &d, data, de);
+               if (ret)
+                       goto err;
+       }
+
+       if (d == -1)
+               goto out;
+
+       dir = fdopendir(d);
+       if (dir == NULL) {
+               /* ENOTDIR if fake symlink, do not consider it as an error */
+               if (errno != ENOTDIR)
+                       llapi_error(LLAPI_MSG_ERROR, errno,
+                                   "fdopendir() failed");
+               else
+                       errno = 0;
 
-       if (d == NULL)
                goto out;
+       }
 
-       while ((dent = readdir64(d)) != NULL) {
+       while ((dent = readdir64(dir)) != NULL) {
                int rc;
 
                if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
@@ -2154,8 +2218,8 @@ static int llapi_semantic_traverse(char *path, int size, DIR *parent,
                path[len] = 0;
                if ((len + dent->d_reclen + 2) > size) {
                        llapi_err_noerrno(LLAPI_MSG_ERROR,
-                                         "error: %s: string buffer too small",
-                                         __func__);
+                                         "error: %s: string buffer too small for %s",
+                                         __func__, path);
                        break;
                }
                strcat(path, "/");
@@ -2164,8 +2228,8 @@ static int llapi_semantic_traverse(char *path, int size, DIR *parent,
                if (dent->d_type == DT_UNKNOWN) {
                        struct lov_user_mds_data *lmd = param->fp_lmd;
 
-                       rc = get_lmd_info(path, d, NULL, lmd,
-                                         param->fp_lum_size, GET_LMD_INFO);
+                       rc = get_lmd_info_fd(path, d, -1, param->fp_lmd,
+                                            param->fp_lum_size, GET_LMD_INFO);
                        if (rc == 0)
                                dent->d_type = IFTODT(lmd->lmd_stx.stx_mode);
                        else if (ret == 0)
@@ -2206,10 +2270,14 @@ out:
        if (sem_fini)
                sem_fini(path, parent, &d, data, de);
 err:
-       if (d)
-               closedir(d);
-       if (p)
-               closedir(p);
+       if (d != -1) {
+               if (dir)
+                       closedir(dir);
+               else
+                       close(d);
+       }
+       if (p != -1)
+               close(p);
        return ret;
 }
 
@@ -2226,7 +2294,7 @@ static int param_callback(char *path, semantic_func_t sem_init,
                return ret;
        }
 
-       buf = (char *)malloc(PATH_MAX + 1);
+       buf = (char *)malloc(2 * PATH_MAX);
        if (!buf)
                return -ENOMEM;
 
@@ -2237,7 +2305,7 @@ static int param_callback(char *path, semantic_func_t sem_init,
 
        param->fp_depth = 0;
 
-       ret = llapi_semantic_traverse(buf, PATH_MAX + 1, NULL, sem_init,
+       ret = llapi_semantic_traverse(buf, 2 * PATH_MAX, -1, sem_init,
                                      sem_fini, param, NULL);
 out:
        find_param_fini(param);
@@ -2247,23 +2315,29 @@ out:
 
 int llapi_file_fget_lov_uuid(int fd, struct obd_uuid *lov_name)
 {
-       int rc = ioctl(fd, OBD_IOC_GETNAME, lov_name);
+       int rc;
 
+       rc = ioctl(fd, OBD_IOC_GETDTNAME, lov_name);
+       if (rc && errno == ENOTTY)
+               rc = ioctl(fd, OBD_IOC_GETNAME_OLD, lov_name);
        if (rc) {
                rc = -errno;
                llapi_error(LLAPI_MSG_ERROR, rc, "cannot get lov name");
        }
+
        return rc;
 }
 
 int llapi_file_fget_lmv_uuid(int fd, struct obd_uuid *lov_name)
 {
-       int rc = ioctl(fd, OBD_IOC_GETMDNAME, lov_name);
+       int rc;
 
+       rc = ioctl(fd, OBD_IOC_GETMDNAME, lov_name);
        if (rc) {
                rc = -errno;
                llapi_error(LLAPI_MSG_ERROR, rc, "error: can't get lmv name.");
        }
+
        return rc;
 }
 
@@ -2271,11 +2345,19 @@ int llapi_file_get_lov_uuid(const char *path, struct obd_uuid *lov_uuid)
 {
        int fd, rc;
 
-       fd = open(path, O_RDONLY | O_NONBLOCK);
+       /* do not follow faked symlinks */
+       fd = open(path, O_RDONLY | O_NONBLOCK | O_NOFOLLOW);
        if (fd < 0) {
-               rc = -errno;
-               llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
-               return rc;
+               /* real symlink should have failed with ELOOP so retry without
+                * O_NOFOLLOW just in case
+                */
+               fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (fd < 0) {
+                       rc = -errno;
+                       llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'",
+                                   path);
+                       return rc;
+               }
        }
 
        rc = llapi_file_fget_lov_uuid(fd, lov_uuid);
@@ -2509,7 +2591,7 @@ free_param:
  * obd index for all these obduuids will be returned in
  * param->fp_obd_indexes
  */
-static int setup_indexes(DIR *dir, char *path, struct obd_uuid *obduuids,
+static int setup_indexes(int d, char *path, struct obd_uuid *obduuids,
                         int num_obds, int **obdindexes, int *obdindex,
                         enum tgt_type type)
 {
@@ -2532,7 +2614,7 @@ static int setup_indexes(DIR *dir, char *path, struct obd_uuid *obduuids,
                return -ENOMEM;
 
 retry_get_uuids:
-       ret = llapi_get_target_uuids(dirfd(dir), uuids, &obdcount, type);
+       ret = llapi_get_target_uuids(d, uuids, &obdcount, type);
        if (ret) {
                if (ret == -EOVERFLOW) {
                        struct obd_uuid *uuids_temp;
@@ -2596,12 +2678,12 @@ out_free:
        return ret;
 }
 
-static int setup_target_indexes(DIR *dir, char *path, struct find_param *param)
+static int setup_target_indexes(int d, char *path, struct find_param *param)
 {
        int ret = 0;
 
        if (param->fp_mdt_uuid) {
-               ret = setup_indexes(dir, path, param->fp_mdt_uuid,
+               ret = setup_indexes(d, path, param->fp_mdt_uuid,
                                    param->fp_num_mdts,
                                    &param->fp_mdt_indexes,
                                    &param->fp_mdt_index, LMV_TYPE);
@@ -2610,7 +2692,7 @@ static int setup_target_indexes(DIR *dir, char *path, struct find_param *param)
        }
 
        if (param->fp_obd_uuid) {
-               ret = setup_indexes(dir, path, param->fp_obd_uuid,
+               ret = setup_indexes(d, path, param->fp_obd_uuid,
                                    param->fp_num_obds,
                                    &param->fp_obd_indexes,
                                    &param->fp_obd_index, LOV_TYPE);
@@ -3095,7 +3177,7 @@ void lmv_dump_user_lmm(struct lmv_user_md *lum, char *pool_name,
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (verbose & ~VERBOSE_STRIPE_COUNT)
                        llapi_printf(LLAPI_MSG_NORMAL, "lmv_stripe_count: ");
-               llapi_printf(LLAPI_MSG_NORMAL, "%u",
+               llapi_printf(LLAPI_MSG_NORMAL, "%d",
                             (int)lum->lum_stripe_count);
                if ((verbose & VERBOSE_STRIPE_OFFSET) && !yaml)
                        separator = " ";
@@ -3135,9 +3217,50 @@ void lmv_dump_user_lmm(struct lmv_user_md *lum, char *pool_name,
                if (flags & LMV_HASH_FLAG_LOST_LMV)
                        llapi_printf(LLAPI_MSG_NORMAL, ",lost_lmv");
 
-               separator = "\n";
+               if (verbose & VERBOSE_HASH_TYPE && !yaml)
+                       separator = " ";
+               else
+                       separator = "\n";
+       }
+
+       if ((verbose & VERBOSE_INHERIT) && lum->lum_magic == LMV_USER_MAGIC) {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+               if (verbose & ~VERBOSE_INHERIT)
+                       llapi_printf(LLAPI_MSG_NORMAL, "lmv_max_inherit: ");
+               if (lum->lum_max_inherit == LMV_INHERIT_UNLIMITED)
+                       llapi_printf(LLAPI_MSG_NORMAL, "-1");
+               else if (lum->lum_max_inherit == LMV_INHERIT_NONE)
+                       llapi_printf(LLAPI_MSG_NORMAL, "0");
+               else
+                       llapi_printf(LLAPI_MSG_NORMAL, "%hhu",
+                                    lum->lum_max_inherit);
+               if (verbose & VERBOSE_INHERIT && !yaml)
+                       separator = " ";
+               else
+                       separator = "\n";
+       }
+
+       if ((verbose & VERBOSE_INHERIT_RR) &&
+           lum->lum_magic == LMV_USER_MAGIC &&
+           lum->lum_stripe_offset == LMV_OFFSET_DEFAULT) {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+               if (verbose & ~VERBOSE_INHERIT_RR)
+                       llapi_printf(LLAPI_MSG_NORMAL, "lmv_max_inherit_rr: ");
+               if (lum->lum_max_inherit_rr == LMV_INHERIT_RR_UNLIMITED)
+                       llapi_printf(LLAPI_MSG_NORMAL, "-1");
+               else if (lum->lum_max_inherit_rr == LMV_INHERIT_RR_NONE)
+                       llapi_printf(LLAPI_MSG_NORMAL, "0");
+               else
+                       llapi_printf(LLAPI_MSG_NORMAL, "%hhu",
+                                    lum->lum_max_inherit_rr);
+               if (verbose & VERBOSE_INHERIT_RR && !yaml)
+                       separator = " ";
+               else
+                       separator = "\n";
        }
 
+       separator = "\n";
+
        if (verbose & VERBOSE_OBJID && lum->lum_magic != LMV_USER_MAGIC) {
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (lum->lum_stripe_count > 0)
@@ -3928,7 +4051,7 @@ static void llapi_lov_dump_user_lmm(struct find_param *param, char *path,
        }
 }
 
-int llapi_file_get_stripe(const char *path, struct lov_user_md *lum)
+static int llapi_file_get_stripe1(const char *path, struct lov_user_md *lum)
 {
        const char *fname;
        char *dname;
@@ -3970,6 +4093,31 @@ out_free:
        return rc;
 }
 
+int llapi_file_get_stripe(const char *path, struct lov_user_md *lum)
+{
+       char *canon_path = NULL;
+       int rc, rc2;
+
+       rc = llapi_file_get_stripe1(path, lum);
+       if (!(rc == -ENOTTY || rc == -ENODATA))
+               goto out;
+
+       /* Handle failure due to symlinks by dereferencing path manually. */
+       canon_path = canonicalize_file_name(path);
+       if (canon_path == NULL)
+               goto out; /* Keep original rc. */
+
+       rc2 = llapi_file_get_stripe1(canon_path, lum);
+       if (rc2 < 0)
+               goto out; /* Keep original rc. */
+
+       rc = 0;
+out:
+       free(canon_path);
+
+       return rc;
+}
+
 int llapi_file_lookup(int dirfd, const char *name)
 {
        struct obd_ioctl_data data = { 0 };
@@ -4210,26 +4358,35 @@ static int check_mdt_match(struct find_param *param)
  * not active, just print the object affected by this
  * failed target
  **/
-static int print_failed_tgt(struct find_param *param, char *path, int type)
+static void print_failed_tgt(struct find_param *param, char *path, int type)
 {
        struct obd_statfs stat_buf;
        struct obd_uuid uuid_buf;
-       int ret;
+       int tgt_nr, i, *indexes;
+       int ret = 0;
 
-       if (type != LL_STATFS_LOV && type != LL_STATFS_LMV)
-               return -EINVAL;
+       if (type != LL_STATFS_LOV && type != LL_STATFS_LMV) {
+               llapi_error(LLAPI_MSG_NORMAL, ret, "%s: wrong statfs type(%d)",
+                           __func__, type);
+               return;
+       }
 
-       memset(&stat_buf, 0, sizeof(struct obd_statfs));
-       memset(&uuid_buf, 0, sizeof(struct obd_uuid));
-       ret = llapi_obd_statfs(path, type,
-                              type == LL_STATFS_LOV ? param->fp_obd_index :
-                              param->fp_mdt_index, &stat_buf,
-                              &uuid_buf);
-       if (ret)
-               llapi_error(LLAPI_MSG_NORMAL, ret, "obd_uuid: %s failed",
-                            param->fp_obd_uuid->uuid);
+       tgt_nr = (type == LL_STATFS_LOV) ? param->fp_obd_index :
+                param->fp_mdt_index;
+       indexes = (type == LL_STATFS_LOV) ? param->fp_obd_indexes :
+                 param->fp_mdt_indexes;
 
-       return ret;
+       for (i = 0; i < tgt_nr; i++) {
+               memset(&stat_buf, 0, sizeof(struct obd_statfs));
+               memset(&uuid_buf, 0, sizeof(struct obd_uuid));
+
+               ret = llapi_obd_statfs(path, type, indexes[i], &stat_buf,
+                                      &uuid_buf);
+               if (ret)
+                       llapi_error(LLAPI_MSG_NORMAL, ret,
+                                   "%s: obd_uuid: %s failed",
+                                   __func__, param->fp_obd_uuid->uuid);
+       }
 }
 
 static int find_check_stripe_size(struct find_param *param)
@@ -4422,38 +4579,45 @@ static int find_check_foreign(struct find_param *param)
 static int find_check_pool(struct find_param *param)
 {
        struct lov_comp_md_v1 *comp_v1 = NULL;
-       struct lov_user_md_v1 *v1 = &param->fp_lmd->lmd_lmm;
-       struct lov_user_md_v3 *v3 = (void *)v1;
+       struct lov_user_md_v3 *v3 = (void *)&param->fp_lmd->lmd_lmm;
        int i, count = 1;
        bool found = false;
 
-       if (v1->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
-               comp_v1 = (struct lov_comp_md_v1 *)v1;
+       if (v3->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
+               comp_v1 = (struct lov_comp_md_v1 *)v3;
                count = comp_v1->lcm_entry_count;
                /* empty requested pool is taken as no pool search */
-               if (count == 0 && param->fp_poolname[0] == '\0')
+               if (count == 0 && param->fp_poolname[0] == '\0') {
                        found = true;
+                       goto found;
+               }
        }
 
        for (i = 0; i < count; i++) {
-               if (comp_v1 != NULL)
-                       v1 = lov_comp_entry(comp_v1, i);
+               if (comp_v1 != NULL) {
+                       if (!(comp_v1->lcm_entries[i].lcme_flags &
+                             LCME_FL_INIT))
+                               continue;
 
-               if (v1->lmm_magic == LOV_USER_MAGIC_FOREIGN)
+                       v3 = (void *)lov_comp_entry(comp_v1, i);
+               }
+
+               if (v3->lmm_magic == LOV_USER_MAGIC_FOREIGN)
                        continue;
 
-               if (((v1->lmm_magic == LOV_USER_MAGIC_V1) &&
+               if (((v3->lmm_magic == LOV_USER_MAGIC_V1) &&
                     (param->fp_poolname[0] == '\0')) ||
-                   ((v1->lmm_magic == LOV_USER_MAGIC_V3) &&
+                   ((v3->lmm_magic == LOV_USER_MAGIC_V3) &&
                     (strncmp(v3->lmm_pool_name,
                              param->fp_poolname, LOV_MAXPOOLNAME) == 0)) ||
-                   ((v1->lmm_magic == LOV_USER_MAGIC_V3) &&
+                   ((v3->lmm_magic == LOV_USER_MAGIC_V3) &&
                     (strcmp(param->fp_poolname, "*") == 0))) {
                        found = true;
                        break;
                }
        }
 
+found:
        if ((found && !param->fp_exclude_pool) ||
            (!found && param->fp_exclude_pool))
                return 1;
@@ -4604,12 +4768,12 @@ static int fget_projid(int fd, int *projid)
        return 0;
 }
 
-static int cb_find_init(char *path, DIR *parent, DIR **dirp,
+static int cb_find_init(char *path, int p, int *dp,
                        void *data, struct dirent64 *de)
 {
        struct find_param *param = (struct find_param *)data;
        struct lov_user_mds_data *lmd = param->fp_lmd;
-       DIR *dir = dirp == NULL ? NULL : *dirp;
+       int d = dp == NULL ? -1 : *dp;
        int decision = 1; /* 1 is accepted; -1 is rejected. */
        int lustre_fs = 1;
        int checked_type = 0;
@@ -4618,7 +4782,7 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
        __u64 flags;
        int fd = -2;
 
-       if (parent == NULL && dir == NULL)
+       if (p == -1 && d == -1)
                return -EINVAL;
 
        /* If a regular expression is presented, make the initial decision */
@@ -4657,17 +4821,19 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
            param->fp_atime || param->fp_mtime || param->fp_ctime ||
            param->fp_check_size || param->fp_check_blocks ||
            find_check_lmm_info(param) ||
-           param->fp_check_mdt_count || param->fp_check_hash_type)
+           param->fp_check_mdt_count || param->fp_hash_type ||
+           param->fp_check_hash_flag)
                decision = 0;
 
        if (param->fp_type != 0 && checked_type == 0)
                decision = 0;
 
        if (decision == 0) {
-               if (dir && (param->fp_check_mdt_count ||
-                   param->fp_check_hash_type || param->fp_check_foreign)) {
+               if (d != -1 && (param->fp_check_mdt_count ||
+                   param->fp_hash_type || param->fp_check_foreign ||
+                   param->fp_check_hash_flag)) {
                        param->fp_get_lmv = 1;
-                       ret = cb_get_dirstripe(path, dir, param);
+                       ret = cb_get_dirstripe(path, &d, param);
                        if (ret != 0) {
                                /*
                                 * XXX this works to decide for foreign
@@ -4684,8 +4850,8 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                }
 
                param->fp_lmd->lmd_lmm.lmm_magic = 0;
-               ret = get_lmd_info(path, parent, dir, param->fp_lmd,
-                                  param->fp_lum_size, GET_LMD_INFO);
+               ret = get_lmd_info_fd(path, p, d, param->fp_lmd,
+                                     param->fp_lum_size, GET_LMD_INFO);
                if (ret == 0 && param->fp_lmd->lmd_lmm.lmm_magic == 0 &&
                    find_check_lmm_info(param)) {
                        struct lov_user_md *lmm = &param->fp_lmd->lmd_lmm;
@@ -4704,8 +4870,8 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                        lmm->lmm_stripe_offset = -1;
                }
                if (ret == 0 && param->fp_mdt_uuid != NULL) {
-                       if (dir != NULL) {
-                               ret = llapi_file_fget_mdtidx(dirfd(dir),
+                       if (d != -1) {
+                               ret = llapi_file_fget_mdtidx(d,
                                                     &param->fp_file_mdt_index);
                        } else if (S_ISREG(lmd->lmd_stx.stx_mode)) {
                                /*
@@ -4728,7 +4894,7 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                                 * For a special file, we assume it resides on
                                 * the same MDT as the parent directory.
                                 */
-                               ret = llapi_file_fget_mdtidx(dirfd(parent),
+                               ret = llapi_file_fget_mdtidx(p,
                                                     &param->fp_file_mdt_index);
                        }
                }
@@ -4767,7 +4933,7 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                }
 
                if (lustre_fs && !param->fp_got_uuids) {
-                       ret = setup_target_indexes(dir ? dir : parent, path,
+                       ret = setup_target_indexes((d != -1) ? d : p, path,
                                                   param);
                        if (ret)
                                goto out;
@@ -4829,15 +4995,17 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                        goto decided;
        }
 
-       if (param->fp_check_hash_type) {
+       if (param->fp_hash_type) {
                __u32 found;
+               __u32 type = param->fp_lmv_md->lum_hash_type &
+                            LMV_HASH_TYPE_MASK;
 
                if (param->fp_lmv_md->lum_magic == LMV_MAGIC_FOREIGN) {
                        decision = -1;
                        goto decided;
                }
 
-               found = param->fp_lmv_md->lum_hash_type & param->fp_hash_type;
+               found = (1 << type) & param->fp_hash_type;
                if ((found && param->fp_exclude_hash_type) ||
                    (!found && !param->fp_exclude_hash_type)) {
                        decision = -1;
@@ -4845,6 +5013,22 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                }
        }
 
+       if (param->fp_check_hash_flag) {
+               __u32 flags = param->fp_lmv_md->lum_hash_type &
+                             ~LMV_HASH_TYPE_MASK;
+
+               if (param->fp_lmv_md->lum_magic == LMV_MAGIC_FOREIGN) {
+                       decision = -1;
+                       goto decided;
+               }
+
+               if (!(flags & param->fp_hash_inflags) ||
+                    (flags & param->fp_hash_exflags)) {
+                       decision = -1;
+                       goto decided;
+               }
+       }
+
        /* If an OBD UUID is specified but none matches, skip this file. */
        if ((param->fp_obd_uuid && param->fp_obd_index == OBD_NOT_FOUND) ||
            (param->fp_mdt_uuid && param->fp_mdt_index == OBD_NOT_FOUND))
@@ -5011,10 +5195,10 @@ obd_matches:
                if (param->fp_mdt_index != OBD_NOT_FOUND)
                        print_failed_tgt(param, path, LL_STATFS_LMV);
 
-               if (dir != NULL)
-                       ret = fstat_f(dirfd(dir), &st);
+               if (d != -1)
+                       ret = fstat_f(d, &st);
                else if (de != NULL)
-                       ret = fstatat_f(dirfd(parent), de->d_name, &st,
+                       ret = fstatat_f(p, de->d_name, &st,
                                        AT_SYMLINK_NOFOLLOW);
                else
                        ret = lstat_f(path, &st);
@@ -5092,34 +5276,33 @@ out:
        return ret;
 }
 
-static int cb_migrate_mdt_init(char *path, DIR *parent, DIR **dirp,
+static int cb_migrate_mdt_init(char *path, int p, int *dp,
                               void *param_data, struct dirent64 *de)
 {
        struct find_param *param = (struct find_param *)param_data;
        struct lmv_user_md *lmu = param->fp_lmv_md;
-       DIR *tmp_parent = parent;
+       int tmp_p = p;
        char raw[MAX_IOC_BUFLEN] = {'\0'};
        char *rawbuf = raw;
        struct obd_ioctl_data data = { 0 };
-       int fd;
        int ret;
        char *path_copy;
        char *filename;
        bool retry = false;
 
-       if (parent == NULL && dirp == NULL)
+       if (p == -1 && dp == NULL)
                return -EINVAL;
 
        if (!lmu)
                return -EINVAL;
 
-       if (dirp != NULL)
-               closedir(*dirp);
+       if (dp != NULL && *dp != -1)
+               close(*dp);
 
-       if (parent == NULL) {
-               tmp_parent = opendir_parent(path);
-               if (tmp_parent == NULL) {
-                       *dirp = NULL;
+       if (p == -1) {
+               tmp_p = open_parent(path);
+               if (tmp_p == -1) {
+                       *dp = -1;
                        ret = -errno;
                        llapi_error(LLAPI_MSG_ERROR, ret,
                                    "can not open %s", path);
@@ -5127,8 +5310,6 @@ static int cb_migrate_mdt_init(char *path, DIR *parent, DIR **dirp,
                }
        }
 
-       fd = dirfd(tmp_parent);
-
        path_copy = strdup(path);
        filename = basename(path_copy);
 
@@ -5140,12 +5321,12 @@ static int cb_migrate_mdt_init(char *path, DIR *parent, DIR **dirp,
        ret = llapi_ioctl_pack(&data, &rawbuf, sizeof(raw));
        if (ret != 0) {
                llapi_error(LLAPI_MSG_ERROR, ret,
-                           "llapi_obd_statfs: error packing ioctl data");
+                           "%s: error packing ioctl data", __func__);
                goto out;
        }
 
 migrate:
-       ret = ioctl(fd, LL_IOC_MIGRATE, rawbuf);
+       ret = ioctl(tmp_p, LL_IOC_MIGRATE, rawbuf);
        if (ret != 0) {
                if (errno == EBUSY && !retry) {
                        /*
@@ -5178,7 +5359,7 @@ migrate:
        }
 
 out:
-       if (dirp != NULL) {
+       if (dp != NULL) {
                /*
                 * If the directory is being migration, we need
                 * close the directory after migration,
@@ -5186,16 +5367,16 @@ out:
                 * on the client side, and re-open to get the
                 * new directory handle
                 */
-               *dirp = opendir(path);
-               if (*dirp == NULL) {
+               *dp = open(path, O_RDONLY|O_NDELAY|O_DIRECTORY);
+               if (*dp == -1) {
                        ret = -errno;
                        llapi_error(LLAPI_MSG_ERROR, ret,
                                    "%s: Failed to open '%s'", __func__, path);
                }
        }
 
-       if (parent == NULL)
-               closedir(tmp_parent);
+       if (p == -1)
+               close(tmp_p);
 
        free(path_copy);
 
@@ -5203,7 +5384,7 @@ out:
 }
 
 /* dir migration finished, shrink its stripes */
-static int cb_migrate_mdt_fini(char *path, DIR *parent, DIR **dirp, void *data,
+static int cb_migrate_mdt_fini(char *path, int p, int *dp, void *data,
                               struct dirent64 *de)
 {
        struct find_param *param = data;
@@ -5214,13 +5395,13 @@ static int cb_migrate_mdt_fini(char *path, DIR *parent, DIR **dirp, void *data,
        if (de && de->d_type != DT_DIR)
                goto out;
 
-       if (*dirp) {
+       if (*dp != -1) {
                /*
                 * close it before setxattr because the latter may destroy the
                 * original object, and cause close fail.
                 */
-               ret = closedir(*dirp);
-               *dirp = NULL;
+               ret = close(*dp);
+               *dp = -1;
                if (ret)
                        goto out;
        }
@@ -5229,7 +5410,7 @@ static int cb_migrate_mdt_fini(char *path, DIR *parent, DIR **dirp, void *data,
        if (ret == -EALREADY)
                ret = 0;
 out:
-       cb_common_fini(path, parent, dirp, data, de);
+       cb_common_fini(path, p, dp, data, de);
        return ret;
 }
 
@@ -5271,20 +5452,20 @@ int llapi_file_fget_mdtidx(int fd, int *mdtidx)
        return 0;
 }
 
-static int cb_get_mdt_index(char *path, DIR *parent, DIR **dirp, void *data,
+static int cb_get_mdt_index(char *path, int p, int *dp, void *data,
                            struct dirent64 *de)
 {
        struct find_param *param = (struct find_param *)data;
-       DIR *d = dirp == NULL ? NULL : *dirp;
+       int d = dp == NULL ? -1 : *dp;
        int ret;
        int mdtidx;
 
-       if (parent == NULL && d == NULL)
+       if (p == -1 && d == -1)
                return -EINVAL;
 
-       if (d != NULL) {
-               ret = llapi_file_fget_mdtidx(dirfd(d), &mdtidx);
-       } else /* if (parent) */ {
+       if (d != -1) {
+               ret = llapi_file_fget_mdtidx(d, &mdtidx);
+       } else /* if (p != -1) */ {
                int fd;
 
                fd = open(path, O_RDONLY | O_NOCTTY);
@@ -5335,34 +5516,49 @@ out:
        return 0;
 }
 
-static int cb_getstripe(char *path, DIR *parent, DIR **dirp, void *data,
+static int cb_getstripe(char *path, int p, int *dp, void *data,
                        struct dirent64 *de)
 {
        struct find_param *param = (struct find_param *)data;
-       DIR *d = dirp == NULL ? NULL : *dirp;
+       int d = dp == NULL ? -1 : *dp;
        int ret = 0;
 
-       if (parent == NULL && d == NULL)
+       if (p == -1 && d == -1)
                return -EINVAL;
 
        if (param->fp_obd_uuid) {
                param->fp_quiet = 1;
-               ret = setup_obd_uuid(d ? dirfd(d) : dirfd(parent), path, param);
+               ret = setup_obd_uuid(d != -1 ? d : p, path, param);
                if (ret)
                        return ret;
        }
 
-       if (d && (param->fp_get_lmv || param->fp_get_default_lmv))
-               ret = cb_get_dirstripe(path, d, param);
-       else if (d ||
-                (parent && !param->fp_get_lmv && !param->fp_get_default_lmv))
-               ret = get_lmd_info(path, parent, d, &param->fp_lmd->lmd_lmm,
-                                  param->fp_lum_size, GET_LMD_STRIPE);
-       else
+       if (d != -1 && (param->fp_get_lmv || param->fp_get_default_lmv))
+               ret = cb_get_dirstripe(path, &d, param);
+       else if (d != -1 ||
+                (p != -1 && !param->fp_get_lmv && !param->fp_get_default_lmv))
+               ret = get_lmd_info_fd(path, p, d, &param->fp_lmd->lmd_lmm,
+                                     param->fp_lum_size, GET_LMD_STRIPE);
+       else if (d == -1 && (param->fp_get_lmv || param->fp_get_default_lmv)) {
+               /* in case of a dangling or valid faked symlink dir, opendir()
+                * should have return either EINVAL or ENOENT, so let's try
+                * to get LMV just in case, and by opening it as a file but
+                * with O_NOFOLLOW ...
+                */
+               int fd = open(path, O_RDONLY | O_NOFOLLOW);
+
+               if (fd == -1)
+                       return 0;
+               ret = cb_get_dirstripe(path, &fd, param);
+               if (ret == 0)
+                       llapi_lov_dump_user_lmm(param, path, LDF_IS_DIR);
+               close(fd);
+               return 0;
+       } else
                return 0;
 
        if (ret) {
-               if (errno == ENODATA && d != NULL) {
+               if (errno == ENODATA && d != -1) {
                        /*
                         * We need to "fake" the "use the default" values
                         * since the lmm struct is zeroed out at this point.
@@ -5382,7 +5578,7 @@ static int cb_getstripe(char *path, DIR *parent, DIR **dirp, void *data,
                                struct lmv_user_md *lum = param->fp_lmv_md;
                                int mdtidx;
 
-                               ret = llapi_file_fget_mdtidx(dirfd(d), &mdtidx);
+                               ret = llapi_file_fget_mdtidx(d, &mdtidx);
                                if (ret != 0)
                                        goto err_out;
                                lum->lum_magic = LMV_MAGIC_V1;
@@ -5402,7 +5598,7 @@ static int cb_getstripe(char *path, DIR *parent, DIR **dirp, void *data,
                                lmm->lmm_stripe_offset = -1;
                                goto dump;
                        }
-               } else if (errno == ENODATA && parent != NULL) {
+               } else if (errno == ENODATA && p != -1) {
                        if (!param->fp_obd_uuid && !param->fp_mdt_uuid)
                                llapi_printf(LLAPI_MSG_NORMAL,
                                             "%s has no stripe info\n", path);
@@ -5422,8 +5618,9 @@ static int cb_getstripe(char *path, DIR *parent, DIR **dirp, void *data,
 err_out:
                        llapi_error(LLAPI_MSG_ERROR, ret,
                                    "error: %s: %s failed for %s",
-                                    __func__, d ? "LL_IOC_LOV_GETSTRIPE" :
-                                   "IOC_MDC_GETFILESTRIPE", path);
+                                    __func__, d != -1 ?
+                                              "LL_IOC_LOV_GETSTRIPE" :
+                                              "IOC_MDC_GETFILESTRIPE", path);
                }
 
                return ret;
@@ -5431,7 +5628,7 @@ err_out:
 
 dump:
        if (!(param->fp_verbose & VERBOSE_MDTINDEX))
-               llapi_lov_dump_user_lmm(param, path, d ? LDF_IS_DIR : 0);
+               llapi_lov_dump_user_lmm(param, path, d != -1 ? LDF_IS_DIR : 0);
 
 out:
        /* Do not get down anymore? */
@@ -5470,7 +5667,7 @@ int llapi_obd_fstatfs(int fd, __u32 type, __u32 index,
        rc = llapi_ioctl_pack(&data, &rawbuf, sizeof(raw));
        if (rc != 0) {
                llapi_error(LLAPI_MSG_ERROR, rc,
-                           "llapi_obd_statfs: error packing ioctl data");
+                           "%s: error packing ioctl data", __func__);
                return rc;
        }
 
@@ -5664,6 +5861,9 @@ int llapi_quotactl(char *mnt, struct if_quotactl *qctl)
        rc = ioctl(root, OBD_IOC_QUOTACTL, qctl);
        if (rc < 0)
                rc = -errno;
+       if (rc == -ENOENT && LUSTRE_Q_CMD_IS_POOL(qctl->qc_cmd))
+               llapi_error(LLAPI_MSG_ERROR | LLAPI_MSG_NO_ERRNO, rc,
+                           "Cannot find pool '%s'", qctl->qc_poolname);
 
        close(root);
        return rc;
@@ -5692,39 +5892,6 @@ int llapi_get_connect_flags(const char *mnt, __u64 *flags)
 }
 
 /**
- * Get a 64-bit value representing the version of file data pointed by fd.
- *
- * Each write or truncate, flushed on OST, will change this value. You can use
- * this value to verify if file data was modified. This only checks the file
- * data, not metadata.
- *
- * \param  flags  0: no flush pages, usually used it the process has already
- *                 taken locks;
- *                LL_DV_RD_FLUSH: OSTs will take LCK_PR to flush dirty pages
- *                  from clients;
- *                LL_DV_WR_FLUSH: OSTs will take LCK_PW to flush all caching
- *                  pages from clients.
- *
- * \retval 0 on success.
- * \retval -errno on error.
- */
-int llapi_get_data_version(int fd, __u64 *data_version, __u64 flags)
-{
-       int rc;
-       struct ioc_data_version idv;
-
-       idv.idv_flags = (__u32)flags;
-
-       rc = ioctl(fd, LL_IOC_DATA_VERSION, &idv);
-       if (rc)
-               rc = -errno;
-       else
-               *data_version = idv.idv_version;
-
-       return rc;
-}
-
-/**
  * Flush cached pages from all clients.
  *
  * \param fd   File descriptor
@@ -5738,301 +5905,3 @@ int llapi_file_flush(int fd)
        return llapi_get_data_version(fd, &dv, LL_DV_WR_FLUSH);
 }
 
-/*
- * Fetch layout version from OST objects. Layout version on OST objects are
- * only set when the file is a mirrored file AND after the file has been
- * written at least once.
- *
- * It actually fetches the least layout version from the objects.
- */
-int llapi_get_ost_layout_version(int fd, __u32 *layout_version)
-{
-       int rc;
-       struct ioc_data_version idv = { 0 };
-
-       rc = ioctl(fd, LL_IOC_DATA_VERSION, &idv);
-       if (rc)
-               rc = -errno;
-       else
-               *layout_version = idv.idv_layout_version;
-
-       return rc;
-}
-
-/*
- * Create a file without any name and open it for read/write
- *
- * - file is created as if it were a standard file in the given \a directory
- * - file does not appear in \a directory and mtime does not change because
- *   the filename is handled specially by the Lustre MDS.
- * - file is destroyed at final close
- *
- * \param[in]  directory       directory from which to inherit layout/MDT idx
- * \param[in]  mdt_idx         MDT index on which the file is created,
- *                             \a idx == -1 means no specific MDT is requested
- * \param[in]  mode            standard open(2) mode
- * \param[in]  stripe_param    stripe parameters. May be NULL.
- *
- * \retval     a file descriptor on success.
- * \retval     -errno on error.
- */
-int llapi_create_volatile_param(const char *directory, int mdt_idx,
-                               int open_flags, mode_t mode,
-                               const struct llapi_stripe_param *stripe_param)
-{
-       char file_path[PATH_MAX];
-       int saved_errno = errno;
-       int fd;
-       unsigned int rnumber;
-       int rc;
-
-       do {
-               rnumber = random();
-               if (mdt_idx == -1)
-                       rc = snprintf(file_path, sizeof(file_path),
-                                     "%s/" LUSTRE_VOLATILE_HDR "::%.4X",
-                                     directory, rnumber);
-               else
-                       rc = snprintf(file_path, sizeof(file_path),
-                                     "%s/" LUSTRE_VOLATILE_HDR ":%.4X:%.4X",
-                                     directory, mdt_idx, rnumber);
-
-               if (rc < 0 || rc >= sizeof(file_path))
-                       return -ENAMETOOLONG;
-
-               /*
-                * Either open O_WRONLY or O_RDWR, creating RDONLY
-                * is non-sensical here
-                */
-               if ((open_flags & O_ACCMODE) == O_RDONLY)
-                       open_flags = O_RDWR | (open_flags & ~O_ACCMODE);
-
-               open_flags |= O_CREAT | O_EXCL | O_NOFOLLOW;
-
-               if (stripe_param != NULL) {
-                       fd = llapi_file_open_param(file_path, open_flags,
-                                                  mode, stripe_param);
-                       if (fd < 0)
-                               rc = fd;
-               } else {
-                       fd = open(file_path, open_flags, mode);
-                       if (fd < 0)
-                               rc = -errno;
-               }
-       } while (fd < 0 && rc == -EEXIST);
-
-       if (fd < 0) {
-               llapi_error(LLAPI_MSG_ERROR, rc,
-                           "Cannot create volatile file '%s' in '%s'",
-                           file_path + strlen(directory) + 1 +
-                           LUSTRE_VOLATILE_HDR_LEN,
-                           directory);
-               return rc;
-       }
-
-       /*
-        * Unlink file in case this wasn't a Lustre filesystem and the magic
-        * volatile filename wasn't handled as intended. The effect is the
-        * same. If volatile open was supported then we expect unlink() to
-        * return -ENOENT.
-        */
-       (void)unlink(file_path);
-
-       /*
-        * Since we are returning successfully we restore errno (and
-        * mask out possible EEXIST from open() and ENOENT from unlink().
-        */
-       errno = saved_errno;
-
-       return fd;
-}
-
-/*
- * Create a file without any name open it for read/write
- *
- * - file is created as if it were a standard file in the given \a directory
- * - file does not appear in \a directory and mtime does not change because
- *   the filename is handled specially by the Lustre MDS.
- * - file is removed at final close
- * - file modes are rw------- since it doesn't make sense to have a read-only
- *   or write-only file that cannot be opened again.
- * - if user wants another mode it must use fchmod() on the open file, no
- *   security problems arise because it cannot be opened by another process.
- *
- * \param[in]  directory       directory from which to inherit layout/MDT idx
- * \param[in]  idx             MDT index on which the file is created,
- *                             \a idx == -1 means no specific MDT is requested
- * \param[in]  open_flags      standard open(2) flags
- *
- * \retval     a file descriptor on success.
- * \retval     -errno on error.
- */
-int llapi_create_volatile_idx(const char *directory, int mdt_idx,
-                             int open_flags)
-{
-       return llapi_create_volatile_param(directory, mdt_idx, open_flags,
-                                          S_IRUSR | S_IWUSR, NULL);
-}
-
-/**
- * Swap the layouts between 2 file descriptors
- * the 2 files must be open for writing
- * first fd received the ioctl, second fd is passed as arg
- * this is assymetric but avoid use of root path for ioctl
- */
-int llapi_fswap_layouts_grouplock(int fd1, int fd2, __u64 dv1, __u64 dv2,
-                                 int gid, __u64 flags)
-{
-       struct lustre_swap_layouts      lsl;
-       struct stat                     st1;
-       struct stat                     st2;
-       int                             rc;
-
-       if (flags & (SWAP_LAYOUTS_KEEP_ATIME | SWAP_LAYOUTS_KEEP_MTIME)) {
-               rc = fstat(fd1, &st1);
-               if (rc < 0)
-                       return -errno;
-
-               rc = fstat(fd2, &st2);
-               if (rc < 0)
-                       return -errno;
-       }
-       lsl.sl_fd = fd2;
-       lsl.sl_flags = flags;
-       lsl.sl_gid = gid;
-       lsl.sl_dv1 = dv1;
-       lsl.sl_dv2 = dv2;
-       rc = ioctl(fd1, LL_IOC_LOV_SWAP_LAYOUTS, &lsl);
-       if (rc < 0)
-               return -errno;
-
-       if (flags & (SWAP_LAYOUTS_KEEP_ATIME | SWAP_LAYOUTS_KEEP_MTIME)) {
-               struct timeval  tv1[2];
-               struct timeval  tv2[2];
-
-               memset(tv1, 0, sizeof(tv1));
-               memset(tv2, 0, sizeof(tv2));
-
-               if (flags & SWAP_LAYOUTS_KEEP_ATIME) {
-                       tv1[0].tv_sec = st1.st_atime;
-                       tv2[0].tv_sec = st2.st_atime;
-               } else {
-                       tv1[0].tv_sec = st2.st_atime;
-                       tv2[0].tv_sec = st1.st_atime;
-               }
-
-               if (flags & SWAP_LAYOUTS_KEEP_MTIME) {
-                       tv1[1].tv_sec = st1.st_mtime;
-                       tv2[1].tv_sec = st2.st_mtime;
-               } else {
-                       tv1[1].tv_sec = st2.st_mtime;
-                       tv2[1].tv_sec = st1.st_mtime;
-               }
-
-               rc = futimes(fd1, tv1);
-               if (rc < 0)
-                       return -errno;
-
-               rc = futimes(fd2, tv2);
-               if (rc < 0)
-                       return -errno;
-       }
-
-       return 0;
-}
-
-int llapi_fswap_layouts(int fd1, int fd2, __u64 dv1, __u64 dv2, __u64 flags)
-{
-       int     rc;
-       int     grp_id;
-
-       do
-               grp_id = random();
-       while (grp_id == 0);
-
-       rc = llapi_fswap_layouts_grouplock(fd1, fd2, dv1, dv2, grp_id, flags);
-       if (rc < 0)
-               return rc;
-
-       return 0;
-}
-
-/**
- * Swap the layouts between 2 files
- * the 2 files are open in write
- */
-int llapi_swap_layouts(const char *path1, const char *path2,
-                      __u64 dv1, __u64 dv2, __u64 flags)
-{
-       int     fd1, fd2, rc;
-
-       fd1 = open(path1, O_WRONLY | O_LOV_DELAY_CREATE);
-       if (fd1 < 0) {
-               rc = -errno;
-               llapi_error(LLAPI_MSG_ERROR, rc,
-                           "error: cannot open '%s' for write", path1);
-               goto out;
-       }
-
-       fd2 = open(path2, O_WRONLY | O_LOV_DELAY_CREATE);
-       if (fd2 < 0) {
-               rc = -errno;
-               llapi_error(LLAPI_MSG_ERROR, rc,
-                           "error: cannot open '%s' for write", path2);
-               goto out_close;
-       }
-
-       rc = llapi_fswap_layouts(fd1, fd2, dv1, dv2, flags);
-       if (rc < 0)
-               llapi_error(LLAPI_MSG_ERROR, rc,
-                           "error: cannot swap layout between '%s' and '%s'",
-                           path1, path2);
-
-       close(fd2);
-out_close:
-       close(fd1);
-out:
-       return rc;
-}
-
-/**
- * Take group lock.
- *
- * \param fd   File to lock.
- * \param gid  Group Identifier.
- *
- * \retval 0 on success.
- * \retval -errno on failure.
- */
-int llapi_group_lock(int fd, int gid)
-{
-       int rc;
-
-       rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
-       if (rc < 0) {
-               rc = -errno;
-               llapi_error(LLAPI_MSG_ERROR, rc, "cannot get group lock");
-       }
-       return rc;
-}
-
-/**
- * Put group lock.
- *
- * \param fd   File to unlock.
- * \param gid  Group Identifier.
- *
- * \retval 0 on success.
- * \retval -errno on failure.
- */
-int llapi_group_unlock(int fd, int gid)
-{
-       int rc;
-
-       rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
-       if (rc < 0) {
-               rc = -errno;
-               llapi_error(LLAPI_MSG_ERROR, rc, "cannot put group lock");
-       }
-       return rc;
-}