Whamcloud - gitweb
LU-8998 lfs: user space tools for PFL
authorNiu Yawei <yawei.niu@intel.com>
Wed, 5 Apr 2017 23:59:49 +0000 (07:59 +0800)
committerJinshan Xiong <jinshan.xiong@intel.com>
Thu, 6 Apr 2017 04:51:59 +0000 (21:51 -0700)
* 'lfs getstripe' to support composite file:

   --component-count:
   Display component count only;

   --component-id|-I [comp_id]:
   When 'comp_id' is provided, display components matching the
   provided 'copm_id', otherwise, display component ID only;

   --component-flags [flags]:
   When the 'flags' is provided, dispaly components contains the
   provided 'flags', otherwise, display component flags only;

   --component-start [[+-]start]:
   When '[+-]start' is provided, display components which have
   'at most' or 'at least' or exactly same extent start, otherwise,
   display component extent start only;

   --component-end|-E [[+-]end]:
   When '[+-]end' is provided, display components which have
   'at most' or 'at least' or exactly sme extent end, otherwise,
   display component extent end only;

* 'lfs find' to support composite file:

   --component-count <[+-]count>:
   Find composite files according to component count;

   --component-flags <flags>:
   Find composite files according to component flags;

   --component-start <[+-]start>:
   Find composite files according to component extent start;

   --component-end|-E <[+-]end>:
   Find composite files according to component extent end;

* 'lfs setstripe' to support composite file:

   --component-del [-I <comp_id> | --component-flags <flags>] filename
   Delete specified component(s) from existing composite file;

   --component-add -E <end1> [stripe options]
                              -E <end2> [stripe options] ...:
   Add component(s) to an existing composite file;

   -E <end1> [stripe options] -E <end2> [stripe options] ...:
   Create composite file with specifed layout components;

* 'lfs migrate' to support migrate composite files:
   - composite layout-> plain layout;
   - plain layout -> composite layout;
   - composite layout -> composite layout;

Reviewed-on: https://review.whamcloud.com/24867

Signed-off-by: Niu Yawei <yawei.niu@intel.com>
Change-Id: Ib0d3eeb7927cf83a173099c474df6539d1264370
Reviewed-by: Andreas Dilger <andreas.dilger@intel.com>
Reviewed-by: Jinshan Xiong <jinshan.xiong@intel.com>
lustre/include/lustre/lustre_idl.h
lustre/include/lustre/lustre_ostid.h
lustre/include/lustre/lustreapi.h
lustre/include/lustre_fid.h
lustre/utils/lfs.c
lustre/utils/liblustreapi.c

index c31a3c4..aa961de 100644 (file)
@@ -236,12 +236,93 @@ enum {
 };
 
 /**
+ * Different FID Format
+ * http://arch.lustre.org/index.php?title=Interoperability_fids_zfs#NEW.0
+ *
+ * FID:
+ * File IDentifier generated by client from range allocated by the seq service.
+ * First 0x400 sequences [2^33, 2^33 + 0x400] are reserved for system use. Note
+ * that on ldiskfs MDTs that IGIF FIDs can use inode numbers starting at 12,
+ * but this is in the IGIF SEQ rangeand does not conflict with assigned FIDs.
+ *
+ * IGIF:
+ * Inode and Generation In FID, a surrogate FID used to globally identify an
+ * existing object on OLD formatted MDT file system. This would only be used on
+ * MDT0 in a DNE filesystem, because there are not expected to be any OLD
+ * formatted DNE filesystems. Belongs to a sequence in [12, 2^32 - 1] range,
+ * where sequence number is inode number, and inode generation is used as OID.
+ * NOTE: This assumes no more than 2^32-1 inodes exist in the MDT filesystem,
+ * which is the maximum possible for an ldiskfs backend. NOTE: This assumes
+ * that the reserved ext3/ext4/ldiskfs inode numbers [0-11] are never visible
+ * to clients, which has always been true.
+ *
+ * IDIF:
+ * Object ID in FID, a surrogate FID used to globally identify an existing
+ * object on OLD formatted OST file system. Belongs to a sequence in
+ * [2^32, 2^33 - 1]. Sequence number is calculated as:
+ *     1 << 32 | (ost_index << 16) | ((objid >> 32) & 0xffff)
+ * that is, SEQ consists of 16-bit OST index, and higher 16 bits of object ID.
+ * The generation of unique SEQ values per OST allows the IDIF FIDs to be
+ * identified in the FLD correctly. The OID field is calculated as:
+ *     objid & 0xffffffff
+ * that is, it consists of lower 32 bits of object ID. NOTE This assumes that
+ * no more than 2^48-1 objects have ever been created on an OST, and that no
+ * more than 65535 OSTs are in use. Both are very reasonable assumptions (can
+ * uniquely map all objects on an OST that created 1M objects per second for 9
+ * years, or combinations thereof).
+ *
+ * OST_MDT0:
+ * Surrogate FID used to identify an existing object on OLD formatted OST
+ * filesystem. Belongs to the reserved sequence 0, and is used internally prior
+ * to the introduction of FID-on-OST, at which point IDIF will be used to
+ * identify objects as residing on a specific OST.
+ *
+ * LLOG:
+ * For Lustre Log objects the object sequence 1 is used. This is compatible with
+ * both OLD and NEW.1 namespaces, as this SEQ number is in the ext3/ldiskfs
+ * reserved inode range and does not conflict with IGIF sequence numbers.
+ *
+ * ECHO:
+ * For testing OST IO performance the object sequence 2 is used. This is
+ * compatible with both OLD and NEW.1 namespaces, as this SEQ number is in the
+ * ext3/ldiskfs reserved inode range and does not conflict with IGIF sequence
+ * numbers.
+ *
+ * OST_MDT1 .. OST_MAX:
+ * For testing with multiple MDTs the object sequence 3 through 9 is used,
+ * allowing direct mapping of MDTs 1 through 7 respectively, for a total of 8
+ * MDTs including OST_MDT0. This matches the legacy CMD project "group"
+ * mappings. However, this SEQ range is only for testing prior to any production
+ * DNE release, as the objects in this range conflict across all OSTs, as the
+ * OST index is not part of the FID.
+ *
+ *
+ * For compatibility with existing OLD OST network protocol structures, the FID
+ * must map onto the o_id and o_gr in a manner that ensures existing objects are
+ * identified consistently for IO, as well as onto the lock namespace to ensure
+ * both IDIFs map onto the same objects for IO as well as resources in the DLM.
+ *
+ * DLM OLD OBIF/IDIF:
+ * resource[] = {o_id, o_seq, 0, 0};  // o_seq == 0 for production releases
+ *
+ * DLM NEW.1 FID (this is the same for both the MDT and OST):
+ * resource[] = {SEQ, OID, VER, HASH};
+ *
+ * Note that for mapping IDIF values to DLM resource names the o_id may be
+ * larger than the 2^33 reserved sequence numbers for IDIF, so it is possible
+ * for the o_id numbers to overlap FID SEQ numbers in the resource. However, in
+ * all production releases the OLD o_seq field is always zero, and all valid FID
+ * OID values are non-zero, so the lock resources will not collide.
+ *
+ * For objects within the IDIF range, group extraction (non-CMD) will be:
+ * o_id = (fid->f_seq & 0x7fff) << 16 | fid->f_oid;
+ * o_seq = 0;  // formerly group number
+ */
+
+/**
  * Note that reserved SEQ numbers below 12 will conflict with ldiskfs
  * inodes in the IGIF namespace, so these reserved SEQ numbers can be
  * used for other purposes and not risk collisions with existing inodes.
- *
- * Different FID Format
- * http://arch.lustre.org/index.php?title=Interoperability_fids_zfs#NEW.0
  */
 enum fid_seq {
        FID_SEQ_OST_MDT0        = 0,
index aebb33e..4e1122d 100644 (file)
@@ -239,4 +239,58 @@ static inline void fid_to_lmm_oi(const struct lu_fid *fid,
        oi->oi.oi_seq = fid_seq(fid);
 }
 
+/**
+ * Unpack an OST object id/seq (group) into a FID.  This is needed for
+ * converting all obdo, lmm, lsm, etc. 64-bit id/seq pairs into proper
+ * FIDs.  Note that if an id/seq is already in FID/IDIF format it will
+ * be passed through unchanged.  Only legacy OST objects in "group 0"
+ * will be mapped into the IDIF namespace so that they can fit into the
+ * struct lu_fid fields without loss.
+ */
+static inline int ostid_to_fid(struct lu_fid *fid, const struct ost_id *ostid,
+                              __u32 ost_idx)
+{
+       __u64 seq = ostid_seq(ostid);
+
+       if (ost_idx > 0xffff) {
+               CERROR("bad ost_idx, "DOSTID" ost_idx:%u\n", POSTID(ostid),
+                      ost_idx);
+               return -EBADF;
+       }
+
+       if (fid_seq_is_mdt0(seq)) {
+               __u64 oid = ostid_id(ostid);
+
+               /* This is a "legacy" (old 1.x/2.early) OST object in "group 0"
+                * that we map into the IDIF namespace.  It allows up to 2^48
+                * objects per OST, as this is the object namespace that has
+                * been in production for years.  This can handle create rates
+                * of 1M objects/s/OST for 9 years, or combinations thereof.
+                */
+               if (oid >= IDIF_MAX_OID) {
+                       CERROR("bad MDT0 id(1), "DOSTID" ost_idx:%u\n",
+                              POSTID(ostid), ost_idx);
+                       return -EBADF;
+               }
+               fid->f_seq = fid_idif_seq(oid, ost_idx);
+               /* truncate to 32 bits by assignment */
+               fid->f_oid = oid;
+               /* in theory, not currently used */
+               fid->f_ver = oid >> 48;
+       } else if (!fid_seq_is_default(seq)) {
+               /* This is either an IDIF object, which identifies objects
+                * across all OSTs, or a regular FID.  The IDIF namespace
+                * maps legacy OST objects into the FID namespace.  In both
+                * cases, we just pass the FID through, no conversion needed.
+                */
+               if (ostid->oi_fid.f_ver) {
+                       CERROR("bad MDT0 id(2), "DOSTID" ost_idx:%u\n",
+                              POSTID(ostid), ost_idx);
+                       return -EBADF;
+               }
+               *fid = ostid->oi_fid;
+       }
+
+       return 0;
+}
 #endif
index 62eb67d..ba6c25d 100644 (file)
@@ -146,7 +146,10 @@ extern int llapi_file_lookup(int dirfd, const char *name);
 #define VERBOSE_DEFAULT                (VERBOSE_COUNT | VERBOSE_SIZE | \
                                 VERBOSE_OFFSET | VERBOSE_POOL | \
                                 VERBOSE_OBJID | VERBOSE_GENERATION | \
-                                VERBOSE_LAYOUT | VERBOSE_HASH_TYPE)
+                                VERBOSE_LAYOUT | VERBOSE_HASH_TYPE | \
+                                VERBOSE_COMP_COUNT | VERBOSE_COMP_FLAGS | \
+                                VERBOSE_COMP_START | VERBOSE_COMP_END | \
+                                VERBOSE_COMP_ID)
 
 struct find_param {
        unsigned int             fp_max_depth;
@@ -165,11 +168,14 @@ struct find_param {
        /* these need to be signed values */
        int                      fp_size_sign:2,
                                 fp_stripe_size_sign:2,
-                                fp_stripe_count_sign:2;
+                                fp_stripe_count_sign:2,
+                                fp_comp_start_sign:2,
+                                fp_comp_end_sign:2,
+                                fp_comp_count_sign:2;
        unsigned long long       fp_size;
        unsigned long long       fp_size_units;
 
-       unsigned long            fp_zero_end:1,
+       unsigned long long       fp_zero_end:1,
                                 fp_recursive:1,
                                 fp_exclude_pattern:1,
                                 fp_exclude_type:1,
@@ -195,7 +201,17 @@ struct find_param {
                                 fp_check_layout:1,
                                 fp_exclude_layout:1,
                                 fp_get_default_lmv:1, /* Get default LMV */
-                                fp_migrate:1;
+                                fp_migrate:1,
+                                fp_check_comp_count:1,
+                                fp_exclude_comp_count:1,
+                                fp_check_comp_flags:1,
+                                fp_exclude_comp_flags:1,
+                                fp_check_comp_start:1,
+                                fp_exclude_comp_start:1,
+                                fp_check_comp_end:1,
+                                fp_exclude_comp_end:1,
+                                fp_check_comp_id:1,
+                                fp_exclude_comp_id:1;
 
        int                      fp_verbose;
        int                      fp_quiet;
@@ -229,6 +245,14 @@ struct find_param {
        unsigned long long       fp_stripe_count;
        __u32                    fp_layout;
 
+       __u32                    fp_comp_count;
+       __u32                    fp_comp_flags;
+       __u32                    fp_comp_id;
+       unsigned long long       fp_comp_start;
+       unsigned long long       fp_comp_start_units;
+       unsigned long long       fp_comp_end;
+       unsigned long long       fp_comp_end_units;
+
        /* In-process parameters. */
        unsigned long            fp_got_uuids:1,
                                 fp_obds_printed:1;
index 9021834..f85449a 100644 (file)
@@ -661,62 +661,6 @@ static inline void ost_fid_build_resid(const struct lu_fid *fid,
        }
 }
 
-/**
- * Unpack an OST object id/seq (group) into a FID.  This is needed for
- * converting all obdo, lmm, lsm, etc. 64-bit id/seq pairs into proper
- * FIDs.  Note that if an id/seq is already in FID/IDIF format it will
- * be passed through unchanged.  Only legacy OST objects in "group 0"
- * will be mapped into the IDIF namespace so that they can fit into the
- * struct lu_fid fields without loss.  For reference see:
- * http://arch.lustre.org/index.php?title=Interoperability_fids_zfs
- */
-static inline int ostid_to_fid(struct lu_fid *fid, const struct ost_id *ostid,
-                              u32 ost_idx)
-{
-       u64 seq = ostid_seq(ostid);
-
-       if (ost_idx > 0xffff) {
-               CERROR("bad ost_idx, "DOSTID" ost_idx:%u\n", POSTID(ostid),
-                      ost_idx);
-               return -EBADF;
-       }
-
-       if (fid_seq_is_mdt0(seq)) {
-               u64 oid = ostid_id(ostid);
-
-               /* This is a "legacy" (old 1.x/2.early) OST object in "group 0"
-                * that we map into the IDIF namespace.  It allows up to 2^48
-                * objects per OST, as this is the object namespace that has
-                * been in production for years.  This can handle create rates
-                * of 1M objects/s/OST for 9 years, or combinations thereof.
-                */
-               if (oid >= IDIF_MAX_OID) {
-                       CERROR("bad MDT0 id(1), "DOSTID" ost_idx:%u\n",
-                              POSTID(ostid), ost_idx);
-                       return -EBADF;
-               }
-               fid->f_seq = fid_idif_seq(oid, ost_idx);
-               /* truncate to 32 bits by assignment */
-               fid->f_oid = oid;
-               /* in theory, not currently used */
-               fid->f_ver = oid >> 48;
-       } else if (likely(!fid_seq_is_default(seq))) {
-               /* This is either an IDIF object, which identifies objects
-                * across all OSTs, or a regular FID.  The IDIF namespace
-                * maps legacy OST objects into the FID namespace.  In both
-                * cases, we just pass the FID through, no conversion needed.
-                */
-               if (ostid->oi_fid.f_ver) {
-                       CERROR("bad MDT0 id(2), "DOSTID" ost_idx:%u\n",
-                              POSTID(ostid), ost_idx);
-                       return -EBADF;
-               }
-               *fid = ostid->oi_fid;
-       }
-
-       return 0;
-}
-
 static inline void ost_fid_from_resid(struct lu_fid *fid,
                                      const struct ldlm_res_id *name,
                                      int ost_idx)
index b6665a1..f522852 100644 (file)
@@ -120,7 +120,8 @@ static int lfs_list_commands(int argc, char **argv);
        "                 [--stripe-index|-i <start_ost_idx>]\n"        \
        "                 [--stripe-size|-S <stripe_size>]\n"           \
        "                 [--pool|-p <pool_name>]\n"                    \
-       "                 [--ost|-o <ost_indices>]\n"
+       "                 [--ost|-o <ost_indices>]\n"                   \
+       "                 [--component-end|-E <comp_end>]\n"
 
 #define SSM_HELP_COMMON \
        "\tstripe_size:  Number of bytes on each OST (0 filesystem default)\n" \
@@ -134,13 +135,17 @@ static int lfs_list_commands(int argc, char **argv);
        "\t                -o <ost_1>,<ost_i>-<ost_j>,<ost_n>\n"        \
        "\t              Or:\n"                                         \
        "\t                -o <ost_1> -o <ost_i>-<ost_j> -o <ost_n>\n"  \
-       "\t              If --pool is set with --ost, then the OSTs\n" \
-       "\t              must be the members of the pool."
+       "\t              If --pool is set with --ost, then the OSTs\n"  \
+       "\t              must be the members of the pool."              \
+       "\tcomp_end:     Extent end of the component\n"                 \
+       "\t              Can be specified with k, m or g (in KB, MB and GB\n" \
+       "\t              respectively, -1 for EOF), it must be aligned with\n"\
+       "\t              the stripe_size\n"
 
 #define SETSTRIPE_USAGE                                                \
        SSM_CMD_COMMON("setstripe")                             \
        "                 <directory|filename>\n"               \
-       SSM_HELP_COMMON
+       SSM_HELP_COMMON                                         \
 
 #define MIGRATE_USAGE                                                  \
        SSM_CMD_COMMON("migrate  ")                                     \
@@ -173,9 +178,24 @@ command_t cmdlist[] = {
        {"setstripe", lfs_setstripe, 0,
         "Create a new file with a specific striping pattern or\n"
         "set the default striping pattern on an existing directory or\n"
-        "delete the default striping pattern from an existing directory\n"
-        "usage: setstripe -d <directory>   (to delete default striping)\n"\
+        "delete the default striping pattern from an existing directory or\n"
+        "add layout component(s) to an existing composite file or\n"
+        "delete specified component(s) from an existing composite file\n\n"
+        "To delete default striping from an existing directory:\n"
+        "usage: setstripe -d <directory>\n"
+        " or\n"
+        "To delete component(s) from an existing composite file:\n"
+        "usage: setstripe --component-del [--component-id|-I <comp_id>]\n"
+        "                               [--component-flags|-F <comp_flags>]\n"
+        "                               <filename>\n"
+        "\tcomp_id:     Unique component ID\n"
+        "\tcomp_flags:  'init' indicating all instantiated components\n"
+        "\t-I and -F can't be specified at the same time\n"
+        " or\n"
+        "To add component(s) to an existing composite file:\n"
+        SSM_CMD_COMMON("setstripe --component-add")
         " or\n"
+        "To create a file with specified striping/composite layout:\n"
         SETSTRIPE_USAGE},
        {"getstripe", lfs_getstripe, 0,
         "To list the striping info for a given file or files in a\n"
@@ -185,6 +205,11 @@ command_t cmdlist[] = {
         "                 [--pool|-p] [--stripe-size|-S] [--directory|-d]\n"
         "                 [--mdt|-m] [--recursive|-r] [--raw|-R]\n"
         "                 [--layout|-L] [--fid|-F] [--generation|-g]\n"
+        "                 [--component-id|-I [comp_id]]\n"
+        "                 [--component-flags [comp_flags]]\n"
+        "                 [--component-count [comp_count]]\n"
+        "                 [--component-start [comp_start]]\n"
+        "                 [--component-end|-E [comp_end]]\n"
         "                 <directory|filename> ..."},
        {"setdirstripe", lfs_setdirstripe, 0,
         "To create a striped directory on a specified MDT. This can only\n"
@@ -225,6 +250,10 @@ command_t cmdlist[] = {
          "     [[!] --gid|-g|--group|-G <gid>|<gname>]\n"
          "     [[!] --uid|-u|--user|-U <uid>|<uname>] [[!] --pool <pool>]\n"
         "     [[!] --layout|-L released,raid0]\n"
+        "     [[!] --component-count [+-]<comp_cnt>]\n"
+        "     [[!] --component-start [+-]N[kMGTPE]]\n"
+        "     [[!] --component-end|-E [+-]N[kMGTPE]]\n"
+        "     [[!] --component-flags <comp_flags>]\n"
          "\t !: used before an option indicates 'NOT' requested attribute\n"
          "\t -: used before a value indicates 'AT MOST' requested value\n"
          "\t +: used before a value indicates 'AT LEAST' requested value\n"},
@@ -622,8 +651,82 @@ static int migrate_nonblock(int fd, int fdv, const struct stat *st,
        return 0;
 }
 
+static int lfs_component_set(char *fname, int comp_id, __u32 flags)
+{
+       return -ENOTSUP;
+}
+
+static int lfs_component_del(char *fname, __u32 comp_id, __u32 flags)
+{
+       int     rc = 0;
+
+       if (flags != 0 && comp_id != 0)
+               return -EINVAL;
+
+       /* LCME_FL_INIT is the only supported flag in PFL */
+       if (flags != 0) {
+               if (flags & ~LCME_KNOWN_FLAGS) {
+                       fprintf(stderr, "Invalid component flags %#x\n", flags);
+                       return -EINVAL;
+               }
+               comp_id = LCME_ID_NONE | flags;
+       } else if (comp_id > LCME_ID_MAX) {
+               fprintf(stderr, "Invalid component id %u\n", comp_id);
+               return -EINVAL;
+       }
+
+       rc = llapi_layout_file_comp_del(fname, comp_id);
+       if (rc)
+               fprintf(stderr, "Delete component %#x from %s failed. %s\n",
+                       comp_id, fname, strerror(errno));
+       return rc;
+}
+
+static int lfs_component_add(char *fname, struct llapi_layout *layout)
+{
+       int     rc;
+
+       if (layout == NULL)
+               return -EINVAL;
+
+       rc = llapi_layout_file_comp_add(fname, layout);
+       if (rc)
+               fprintf(stderr, "Add layout component(s) to %s failed. %s\n",
+                       fname, strerror(errno));
+       return rc;
+}
+
+static int lfs_component_create(char *fname, int open_flags, mode_t open_mode,
+                               struct llapi_layout *layout)
+{
+       struct stat     st;
+       int     fd;
+
+       if (layout == NULL)
+               return -EINVAL;
+
+       fd = lstat(fname, &st);
+       if (fd == 0 && S_ISDIR(st.st_mode)) {
+               open_flags = O_DIRECTORY | O_RDONLY;
+               fd = llapi_layout_file_open(fname, open_flags, open_mode,
+                                           layout);
+       } else {
+               fd = llapi_layout_file_create(fname, open_flags, open_mode,
+                                             layout);
+       }
+
+       if (fd < 0)
+               fprintf(stderr, "%s %s failed. %s\n",
+                       S_ISDIR(st.st_mode) ?
+                               "Set default composite layout to " :
+                               "Create composite file",
+                       fname, strerror(errno));
+       return fd;
+}
+
 static int lfs_migrate(char *name, __u64 migration_flags,
-                      struct llapi_stripe_param *param)
+                      struct llapi_stripe_param *param,
+                      struct llapi_layout *layout)
 {
        int                      fd = -1;
        int                      fdv = -1;
@@ -638,7 +741,7 @@ static int lfs_migrate(char *name, __u64 migration_flags,
        int                      rc;
        struct lov_user_md      *lum = NULL;
        int                      lum_size;
-       int                      buf_size;
+       int                      buf_size = 1024 * 1024 * 4;
        bool                     have_lease_rdlck = false;
        struct stat              st;
        struct stat              stv;
@@ -656,10 +759,12 @@ static int lfs_migrate(char *name, __u64 migration_flags,
         * (eg: no stripe)
         * in case of a real error, a later call will fail with better
         * error management */
-       if (rc < 0)
-               buf_size = 1024 * 1024;
-       else
-               buf_size = lum->lmm_stripe_size;
+       if (rc == 0) {
+               if ((lum->lmm_magic == LOV_USER_MAGIC_V1 ||
+                    lum->lmm_magic == LOV_USER_MAGIC_V3) &&
+                   lum->lmm_stripe_size != 0)
+                       buf_size = lum->lmm_stripe_size;
+       }
 
        /* open file, direct io */
        /* even if the file is only read, WR mode is nedeed to allow
@@ -715,6 +820,9 @@ static int lfs_migrate(char *name, __u64 migration_flags,
        }
 
        do {
+               int open_flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW;
+               mode_t open_mode = S_IRUSR | S_IWUSR;
+
                random_value = random();
                rc = snprintf(volatile_file, sizeof(volatile_file),
                              "%s/%s:%.4X:%.4X", parent, LUSTRE_VOLATILE_HDR,
@@ -725,9 +833,14 @@ static int lfs_migrate(char *name, __u64 migration_flags,
                }
 
                /* create, open a volatile file, use caching (ie no directio) */
-               fdv = llapi_file_open_param(volatile_file,
-                               O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW,
-                                           S_IRUSR | S_IWUSR, param);
+               if (param != NULL)
+                       fdv = llapi_file_open_param(volatile_file, open_flags,
+                                                   open_mode, param);
+               else if (layout != NULL)
+                       fdv = lfs_component_create(volatile_file, open_flags,
+                                                  open_mode, layout);
+               else
+                       fdv = -EINVAL;
        } while (fdv == -EEXIST);
 
        if (fdv < 0) {
@@ -886,9 +999,277 @@ static int parse_targets(__u32 *osts, int size, int offset, char *arg)
        return rc < 0 ? rc : nr;
 }
 
+static int verify_pool_name(char *prog_name, char *pool_name)
+{
+       char    *ptr;
+       int      rc;
+
+       if (pool_name == NULL)
+               return 0;
+
+       ptr = strchr(pool_name, '.');
+       if (ptr == NULL) {
+               ptr = pool_name;
+       } else {
+               if (ptr == pool_name) {
+                       fprintf(stderr, "error: %s: fsname is empty "
+                               "in pool name '%s'\n",
+                               prog_name, pool_name);
+                       return -EINVAL;
+               }
+               ++ptr;
+       }
+
+       rc = lustre_is_poolname_valid(ptr, 1, LOV_MAXPOOLNAME);
+       if (rc == -1) {
+               fprintf(stderr, "error: %s: poolname '%s' is empty\n",
+                       prog_name, pool_name);
+               return -EINVAL;
+       } else if (rc == -2) {
+               fprintf(stderr, "error: %s: pool name '%s' is too long "
+                       "(max is %d characters)\n",
+                       prog_name, pool_name, LOV_MAXPOOLNAME);
+               return -EINVAL;
+       } else if (rc > 0) {
+               fprintf(stderr, "error: %s: char '%c' not allowed in "
+                       "pool name '%s'\n",
+                       prog_name, rc, pool_name);
+               return -EINVAL;
+       }
+       return rc;
+}
+
+struct lfs_setstripe_args {
+       __u64                    lsa_comp_end;
+       unsigned long long       lsa_stripe_size;
+       int                      lsa_stripe_count;
+       int                      lsa_stripe_off;
+       __u32                    lsa_comp_flags;
+       int                      lsa_nr_osts;
+       __u32                   *lsa_osts;
+       char                    *lsa_pool_name;
+};
+
+static inline void setstripe_args_init(struct lfs_setstripe_args *lsa)
+{
+       memset(lsa, 0, sizeof(*lsa));
+       lsa->lsa_stripe_off = -1;
+}
+
+static inline bool setstripe_args_specified(struct lfs_setstripe_args *lsa)
+{
+       return (lsa->lsa_stripe_size != 0 || lsa->lsa_stripe_count != 0 ||
+               lsa->lsa_stripe_off != -1 || lsa->lsa_pool_name != NULL ||
+               lsa->lsa_comp_end != 0);
+}
+
+static int comp_args_to_layout(struct llapi_layout **composite,
+                              struct lfs_setstripe_args *lsa)
+{
+       struct llapi_layout *layout = *composite;
+       uint64_t prev_end = 0;
+       int i = 0, rc;
+
+       if (layout == NULL) {
+               layout = llapi_layout_alloc();
+               if (layout == NULL) {
+                       fprintf(stderr, "Alloc llapi_layout failed. %s\n",
+                               strerror(errno));
+                       return -ENOMEM;
+               }
+               *composite = layout;
+       } else {
+               uint64_t start;
+
+               /* Get current component extent, current component
+                * must be the tail component. */
+               rc = llapi_layout_comp_extent_get(layout, &start, &prev_end);
+               if (rc) {
+                       fprintf(stderr, "Get comp extent failed. %s\n",
+                               strerror(errno));
+                       return rc;
+               }
+
+               rc = llapi_layout_comp_add(layout);
+               if (rc) {
+                       fprintf(stderr, "Add component failed. %s\n",
+                               strerror(errno));
+                       return rc;
+               }
+       }
+
+       rc = llapi_layout_comp_extent_set(layout, prev_end, lsa->lsa_comp_end);
+       if (rc) {
+               fprintf(stderr, "Set extent [%lu, %llu) failed. %s\n",
+                       prev_end, lsa->lsa_comp_end, strerror(errno));
+               return rc;
+       }
+
+       if (lsa->lsa_stripe_size != 0) {
+               rc = llapi_layout_stripe_size_set(layout,
+                                                 lsa->lsa_stripe_size);
+               if (rc) {
+                       fprintf(stderr, "Set stripe size %llu failed. %s\n",
+                               lsa->lsa_stripe_size, strerror(errno));
+                       return rc;
+               }
+       }
+
+       if (lsa->lsa_stripe_count != 0) {
+               rc = llapi_layout_stripe_count_set(layout,
+                                                  lsa->lsa_stripe_count == -1 ?
+                                                  LLAPI_LAYOUT_WIDE :
+                                                  lsa->lsa_stripe_count);
+               if (rc) {
+                       fprintf(stderr, "Set stripe count %d failed. %s\n",
+                               lsa->lsa_stripe_count, strerror(errno));
+                       return rc;
+               }
+       }
+
+       if (lsa->lsa_pool_name != NULL) {
+               rc = llapi_layout_pool_name_set(layout, lsa->lsa_pool_name);
+               if (rc) {
+                       fprintf(stderr, "Set pool name: %s failed. %s\n",
+                               lsa->lsa_pool_name, strerror(errno));
+                       return rc;
+               }
+       }
+
+       if (lsa->lsa_nr_osts > 0) {
+               if (lsa->lsa_stripe_count > 0 &&
+                   lsa->lsa_nr_osts != lsa->lsa_stripe_count) {
+                       fprintf(stderr, "stripe_count(%d) != nr_osts(%d)\n",
+                               lsa->lsa_stripe_count, lsa->lsa_nr_osts);
+                       return -EINVAL;
+               }
+               for (i = 0; i < lsa->lsa_nr_osts; i++) {
+                       rc = llapi_layout_ost_index_set(layout, i,
+                                                       lsa->lsa_osts[i]);
+                       if (rc)
+                               break;
+               }
+       } else if (lsa->lsa_stripe_off != -1) {
+               rc = llapi_layout_ost_index_set(layout, 0, lsa->lsa_stripe_off);
+       }
+       if (rc) {
+               fprintf(stderr, "Set ost index %d failed. %s\n",
+                       i, strerror(errno));
+               return rc;
+       }
+
+       return 0;
+}
+
+/* In 'lfs setstripe --component-add' mode, we need to fetch the extent
+ * end of the last component in the existing file, and adjust the
+ * first extent start of the components to be added accordingly. */
+static int adjust_first_extent(char *fname, struct llapi_layout *layout)
+{
+       struct llapi_layout *head;
+       uint64_t start, end, stripe_size, prev_end = 0;
+       int rc;
+
+       if (layout == NULL)
+               return -EINVAL;
+
+       head = llapi_layout_get_by_path(fname, 0);
+       if (head == NULL) {
+               fprintf(stderr, "Read layout from %s failed. %s\n",
+                       fname, strerror(errno));
+               return -EINVAL;
+       }
+
+       /* Current component of 'head' should be tail of component list. */
+       rc = llapi_layout_comp_extent_get(head, &start, &prev_end);
+       if (rc) {
+               fprintf(stderr, "Get prev extent failed. %s\n",
+                       strerror(errno));
+               llapi_layout_free(head);
+               return rc;
+       }
+
+       llapi_layout_free(head);
+
+       /* Make sure we use the first component of the layout to be added. */
+       rc = llapi_layout_comp_move(layout, LLAPI_LAYOUT_COMP_POS_FIRST);
+       if (rc < 0) {
+               fprintf(stderr, "Move component cursor failed. %s\n",
+                       strerror(errno));
+               return rc;
+       }
+
+       rc = llapi_layout_comp_extent_get(layout, &start, &end);
+       if (rc) {
+               fprintf(stderr, "Get extent failed. %s\n", strerror(errno));
+               return rc;
+       }
+
+       if (start > prev_end || end <= prev_end) {
+               fprintf(stderr, "First extent to be set [%lu, %lu) isn't "
+                       "adjacent with the existing file extent end: %lu\n",
+                       start, end, prev_end);
+               return -EINVAL;
+       }
+
+       rc = llapi_layout_stripe_size_get(layout, &stripe_size);
+       if (rc) {
+               fprintf(stderr, "Get stripe size failed. %s\n",
+                       strerror(errno));
+               return rc;
+       }
+
+       if (stripe_size != LLAPI_LAYOUT_DEFAULT &&
+           (prev_end & (stripe_size - 1))) {
+               fprintf(stderr, "Stripe size %lu not aligned with %lu\n",
+                       stripe_size, prev_end);
+               return -EINVAL;
+       }
+
+       rc = llapi_layout_comp_extent_set(layout, prev_end, end);
+       if (rc) {
+               fprintf(stderr, "Set component extent [%lu, %lu) failed. %s\n",
+                       prev_end, end, strerror(errno));
+               return rc;
+       }
+
+       return 0;
+}
+
+static int comp_name2flags(__u32 *flags, char *name)
+{
+       char *ptr;
+
+       if (name == NULL)
+               return -EINVAL;
+
+       *flags = 0;
+       for (ptr = name; ; ptr = NULL) {
+               char *flg = strtok(ptr, ",");
+               if (flg == NULL)
+                       break;
+               if (strcmp(flg, "init") == 0)
+                       *flags |= LCME_FL_INIT;
+               else
+                       return -EINVAL;
+       }
+       return (*flags == 0) ? -EINVAL : 0;
+}
+
+enum {
+       LFS_POOL_OPT = 3,
+       LFS_COMP_COUNT_OPT,
+       LFS_COMP_START_OPT,
+       LFS_COMP_FLAGS_OPT,
+       LFS_COMP_DEL_OPT,
+       LFS_COMP_SET_OPT,
+       LFS_COMP_ADD_OPT
+};
+
 /* functions */
 static int lfs_setstripe(int argc, char **argv)
 {
+       struct lfs_setstripe_args        lsa;
        struct llapi_stripe_param       *param = NULL;
        struct find_param                migrate_mdt_param = {
                .fp_max_depth = -1,
@@ -897,26 +1278,27 @@ static int lfs_setstripe(int argc, char **argv)
        char                            *fname;
        int                              result;
        int                              result2 = 0;
-       unsigned long long               st_size;
-       int                              st_offset, st_count;
        char                            *end;
        int                              c;
        int                              delete = 0;
-       char                            *stripe_size_arg = NULL;
-       char                            *stripe_off_arg = NULL;
-       char                            *stripe_count_arg = NULL;
-       char                            *pool_name_arg = NULL;
        char                            *mdt_idx_arg = NULL;
        unsigned long long               size_units = 1;
        bool                             migrate_mode = false;
        bool                             migration_block = false;
        __u64                            migration_flags = 0;
        __u32                            osts[LOV_MAX_STRIPE_COUNT] = { 0 };
-       int                              nr_osts = 0;
+       int                              comp_del = 0, comp_set = 0;
+       int                              comp_add = 0;
+       __u32                            comp_id = 0;
+       struct llapi_layout             *layout = NULL;
 
        struct option            long_opts[] = {
                /* --block is only valid in migrate mode */
                {"block",        no_argument,       0, 'b'},
+               {"component-add", no_argument, 0, LFS_COMP_ADD_OPT},
+               {"component-del", no_argument, 0, LFS_COMP_DEL_OPT},
+               {"component-flags", required_argument, 0, LFS_COMP_FLAGS_OPT},
+               {"component-set", no_argument, 0, LFS_COMP_SET_OPT},
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 59, 0)
                /* This formerly implied "stripe-count", but was explicitly
                 * made "stripe-count" for consistency with other options,
@@ -926,6 +1308,7 @@ static int lfs_setstripe(int argc, char **argv)
                {"stripe-count", required_argument, 0, 'c'},
                {"stripe_count", required_argument, 0, 'c'},
                {"delete",       no_argument,       0, 'd'},
+               {"component-end", required_argument, 0, 'E'},
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 59, 0)
                /* This formerly implied "stripe-index", but was explicitly
                 * made "stripe-index" for consistency with other options,
@@ -934,6 +1317,7 @@ static int lfs_setstripe(int argc, char **argv)
 #endif
                {"stripe-index", required_argument, 0, 'i'},
                {"stripe_index", required_argument, 0, 'i'},
+               {"component-id", required_argument, 0, 'I'},
                {"mdt",          required_argument, 0, 'm'},
                {"mdt-index",    required_argument, 0, 'm'},
                {"mdt_index",    required_argument, 0, 'm'},
@@ -958,24 +1342,39 @@ static int lfs_setstripe(int argc, char **argv)
                {0, 0, 0, 0}
        };
 
-       st_size = 0;
-       st_offset = -1;
-       st_count = 0;
+       setstripe_args_init(&lsa);
 
        if (strcmp(argv[0], "migrate") == 0)
                migrate_mode = true;
 
-       while ((c = getopt_long(argc, argv, "bc:di:m:no:p:s:S:v",
+       while ((c = getopt_long(argc, argv, "bc:dE:i:I:m:no:p:s:S:v",
                                long_opts, NULL)) >= 0) {
                switch (c) {
                case 0:
                        /* Long options. */
                        break;
+               case LFS_COMP_ADD_OPT:
+                       comp_add = 1;
+                       break;
+               case LFS_COMP_DEL_OPT:
+                       comp_del = 1;
+                       break;
+               case LFS_COMP_FLAGS_OPT:
+                       result = comp_name2flags(&lsa.lsa_comp_flags, optarg);
+                       if (result != 0) {
+                               fprintf(stderr, "error: %s: bad comp flags "
+                                       "'%s'\n", argv[0], optarg);
+                               goto error;
+                       }
+                       break;
+               case LFS_COMP_SET_OPT:
+                       comp_set = 1;
+                       break;
                case 'b':
                        if (!migrate_mode) {
                                fprintf(stderr, "--block is valid only for"
                                                " migrate mode\n");
-                               return CMD_HELP;
+                               goto error;
                        }
                        migration_block = true;
                        break;
@@ -985,39 +1384,66 @@ static int lfs_setstripe(int argc, char **argv)
                                fprintf(stderr, "warning: '--count' deprecated"
                                        ", use '--stripe-count' instead\n");
 #endif
-                       stripe_count_arg = optarg;
+                       lsa.lsa_stripe_count = strtoul(optarg, &end, 0);
+                       if (*end != '\0') {
+                               fprintf(stderr, "error: %s: bad stripe count "
+                                       "'%s'\n", argv[0], optarg);
+                               goto error;
+                       }
                        break;
                case 'd':
                        /* delete the default striping pattern */
                        delete = 1;
                        break;
-               case 'o':
-                       nr_osts = parse_targets(osts,
-                                               sizeof(osts) / sizeof(__u32),
-                                               nr_osts, optarg);
-                       if (nr_osts < 0) {
-                               fprintf(stderr,
-                                       "error: %s: bad OST indices '%s'\n",
-                                       argv[0], optarg);
-                               return CMD_HELP;
+               case 'E':
+                       if (lsa.lsa_comp_end != 0) {
+                               result = comp_args_to_layout(&layout, &lsa);
+                               if (result)
+                                       goto error;
+
+                               setstripe_args_init(&lsa);
                        }
 
-                       if (st_offset == -1) /* first in the command line */
-                               st_offset = osts[0];
+                       if (!strncmp(optarg, "-1", strlen("-1")) ||
+                           !strncmp(optarg, "EOF", strlen("EOF")) ||
+                           !strncmp(optarg, "eof", strlen("eof"))) {
+                               lsa.lsa_comp_end = LUSTRE_EOF;
+                       } else {
+                               result = llapi_parse_size(optarg,
+                                                       &lsa.lsa_comp_end,
+                                                       &size_units, 0);
+                               if (result) {
+                                       fprintf(stderr, "error: %s: "
+                                               "bad component end '%s'\n",
+                                               argv[0], optarg);
+                                       goto error;
+                               }
+                       }
                        break;
                case 'i':
-#if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
                        if (strcmp(argv[optind - 1], "--index") == 0)
                                fprintf(stderr, "warning: '--index' deprecated"
                                        ", use '--stripe-index' instead\n");
-#endif
-                       stripe_off_arg = optarg;
+                       lsa.lsa_stripe_off = strtol(optarg, &end, 0);
+                       if (*end != '\0') {
+                               fprintf(stderr, "error: %s: bad stripe offset "
+                                       "'%s'\n", argv[0], optarg);
+                               goto error;
+                       }
+                       break;
+               case 'I':
+                       comp_id = strtoul(optarg, &end, 0);
+                       if (*end != '\0' || comp_id == 0) {
+                               fprintf(stderr, "error: %s: bad comp ID "
+                                       "'%s'\n", argv[0], optarg);
+                               goto error;
+                       }
                        break;
                case 'm':
                        if (!migrate_mode) {
                                fprintf(stderr, "--mdt-index is valid only for"
                                                " migrate mode\n");
-                               return CMD_HELP;
+                               goto error;
                        }
                        mdt_idx_arg = optarg;
                        break;
@@ -1025,10 +1451,31 @@ static int lfs_setstripe(int argc, char **argv)
                        if (!migrate_mode) {
                                fprintf(stderr, "--non-block is valid only for"
                                                " migrate mode\n");
-                               return CMD_HELP;
+                               goto error;
                        }
                        migration_flags |= MIGRATION_NONBLOCK;
                        break;
+               case 'o':
+                       lsa.lsa_nr_osts = parse_targets(osts,
+                                               sizeof(osts) / sizeof(__u32),
+                                               lsa.lsa_nr_osts, optarg);
+                       if (lsa.lsa_nr_osts < 0) {
+                               fprintf(stderr,
+                                       "error: %s: bad OST indices '%s'\n",
+                                       argv[0], optarg);
+                               goto error;
+                       }
+
+                       lsa.lsa_osts = osts;
+                       if (lsa.lsa_stripe_off == -1)
+                               lsa.lsa_stripe_off = osts[0];
+                       break;
+               case 'p':
+                       result = verify_pool_name(argv[0], optarg);
+                       if (result)
+                               goto error;
+                       lsa.lsa_pool_name = optarg;
+                       break;
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 59, 0)
                case 's':
 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
@@ -1037,119 +1484,109 @@ static int lfs_setstripe(int argc, char **argv)
 #endif
 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 59, 0) */
                case 'S':
-                       stripe_size_arg = optarg;
-                       break;
-               case 'p':
-                       pool_name_arg = optarg;
+                       result = llapi_parse_size(optarg, &lsa.lsa_stripe_size,
+                                                 &size_units, 0);
+                       if (result) {
+                               fprintf(stderr, "error: %s: bad stripe size "
+                                       "'%s'\n", argv[0], optarg);
+                               goto error;
+                       }
                        break;
                case 'v':
                        if (!migrate_mode) {
                                fprintf(stderr, "--verbose is valid only for"
                                                " migrate mode\n");
-                               return CMD_HELP;
+                               goto error;
                        }
                        migrate_mdt_param.fp_verbose = VERBOSE_DETAIL;
                        break;
                default:
-                       return CMD_HELP;
+                       goto error;
                }
        }
 
        fname = argv[optind];
 
-       if (delete &&
-           (stripe_size_arg != NULL || stripe_off_arg != NULL ||
-            stripe_count_arg != NULL || pool_name_arg != NULL)) {
-               fprintf(stderr, "error: %s: cannot specify -d with "
-                       "-s, -c, -o, or -p options\n",
-                       argv[0]);
-               return CMD_HELP;
+       if (lsa.lsa_comp_end != 0) {
+               result = comp_args_to_layout(&layout, &lsa);
+               if (result)
+                       goto error;
        }
 
        if (optind == argc) {
                fprintf(stderr, "error: %s: missing filename|dirname\n",
                        argv[0]);
-               return CMD_HELP;
+               goto error;
        }
 
-       if (mdt_idx_arg != NULL && optind > 3) {
-               fprintf(stderr, "error: %s: cannot specify -m with other "
-                       "options\n", argv[0]);
-               return CMD_HELP;
+       /* Only LCME_FL_INIT flags is used in PFL, and it shouldn't be
+        * altered by user space tool, so we don't need to support the
+        * --component-set for this moment. */
+       if (comp_set != 0) {
+               fprintf(stderr, "error: %s: --component-set isn't supported.\n",
+                       argv[0]);
+               goto error;
        }
 
-       if ((migration_flags & MIGRATION_NONBLOCK) && migration_block) {
-               fprintf(stderr,
-                       "error: %s: cannot specify --non-block and --block\n",
+       if ((delete + comp_set + comp_del + comp_add) > 1) {
+               fprintf(stderr, "error: %s: can't specify --component-set, "
+                       "--component-del, --component-add or -d together\n",
                        argv[0]);
-               return CMD_HELP;
+               goto error;
        }
 
-       if (pool_name_arg != NULL) {
-               char    *ptr;
-               int     rc;
+       if (delete && (setstripe_args_specified(&lsa) || comp_id != 0 ||
+                      lsa.lsa_comp_flags != 0 || layout != NULL)) {
+               fprintf(stderr, "error: %s: can't specify -d with "
+                       "-s, -c, -o, -p, -I, -F or -E options\n",
+                       argv[0]);
+               goto error;
+       }
 
-               ptr = strchr(pool_name_arg, '.');
-               if (ptr == NULL) {
-                       ptr = pool_name_arg;
-               } else {
-                       if ((ptr - pool_name_arg) == 0) {
-                               fprintf(stderr, "error: %s: fsname is empty "
-                                       "in pool name '%s'\n",
-                                       argv[0], pool_name_arg);
-                               return CMD_HELP;
-                       }
+       if ((comp_set || comp_del) &&
+           (setstripe_args_specified(&lsa) || layout != NULL)) {
+               fprintf(stderr, "error: %s: can't specify --component-del or "
+                       "--component-set with -s, -c, -o, -p or -E options.\n",
+                       argv[0]);
+               goto error;
+       }
 
-                       ++ptr;
-               }
+       if (comp_del && comp_id != 0 && lsa.lsa_comp_flags != 0) {
+               fprintf(stderr, "error: %s: can't specify both -I and -F for "
+                       "--component-del option.\n", argv[0]);
+               goto error;
+       }
 
-               rc = lustre_is_poolname_valid(ptr, 1, LOV_MAXPOOLNAME);
-               if (rc == -1) {
-                       fprintf(stderr, "error: %s: poolname '%s' is "
-                               "empty\n",
-                               argv[0], pool_name_arg);
-                       return CMD_HELP;
-               } else if (rc == -2) {
-                       fprintf(stderr, "error: %s: pool name '%s' is too long "
-                               "(max is %d characters)\n",
-                               argv[0], pool_name_arg, LOV_MAXPOOLNAME);
-                       return CMD_HELP;
-               } else if (rc > 0) {
-                       fprintf(stderr, "error: %s: char '%c' not allowed in "
-                               "pool name '%s'\n",
-                               argv[0], rc, pool_name_arg);
-                       return CMD_HELP;
+       if (comp_add) {
+               if (layout == NULL) {
+                       fprintf(stderr, "error: %s: -E option must be present"
+                               "in --component-add mode.\n", argv[0]);
+                       goto error;
                }
+               result = adjust_first_extent(fname, layout);
+               if (result != 0)
+                       goto error;
        }
 
-       /* get the stripe size */
-       if (stripe_size_arg != NULL) {
-               result = llapi_parse_size(stripe_size_arg, &st_size,
-                                         &size_units, 0);
-               if (result) {
-                       fprintf(stderr, "error: %s: bad stripe size '%s'\n",
-                               argv[0], stripe_size_arg);
-                       return CMD_HELP;
-               }
+       if (mdt_idx_arg != NULL && optind > 3) {
+               fprintf(stderr, "error: %s: cannot specify -m with other "
+                       "options\n", argv[0]);
+               goto error;
+       }
+
+       if ((migration_flags & MIGRATION_NONBLOCK) && migration_block) {
+               fprintf(stderr,
+                       "error: %s: cannot specify --non-block and --block\n",
+                       argv[0]);
+               goto error;
+       }
+
+       /* support --component-id option for migrate later. */
+       if (migrate_mode && comp_id != 0) {
+               fprintf(stderr, "error: %s: -I isn't supported yet.\n",
+                       argv[0]);
+               goto error;
        }
-        /* get the stripe offset */
-        if (stripe_off_arg != NULL) {
-                st_offset = strtol(stripe_off_arg, &end, 0);
-                if (*end != '\0') {
-                        fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
-                                argv[0], stripe_off_arg);
-                        return CMD_HELP;
-                }
-        }
-        /* get the stripe count */
-        if (stripe_count_arg != NULL) {
-                st_count = strtoul(stripe_count_arg, &end, 0);
-                if (*end != '\0') {
-                        fprintf(stderr, "error: %s: bad stripe count '%s'\n",
-                                argv[0], stripe_count_arg);
-                        return CMD_HELP;
-                }
-        }
 
        if (mdt_idx_arg != NULL) {
                /* initialize migrate mdt parameters */
@@ -1157,41 +1594,72 @@ static int lfs_setstripe(int argc, char **argv)
                if (*end != '\0') {
                        fprintf(stderr, "error: %s: bad MDT index '%s'\n",
                                argv[0], mdt_idx_arg);
-                       return CMD_HELP;
+                       goto error;
                }
                migrate_mdt_param.fp_migrate = 1;
-       } else {
+       } else if (layout == NULL) {
                /* initialize stripe parameters */
-               param = calloc(1, offsetof(typeof(*param), lsp_osts[nr_osts]));
+               param = calloc(1, offsetof(typeof(*param),
+                              lsp_osts[lsa.lsa_nr_osts]));
                if (param == NULL) {
-                       fprintf(stderr, "error: %s: run out of memory\n",
-                               argv[0]);
-                       return CMD_HELP;
+                       fprintf(stderr, "error: %s: %s\n", argv[0],
+                               strerror(ENOMEM));
+                       goto error;
                }
 
-               param->lsp_stripe_size = st_size;
-               param->lsp_stripe_offset = st_offset;
-               param->lsp_stripe_count = st_count;
+               param->lsp_stripe_size = lsa.lsa_stripe_size;
+               param->lsp_stripe_offset = lsa.lsa_stripe_off;
+               param->lsp_stripe_count = lsa.lsa_stripe_count;
                param->lsp_stripe_pattern = 0;
-               param->lsp_pool = pool_name_arg;
+               param->lsp_pool = lsa.lsa_pool_name;
                param->lsp_is_specific = false;
-               if (nr_osts > 0) {
-                       if (st_count > 0 && nr_osts != st_count) {
+               if (lsa.lsa_nr_osts > 0) {
+                       if (lsa.lsa_stripe_count > 0 &&
+                           lsa.lsa_nr_osts != lsa.lsa_stripe_count) {
                                fprintf(stderr, "error: %s: stripe count '%d' "
                                        "doesn't match the number of OSTs: %d\n"
-                                       , argv[0], st_count, nr_osts);
+                                       , argv[0], lsa.lsa_stripe_count,
+                                       lsa.lsa_nr_osts);
                                free(param);
-                               return CMD_HELP;
+                               goto error;
                        }
 
                        param->lsp_is_specific = true;
-                       param->lsp_stripe_count = nr_osts;
-                       memcpy(param->lsp_osts, osts, sizeof(*osts) * nr_osts);
+                       param->lsp_stripe_count = lsa.lsa_nr_osts;
+                       memcpy(param->lsp_osts, osts,
+                              sizeof(*osts) * lsa.lsa_nr_osts);
                }
        }
 
        for (fname = argv[optind]; fname != NULL; fname = argv[++optind]) {
-               if (!migrate_mode) {
+               char *op;
+               if (mdt_idx_arg != NULL) {
+                       result = llapi_migrate_mdt(fname, &migrate_mdt_param);
+                       op = "migrate mdt objects of";
+               } else if (migrate_mode) {
+                       result = lfs_migrate(fname, migration_flags, param,
+                                            layout);
+                       op = "migrate ost objects of";
+               } else if (comp_set != 0) {
+                       result = lfs_component_set(fname, comp_id,
+                                                  lsa.lsa_comp_flags);
+                       op = "modify component flags of";
+               } else if (comp_del != 0) {
+                       result = lfs_component_del(fname, comp_id,
+                                                  lsa.lsa_comp_flags);
+                       op = "delete component of";
+               } else if (comp_add != 0) {
+                       result = lfs_component_add(fname, layout);
+                       op = "add component to";
+               } else if (layout != NULL) {
+                       result = lfs_component_create(fname, O_CREAT | O_WRONLY,
+                                                     0644, layout);
+                       if (result >= 0) {
+                               close(result);
+                               result = 0;
+                       }
+                       op = "create composite";
+               } else {
                        result = llapi_file_open_param(fname,
                                                       O_CREAT | O_WRONLY,
                                                       0644, param);
@@ -1199,26 +1667,26 @@ static int lfs_setstripe(int argc, char **argv)
                                close(result);
                                result = 0;
                        }
-               } else if (mdt_idx_arg != NULL) {
-                       result = llapi_migrate_mdt(fname, &migrate_mdt_param);
-               } else {
-                       result = lfs_migrate(fname, migration_flags, param);
+                       op = "create striped";
                }
                if (result) {
                        /* Save the first error encountered. */
                        if (result2 == 0)
                                result2 = result;
                        fprintf(stderr, "error: %s: %s file '%s' failed: %s\n",
-                               argv[0], migrate_mode ? "migrate" : "create",
-                               fname,
-                               pool_name_arg != NULL && result == EINVAL ?
+                               argv[0], op, fname,
+                               lsa.lsa_pool_name != NULL && result == EINVAL ?
                                "OST not in pool?" : strerror(errno));
                        continue;
                }
        }
 
        free(param);
+       llapi_layout_free(layout);
        return result2;
+error:
+       llapi_layout_free(layout);
+       return CMD_HELP;
 }
 
 static int lfs_poollist(int argc, char **argv)
@@ -1330,7 +1798,6 @@ static int name2layout(__u32 *layout, char *name)
        return 0;
 }
 
-#define FIND_POOL_OPT 3
 static int lfs_find(int argc, char **argv)
 {
        int c, rc;
@@ -1341,15 +1808,20 @@ static int lfs_find(int argc, char **argv)
                .fp_quiet = 1,
        };
         struct option long_opts[] = {
-                {"atime",        required_argument, 0, 'A'},
-                {"stripe-count", required_argument, 0, 'c'},
-                {"stripe_count", required_argument, 0, 'c'},
-                {"ctime",        required_argument, 0, 'C'},
-                {"maxdepth",     required_argument, 0, 'D'},
-                {"gid",          required_argument, 0, 'g'},
-                {"group",        required_argument, 0, 'G'},
-                {"stripe-index", required_argument, 0, 'i'},
-                {"stripe_index", required_argument, 0, 'i'},
+               {"atime",        required_argument, 0, 'A'},
+               {"component-count", required_argument, 0, LFS_COMP_COUNT_OPT},
+               {"component-flags", required_argument, 0, LFS_COMP_FLAGS_OPT},
+               {"component-start", required_argument, 0, LFS_COMP_START_OPT},
+               {"stripe-count", required_argument, 0, 'c'},
+               {"stripe_count", required_argument, 0, 'c'},
+               {"ctime",        required_argument, 0, 'C'},
+               {"maxdepth",     required_argument, 0, 'D'},
+               {"component-end", required_argument, 0, 'E'},
+               {"gid",          required_argument, 0, 'g'},
+               {"group",        required_argument, 0, 'G'},
+               {"stripe-index", required_argument, 0, 'i'},
+               {"stripe_index", required_argument, 0, 'i'},
+               /*{"component-id", required_argument, 0, 'I'},*/
                {"layout",       required_argument, 0, 'L'},
                 {"mdt",          required_argument, 0, 'm'},
                 {"mdt-index",    required_argument, 0, 'm'},
@@ -1360,7 +1832,7 @@ static int lfs_find(int argc, char **argv)
                 {"obd",          required_argument, 0, 'O'},
                 {"ost",          required_argument, 0, 'O'},
                 /* no short option for pool, p/P already used */
-                {"pool",         required_argument, 0, FIND_POOL_OPT},
+               {"pool",         required_argument, 0, LFS_POOL_OPT},
                 {"print0",       no_argument,       0, 'p'},
                 {"print",        no_argument,       0, 'P'},
                 {"size",         required_argument, 0, 's'},
@@ -1383,8 +1855,8 @@ static int lfs_find(int argc, char **argv)
 
        /* when getopt_long_only() hits '!' it returns 1, puts "!" in optarg */
        while ((c = getopt_long_only(argc, argv,
-                                    "-A:c:C:D:g:G:i:L:m:M:n:O:Ppqrs:S:t:u:U:v",
-                                    long_opts, NULL)) >= 0) {
+                               "-A:c:C:D:E:g:G:i:L:m:M:n:O:Ppqrs:S:t:u:U:v",
+                               long_opts, NULL)) >= 0) {
                 xtime = NULL;
                 xsign = NULL;
                 if (neg_opt)
@@ -1445,6 +1917,53 @@ static int lfs_find(int argc, char **argv)
                        if (rc)
                                *xsign = rc;
                        break;
+               case LFS_COMP_COUNT_OPT:
+                       if (optarg[0] == '+') {
+                               param.fp_comp_count_sign = -1;
+                               optarg++;
+                       } else if (optarg[0] == '-') {
+                               param.fp_comp_count_sign =  1;
+                               optarg++;
+                       }
+
+                       param.fp_comp_count = strtoul(optarg, &endptr, 0);
+                       if (*endptr != '\0') {
+                               fprintf(stderr, "error: bad component count "
+                                       "'%s'\n", optarg);
+                               goto err;
+                       }
+                       param.fp_check_comp_count = 1;
+                       param.fp_exclude_comp_count = !!neg_opt;
+                       break;
+               case LFS_COMP_FLAGS_OPT:
+                       rc = comp_name2flags(&param.fp_comp_flags, optarg);
+                       if (rc) {
+                               fprintf(stderr, "error: bad component flags "
+                                       "'%s'\n", optarg);
+                               goto err;
+                       }
+                       param.fp_check_comp_flags = 1;
+                       param.fp_exclude_comp_flags = !!neg_opt;
+                       break;
+               case LFS_COMP_START_OPT:
+                       if (optarg[0] == '+') {
+                               param.fp_comp_start_sign = -1;
+                               optarg++;
+                       } else if (optarg[0] == '-') {
+                               param.fp_comp_start_sign =  1;
+                               optarg++;
+                       }
+
+                       rc = llapi_parse_size(optarg, &param.fp_comp_start,
+                                             &param.fp_comp_start_units, 0);
+                       if (rc) {
+                               fprintf(stderr, "error: bad component start "
+                                       "'%s'\n", optarg);
+                               goto err;
+                       }
+                       param.fp_check_comp_start = 1;
+                       param.fp_exclude_comp_start = !!neg_opt;
+                       break;
                 case 'c':
                         if (optarg[0] == '+') {
                                param.fp_stripe_count_sign = -1;
@@ -1467,6 +1986,25 @@ static int lfs_find(int argc, char **argv)
                case 'D':
                        param.fp_max_depth = strtol(optarg, 0, 0);
                        break;
+               case 'E':
+                       if (optarg[0] == '+') {
+                               param.fp_comp_end_sign = -1;
+                               optarg++;
+                       } else if (optarg[0] == '-') {
+                               param.fp_comp_end_sign =  1;
+                               optarg++;
+                       }
+
+                       rc = llapi_parse_size(optarg, &param.fp_comp_end,
+                                             &param.fp_comp_end_units, 0);
+                       if (rc) {
+                               fprintf(stderr, "error: bad component end "
+                                       "'%s'\n", optarg);
+                               goto err;
+                       }
+                       param.fp_check_comp_end = 1;
+                       param.fp_exclude_comp_end = !!neg_opt;
+                       break;
                case 'g':
                case 'G':
                        rc = name2id(&param.fp_gid, optarg, GROUP);
@@ -1504,7 +2042,7 @@ static int lfs_find(int argc, char **argv)
                        param.fp_exclude_uid = !!neg_opt;
                        param.fp_check_uid = 1;
                         break;
-                case FIND_POOL_OPT:
+               case LFS_POOL_OPT:
                         if (strlen(optarg) > LOV_MAXPOOLNAME) {
                                 fprintf(stderr,
                                         "Pool name %s is too long"
@@ -1714,6 +2252,9 @@ static int lfs_getstripe_internal(int argc, char **argv,
                                  struct find_param *param)
 {
        struct option long_opts[] = {
+               {"component-count",     no_argument, 0, LFS_COMP_COUNT_OPT},
+               {"component-flags", required_argument, 0, LFS_COMP_FLAGS_OPT},
+               {"component-start", required_argument, 0, LFS_COMP_START_OPT},
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 59, 0)
                /* This formerly implied "stripe-count", but was explicitly
                 * made "stripe-count" for consistency with other options,
@@ -1724,6 +2265,7 @@ static int lfs_getstripe_internal(int argc, char **argv,
                {"stripe_count",        no_argument,            0, 'c'},
                {"directory",           no_argument,            0, 'd'},
                {"default",             no_argument,            0, 'D'},
+               {"component-end",       required_argument,      0, 'E'},
                {"fid",                 no_argument,            0, 'F'},
                {"generation",          no_argument,            0, 'g'},
 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 59, 0)
@@ -1734,6 +2276,7 @@ static int lfs_getstripe_internal(int argc, char **argv,
 #endif
                {"stripe-index",        no_argument,            0, 'i'},
                {"stripe_index",        no_argument,            0, 'i'},
+               {"component-id",        required_argument,      0, 'I'},
                {"layout",              no_argument,            0, 'L'},
                {"mdt",                 no_argument,            0, 'm'},
                {"mdt-index",           no_argument,            0, 'm'},
@@ -1766,8 +2309,9 @@ static int lfs_getstripe_internal(int argc, char **argv,
                {0, 0, 0, 0}
        };
        int c, rc;
+       char *end, *tmp;
 
-       while ((c = getopt_long(argc, argv, "cdDFghiLmMoO:pqrRsSv",
+       while ((c = getopt_long(argc, argv, "cdDE:FghiI:LmMoO:pqrRsSv",
                                long_opts, NULL)) != -1) {
                switch (c) {
                case 'O':
@@ -1782,12 +2326,83 @@ static int lfs_getstripe_internal(int argc, char **argv,
                case 'q':
                        param->fp_quiet++;
                        break;
+               case LFS_COMP_COUNT_OPT:
+                       param->fp_verbose |= VERBOSE_COMP_COUNT;
+                       param->fp_max_depth = 0;
+                       break;
+               case LFS_COMP_FLAGS_OPT:
+                       if (optarg != NULL) {
+                               rc = comp_name2flags(&param->fp_comp_flags,
+                                                    optarg);
+                               if (rc != 0) {
+                                       param->fp_verbose |=
+                                               VERBOSE_COMP_FLAGS;
+                                       param->fp_max_depth = 0;
+                                       optind--;
+                               } else {
+                                       param->fp_check_comp_flags = 1;
+                               }
+                       } else {
+                               param->fp_verbose |= VERBOSE_COMP_FLAGS;
+                               param->fp_max_depth = 0;
+                       }
+                       break;
+               case LFS_COMP_START_OPT:
+                       if (optarg != NULL) {
+                               tmp = optarg;
+                               if (tmp[0] == '+') {
+                                       param->fp_comp_start_sign = 1;
+                                       tmp++;
+                               } else if (tmp[0] == '-') {
+                                       param->fp_comp_start_sign = -1;
+                                       tmp++;
+                               }
+                               rc = llapi_parse_size(tmp,
+                                               &param->fp_comp_start,
+                                               &param->fp_comp_start_units, 0);
+                               if (rc != 0) {
+                                       param->fp_verbose |= VERBOSE_COMP_START;
+                                       param->fp_max_depth = 0;
+                                       optind--;
+                               } else {
+                                       param->fp_check_comp_start = 1;
+                               }
+                       } else {
+                               param->fp_verbose |= VERBOSE_COMP_START;
+                               param->fp_max_depth = 0;
+                       }
+                       break;
                case 'd':
                        param->fp_max_depth = 0;
                        break;
                case 'D':
                        param->fp_get_default_lmv = 1;
                        break;
+               case 'E':
+                       if (optarg != NULL) {
+                               tmp = optarg;
+                               if (tmp[0] == '+') {
+                                       param->fp_comp_end_sign = 1;
+                                       tmp++;
+                               } else if (tmp[0] == '-') {
+                                       param->fp_comp_end_sign = -1;
+                                       tmp++;
+                               }
+                               rc = llapi_parse_size(tmp,
+                                               &param->fp_comp_end,
+                                               &param->fp_comp_end_units, 0);
+                               if (rc != 0) {
+                                       param->fp_verbose |= VERBOSE_COMP_END;
+                                       param->fp_max_depth = 0;
+                                       optind--;
+                               } else {
+                                       param->fp_check_comp_end = 1;
+                               }
+                       } else {
+                               param->fp_verbose |= VERBOSE_COMP_END;
+                               param->fp_max_depth = 0;
+                       }
+                       break;
                case 'F':
                        if (!(param->fp_verbose & VERBOSE_DETAIL)) {
                                param->fp_verbose |= VERBOSE_DFID;
@@ -1840,6 +2455,21 @@ static int lfs_getstripe_internal(int argc, char **argv,
                                param->fp_max_depth = 0;
                        }
                        break;
+               case 'I':
+                       if (optarg != NULL) {
+                               param->fp_comp_id = strtoul(optarg, &end, 0);
+                               if (*end != '\0') {
+                                       param->fp_verbose |= VERBOSE_COMP_ID;
+                                       param->fp_max_depth = 0;
+                                       optind--;
+                               } else {
+                                       param->fp_check_comp_id = 1;
+                               }
+                       } else {
+                               param->fp_max_depth = 0;
+                               param->fp_verbose |= VERBOSE_COMP_ID;
+                       }
+                       break;
                case 'p':
                        if (!(param->fp_verbose & VERBOSE_DETAIL)) {
                                param->fp_verbose |= VERBOSE_POOL;
@@ -1883,6 +2513,8 @@ static int lfs_getstripe_internal(int argc, char **argv,
 
        if (param->fp_recursive)
                param->fp_max_depth = -1;
+       else if (param->fp_verbose & VERBOSE_DETAIL)
+               param->fp_max_depth = 1;
 
        if (!param->fp_verbose)
                param->fp_verbose = VERBOSE_DEFAULT;
index e936b95..abcf0df 100644 (file)
@@ -2101,35 +2101,48 @@ int sattr_cache_get_defaults(const char *const fsname,
         return 0;
 }
 
+enum lov_dump_flags {
+       LDF_IS_DIR      = 0x0001,
+       LDF_IS_RAW      = 0x0002,
+       LDF_INDENT      = 0x0004,
+};
+
 static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
                                     struct lov_user_ost_data_v1 *objects,
-                                    int is_dir, int verbose, int depth,
-                                    int raw, char *pool_name)
+                                    int verbose, int depth, char *pool_name,
+                                    enum lov_dump_flags flags)
 {
+       bool is_dir = flags & LDF_IS_DIR;
+       bool is_raw = flags & LDF_IS_RAW;
+       bool indent = flags & LDF_INDENT;
        char *prefix = is_dir ? "" : "lmm_";
        char *separator = "";
+       char *space = indent ? "      " : "";
        int rc;
 
        if (is_dir && lmm_oi_seq(&lum->lmm_oi) == FID_SEQ_LOV_DEFAULT) {
                lmm_oi_set_seq(&lum->lmm_oi, 0);
-               if (verbose & VERBOSE_DETAIL)
-                       llapi_printf(LLAPI_MSG_NORMAL, "(Default) ");
+               if (!indent && (verbose & VERBOSE_DETAIL))
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s(Default) ", space);
        }
 
-       if (depth && path && ((verbose != VERBOSE_OBJID) || !is_dir))
+       if (!indent && depth && path && ((verbose != VERBOSE_OBJID) || !is_dir))
                llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
 
        if ((verbose & VERBOSE_DETAIL) && !is_dir) {
-               llapi_printf(LLAPI_MSG_NORMAL, "lmm_magic:          0x%08X\n",
-                            lum->lmm_magic);
-               llapi_printf(LLAPI_MSG_NORMAL, "lmm_seq:            %#jx\n",
+               llapi_printf(LLAPI_MSG_NORMAL, "%s%smagic:         0x%08X\n",
+                            space, prefix, lum->lmm_magic);
+               llapi_printf(LLAPI_MSG_NORMAL, "%s%sseq:           %#jx\n",
+                            space, prefix,
                             (uintmax_t)lmm_oi_seq(&lum->lmm_oi));
-               llapi_printf(LLAPI_MSG_NORMAL, "lmm_object_id:      %#jx\n",
+               llapi_printf(LLAPI_MSG_NORMAL, "%s%sobject_id:     %#jx\n",
+                            space, prefix,
                             (uintmax_t)lmm_oi_id(&lum->lmm_oi));
        }
        if ((verbose & (VERBOSE_DETAIL | VERBOSE_DFID)) && !is_dir) {
                if (verbose & ~VERBOSE_DFID)
-                       llapi_printf(LLAPI_MSG_NORMAL, "lmm_fid:            ");
+                       llapi_printf(LLAPI_MSG_NORMAL, "%slmm_fid:           ",
+                                    space);
                /* This needs a bit of hand-holding since old 1.x lmm_oi
                 * have { oi.oi_id = mds_inum, oi.oi_seq = 0 } and 2.x lmm_oi
                 * have { oi.oi_id = mds_oid, oi.oi_seq = mds_seq } instead of
@@ -2158,10 +2171,10 @@ static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
 
        if (verbose & VERBOSE_COUNT) {
                if (verbose & ~VERBOSE_COUNT)
-                       llapi_printf(LLAPI_MSG_NORMAL, "%sstripe_count:   ",
-                                    prefix);
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s%sstripe_count:  ",
+                                    space, prefix);
                if (is_dir) {
-                       if (!raw && lum->lmm_stripe_count == 0) {
+                       if (!is_raw && lum->lmm_stripe_count == 0) {
                                unsigned int scount;
                                rc = sattr_cache_get_defaults(NULL, path,
                                                              &scount, NULL,
@@ -2189,9 +2202,9 @@ static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
        if (verbose & VERBOSE_SIZE) {
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (verbose & ~VERBOSE_SIZE)
-                       llapi_printf(LLAPI_MSG_NORMAL, "%sstripe_size:    ",
-                                    prefix);
-               if (is_dir && !raw && lum->lmm_stripe_size == 0) {
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s%sstripe_size:   ",
+                                    space, prefix);
+               if (is_dir && !is_raw && lum->lmm_stripe_size == 0) {
                        unsigned int ssize;
                        rc = sattr_cache_get_defaults(NULL, path, NULL, &ssize,
                                                      NULL);
@@ -2211,8 +2224,8 @@ static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
        if ((verbose & VERBOSE_LAYOUT) && !is_dir) {
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (verbose & ~VERBOSE_LAYOUT)
-                       llapi_printf(LLAPI_MSG_NORMAL, "%spattern:        ",
-                                    prefix);
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s%spattern:       ",
+                                    space, prefix);
                llapi_printf(LLAPI_MSG_NORMAL, "%.x", lum->lmm_pattern);
                separator = "\n";
        }
@@ -2220,8 +2233,8 @@ static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
        if ((verbose & VERBOSE_GENERATION) && !is_dir) {
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (verbose & ~VERBOSE_GENERATION)
-                       llapi_printf(LLAPI_MSG_NORMAL, "%slayout_gen:     ",
-                                    prefix);
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s%slayout_gen:    ",
+                                    space, prefix);
                llapi_printf(LLAPI_MSG_NORMAL, "%u",
                             (int)lum->lmm_layout_gen);
                separator = "\n";
@@ -2230,8 +2243,8 @@ static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
        if (verbose & VERBOSE_OFFSET) {
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (verbose & ~VERBOSE_OFFSET)
-                       llapi_printf(LLAPI_MSG_NORMAL, "%sstripe_offset:  ",
-                                    prefix);
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s%sstripe_offset: ",
+                                    space, prefix);
                if (is_dir)
                        llapi_printf(LLAPI_MSG_NORMAL, "%d",
                                     lum->lmm_stripe_offset ==
@@ -2243,49 +2256,70 @@ static void lov_dump_user_lmm_header(struct lov_user_md *lum, char *path,
                separator = is_dir ? " " : "\n";
        }
 
-       if ((verbose & VERBOSE_POOL) && (pool_name != NULL)) {
+       if ((verbose & VERBOSE_POOL) && pool_name && (pool_name[0] != '\0')) {
                llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
                if (verbose & ~VERBOSE_POOL)
-                       llapi_printf(LLAPI_MSG_NORMAL, "%spool:           ",
-                                    prefix);
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s%spool:          ",
+                                    space, prefix);
                llapi_printf(LLAPI_MSG_NORMAL, "%s", pool_name);
+               separator = is_dir ? " " : "\n";
        }
 
-       if (!is_dir || (is_dir && (verbose != VERBOSE_OBJID)))
+       if (strlen(separator) != 0)
                llapi_printf(LLAPI_MSG_NORMAL, "\n");
 }
 
 void lov_dump_user_lmm_v1v3(struct lov_user_md *lum, char *pool_name,
-                            struct lov_user_ost_data_v1 *objects,
-                            char *path, int is_dir, int obdindex,
-                            int depth, int header, int raw)
+                           struct lov_user_ost_data_v1 *objects,
+                           char *path, int obdindex, int depth,
+                           int header, enum lov_dump_flags flags)
 {
-        int i, obdstripe = (obdindex != OBD_NOT_FOUND) ? 0 : 1;
+       bool is_dir = flags & LDF_IS_DIR;
+       bool indent = flags & LDF_INDENT;
+       int i, obdstripe = (obdindex != OBD_NOT_FOUND) ? 0 : 1;
 
-        if (!obdstripe) {
-                for (i = 0; !is_dir && i < lum->lmm_stripe_count; i++) {
-                        if (obdindex == objects[i].l_ost_idx) {
-                                obdstripe = 1;
-                                break;
-                        }
-                }
-        }
+       if (!obdstripe) {
+               for (i = 0; !is_dir && i < lum->lmm_stripe_count; i++) {
+                       if (obdindex == objects[i].l_ost_idx) {
+                               obdstripe = 1;
+                               break;
+                       }
+               }
+       }
 
-        if (obdstripe == 1)
-                lov_dump_user_lmm_header(lum, path, objects, is_dir, header,
-                                         depth, raw, pool_name);
+       if (obdstripe == 0)
+               return;
 
-        if (!is_dir && (header & VERBOSE_OBJID) &&
+       lov_dump_user_lmm_header(lum, path, objects, header, depth, pool_name,
+                                flags);
+
+       if (!is_dir && (header & VERBOSE_OBJID) &&
            !(lum->lmm_pattern & LOV_PATTERN_F_RELEASED)) {
-                if (obdstripe == 1)
-                        llapi_printf(LLAPI_MSG_NORMAL,
-                                  "\tobdidx\t\t objid\t\t objid\t\t group\n");
-
-                for (i = 0; i < lum->lmm_stripe_count; i++) {
-                        int idx = objects[i].l_ost_idx;
-                        long long oid = ostid_id(&objects[i].l_ost_oi);
-                        long long gr = ostid_seq(&objects[i].l_ost_oi);
-                       if ((obdindex == OBD_NOT_FOUND) || (obdindex == idx)) {
+               char *space = "      - ";
+
+               if (indent)
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                                    "%6slmm_objects:\n", " ");
+               else
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                               "\tobdidx\t\t objid\t\t objid\t\t group\n");
+
+               for (i = 0; i < lum->lmm_stripe_count; i++) {
+                       int idx = objects[i].l_ost_idx;
+                       long long oid = ostid_id(&objects[i].l_ost_oi);
+                       long long gr = ostid_seq(&objects[i].l_ost_oi);
+
+                       if (obdindex != OBD_NOT_FOUND && obdindex != idx)
+                               continue;
+
+                       if (indent) {
+                               struct lu_fid fid = { 0 };
+
+                               ostid_to_fid(&fid, &objects[i].l_ost_oi, idx);
+                               llapi_printf(LLAPI_MSG_NORMAL,
+                                   "%s%d: { l_ost_idx: %d, l_fid: "DFID" }\n",
+                                   space, i, idx, PFID(&fid));
+                       } else {
                                char fmt[48];
                                sprintf(fmt, "%s%s%s\n",
                                        "\t%6u\t%14llu\t%#13llx\t",
@@ -2296,10 +2330,9 @@ void lov_dump_user_lmm_v1v3(struct lov_user_md *lum, char *pool_name,
                                             oid, gr,
                                             obdindex == idx ? " *" : "");
                        }
-
-                }
-                llapi_printf(LLAPI_MSG_NORMAL, "\n");
-        }
+               }
+               llapi_printf(LLAPI_MSG_NORMAL, "\n");
+       }
 }
 
 void lmv_dump_user_lmm(struct lmv_user_md *lum, char *pool_name,
@@ -2414,7 +2447,321 @@ void lmv_dump_user_lmm(struct lmv_user_md *lum, char *pool_name,
                llapi_printf(LLAPI_MSG_NORMAL, "\n");
 }
 
-void llapi_lov_dump_user_lmm(struct find_param *param, char *path, int is_dir)
+static void lov_dump_comp_v1_header(struct find_param *param, char *path,
+                                   enum lov_dump_flags flags)
+{
+       struct lov_comp_md_v1 *comp_v1 = (void *)&param->fp_lmd->lmd_lmm;
+       int depth = param->fp_max_depth;
+       int verbose = param->fp_verbose;
+
+       if (depth && path && ((verbose != VERBOSE_OBJID) ||
+                             !(flags & LDF_IS_DIR)))
+               llapi_printf(LLAPI_MSG_NORMAL, "%s\n", path);
+
+       if (verbose & VERBOSE_DETAIL) {
+               llapi_printf(LLAPI_MSG_NORMAL, "composite_header:\n");
+               llapi_printf(LLAPI_MSG_NORMAL, "%2slcm_magic:       0x%08X\n",
+                            " ", comp_v1->lcm_magic);
+               llapi_printf(LLAPI_MSG_NORMAL, "%2slcm_size:        %u\n",
+                            " ", comp_v1->lcm_size);
+               llapi_printf(LLAPI_MSG_NORMAL, "%2slcm_flags:       %u\n",
+                            " ", comp_v1->lcm_flags);
+       }
+
+       if (verbose & VERBOSE_GENERATION) {
+               if (verbose & ~VERBOSE_GENERATION)
+                       llapi_printf(LLAPI_MSG_NORMAL, "%2slcm_layout_gen:  ",
+                                    " ");
+               llapi_printf(LLAPI_MSG_NORMAL, "%u\n", comp_v1->lcm_layout_gen);
+       }
+
+       if (verbose & VERBOSE_COMP_COUNT) {
+               if (verbose & ~VERBOSE_COMP_COUNT)
+                       llapi_printf(LLAPI_MSG_NORMAL, "%2slcm_entry_count: ",
+                                    " ");
+               llapi_printf(LLAPI_MSG_NORMAL, "%u\n",
+                            comp_v1->lcm_entry_count);
+       }
+
+       if (verbose & VERBOSE_DETAIL)
+               llapi_printf(LLAPI_MSG_NORMAL, "components:\n");
+}
+
+static void lov_dump_comp_v1_entry(struct find_param *param,
+                                  enum lov_dump_flags flags, int index)
+{
+       struct lov_comp_md_v1 *comp_v1 = (void *)&param->fp_lmd->lmd_lmm;
+       struct lov_comp_md_entry_v1 *entry;
+       char *separator = "";
+       int verbose = param->fp_verbose;
+
+       entry = &comp_v1->lcm_entries[index];
+
+       if (verbose & VERBOSE_COMP_ID) {
+               if (verbose & VERBOSE_DETAIL)
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                                    "%slcme_id:             ", "  - ");
+               else if (verbose & ~VERBOSE_COMP_ID)
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                                    "%4slcme_id:             ", " ");
+               if (!(flags & LDF_IS_DIR))
+                       llapi_printf(LLAPI_MSG_NORMAL, "%u", entry->lcme_id);
+               else
+                       llapi_printf(LLAPI_MSG_NORMAL, "N/A");
+               separator = "\n";
+       }
+
+       if (verbose & VERBOSE_COMP_FLAGS) {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+               if (verbose & ~VERBOSE_COMP_FLAGS)
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                                    "%4slcme_flags:          ", " ");
+               llapi_printf(LLAPI_MSG_NORMAL, "%#x", entry->lcme_flags);
+               separator = "\n";
+       }
+
+       if (verbose & VERBOSE_COMP_START) {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+               if (verbose & ~VERBOSE_COMP_START)
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                                    "%4slcme_extent.e_start: ", " ");
+               llapi_printf(LLAPI_MSG_NORMAL, "%llu",
+                            entry->lcme_extent.e_start);
+               separator = "\n";
+       }
+
+       if (verbose & VERBOSE_COMP_END) {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+               if (verbose & ~VERBOSE_COMP_END)
+                       llapi_printf(LLAPI_MSG_NORMAL,
+                                    "%4slcme_extent.e_end:   ", " ");
+               if (entry->lcme_extent.e_end == LUSTRE_EOF)
+                       llapi_printf(LLAPI_MSG_NORMAL, "%s", "EOF");
+               else
+                       llapi_printf(LLAPI_MSG_NORMAL, "%llu",
+                                       entry->lcme_extent.e_end);
+               separator = "\n";
+       }
+
+       if (verbose & VERBOSE_DETAIL) {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+               llapi_printf(LLAPI_MSG_NORMAL, "%4slcme_offset:         %u\n",
+                            " ", entry->lcme_offset);
+               llapi_printf(LLAPI_MSG_NORMAL, "%4slcme_size:           %u\n",
+                            " ", entry->lcme_size);
+               llapi_printf(LLAPI_MSG_NORMAL, "%4ssub_layout:\n", " ");
+       } else {
+               llapi_printf(LLAPI_MSG_NORMAL, "%s", separator);
+       }
+}
+
+/* Check if the value matches 1 of the given criteria (e.g. --atime +/-N).
+ * @mds indicates if this is MDS timestamps and there are attributes on OSTs.
+ *
+ * The result is -1 if it does not match, 0 if not yet clear, 1 if matches.
+ * The table below gives the answers for the specified parameters (value and
+ * sign), 1st column is the answer for the MDS value, the 2nd is for the OST:
+ * --------------------------------------
+ * 1 | file > limit; sign > 0 | -1 / -1 |
+ * 2 | file = limit; sign > 0 | -1 / -1 |
+ * 3 | file < limit; sign > 0 |  ? /  1 |
+ * 4 | file > limit; sign = 0 | -1 / -1 |
+ * 5 | file = limit; sign = 0 |  ? /  1 |  <- (see the Note below)
+ * 6 | file < limit; sign = 0 |  ? / -1 |
+ * 7 | file > limit; sign < 0 |  1 /  1 |
+ * 8 | file = limit; sign < 0 |  ? / -1 |
+ * 9 | file < limit; sign < 0 |  ? / -1 |
+ * --------------------------------------
+ * Note: 5th actually means that the value is within the interval
+ * (limit - margin, limit]. */
+static int find_value_cmp(unsigned long long file, unsigned long long limit,
+                         int sign, int negopt, unsigned long long margin,
+                         int mds)
+{
+       int ret = -1;
+
+       if (sign > 0) {
+               /* Drop the fraction of margin (of days). */
+               if (file + margin <= limit)
+                       ret = mds ? 0 : 1;
+       } else if (sign == 0) {
+               if (file <= limit && file + margin > limit)
+                       ret = mds ? 0 : 1;
+               else if (file + margin <= limit)
+                       ret = mds ? 0 : -1;
+       } else if (sign < 0) {
+               if (file > limit)
+                       ret = 1;
+               else if (mds)
+                       ret = 0;
+       }
+
+       return negopt ? ~ret + 1 : ret;
+}
+
+static inline struct lov_user_md *
+lov_comp_entry(struct lov_comp_md_v1 *comp_v1, int ent_idx)
+{
+       return (struct lov_user_md *)((char *)comp_v1 +
+                       comp_v1->lcm_entries[ent_idx].lcme_offset);
+}
+
+static inline struct lov_user_ost_data_v1 *
+lov_v1v3_objects(struct lov_user_md *v1)
+{
+       if (v1->lmm_magic == LOV_USER_MAGIC_V3)
+               return ((struct lov_user_md_v3 *)v1)->lmm_objects;
+       else
+               return v1->lmm_objects;
+}
+
+static inline void
+lov_v1v3_pool_name(struct lov_user_md *v1, char *pool_name)
+{
+       if (v1->lmm_magic == LOV_USER_MAGIC_V3)
+               strlcpy(pool_name, ((struct lov_user_md_v3 *)v1)->lmm_pool_name,
+                       LOV_MAXPOOLNAME);
+       else
+               pool_name[0] = '\0';
+}
+
+/**
+ * An example of "getstripe -v" for a two components PFL file:
+ *
+ * composite_header:
+ * lcm_magic:       0x0BD60BD0
+ * lcm_size:        264
+ * lcm_flags:       0
+ * lcm_layout_gen:  2
+ * lcm_entry_count: 2
+ * components:
+ * - lcme_id:             1
+ *   lcme_flags:          0x10
+ *   lcme_extent.e_start: 0
+ *   lcme_extent.e_end:   1048576
+ *   lcme_offset:         128
+ *   lcme_size:           56
+ *   sub_layout:
+ *     lmm_magic:         0x0BD10BD0
+ *     lmm_seq:           0x200000401
+ *     lmm_object_id:     0x1
+ *     lmm_fid:           [0x200000401:0x1:0x0]
+ *     lmm_stripe_count:  1
+ *     lmm_stripe_size:   1048576
+ *     lmm_pattern:       1
+ *     lmm_layout_gen:    0
+ *     lmm_stripe_offset: 0
+ *     lmm_objects:
+ *     - 0: { l_ost_idx: 0, l_fid: [0x100000000:0x2:0x0] }
+ *
+ * - lcme_id:             2
+ *   lcme_flags:          0x10
+ *   lcme_extent.e_start: 1048576
+ *   lcme_extent.e_end:   EOF
+ *   lcme_offset:         184
+ *   lcme_size:           80
+ *     sub_layout:
+ *     lmm_magic:         0x0BD10BD0
+ *     lmm_seq:           0x200000401
+ *     lmm_object_id:     0x1
+ *     lmm_fid:           [0x200000401:0x1:0x0]
+ *     lmm_stripe_count:  2
+ *     lmm_stripe_size:   1048576
+ *     lmm_pattern:       1
+ *     lmm_layout_gen:    0
+ *     lmm_stripe_offset: 1
+ *     lmm_objects:
+ *     - 0: { l_ost_idx: 1, l_fid: [0x100010000:0x2:0x0] }
+ *     - 1: { l_ost_idx: 0, l_fid: [0x100000000:0x3:0x0] }
+ */
+static void lov_dump_comp_v1(struct find_param *param, char *path,
+                            enum lov_dump_flags flags)
+{
+       struct lov_comp_md_entry_v1 *entry;
+       struct lov_user_ost_data_v1 *objects;
+       struct lov_comp_md_v1 *comp_v1 = (void *)&param->fp_lmd->lmd_lmm;
+       struct lov_user_md_v1 *v1;
+       char pool_name[LOV_MAXPOOLNAME + 1];
+       int obdindex = param->fp_obd_index;
+       int i, j, match, obdstripe = 0;
+
+       if (obdindex != OBD_NOT_FOUND) {
+               for (i = 0; !(flags & LDF_IS_DIR) &&
+                           i < comp_v1->lcm_entry_count; i++) {
+                       if (!(comp_v1->lcm_entries[i].lcme_flags &
+                             LCME_FL_INIT))
+                               continue;
+
+                       v1 = lov_comp_entry(comp_v1, i);
+                       objects = lov_v1v3_objects(v1);
+
+                       for (j = 0; j < v1->lmm_stripe_count; j++) {
+                               if (obdindex == objects[j].l_ost_idx) {
+                                       obdstripe = 1;
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               obdstripe = 1;
+       }
+
+       if (obdstripe == 0)
+               return;
+
+       lov_dump_comp_v1_header(param, path, flags);
+
+       flags |= LDF_INDENT;
+
+       for (i = 0; i < comp_v1->lcm_entry_count; i++) {
+               entry = &comp_v1->lcm_entries[i];
+
+               if (param->fp_check_comp_flags &&
+                   !(param->fp_comp_flags & entry->lcme_flags))
+                       continue;
+
+               if (param->fp_check_comp_id &&
+                   param->fp_comp_id != entry->lcme_id)
+                       continue;
+
+               if (param->fp_check_comp_start) {
+                       match = find_value_cmp(entry->lcme_extent.e_start,
+                                              param->fp_comp_start,
+                                              param->fp_comp_start_sign,
+                                              0,
+                                              param->fp_comp_start_units, 0);
+                       if (match == -1)
+                               continue;
+               }
+
+               if (param->fp_check_comp_end) {
+                       unsigned long long margin;
+
+                       margin = entry->lcme_extent.e_end == LUSTRE_EOF ?
+                               0 : param->fp_comp_end_units;
+
+                       match = find_value_cmp(entry->lcme_extent.e_end,
+                                              param->fp_comp_end,
+                                              param->fp_comp_end_sign,
+                                              0, margin, 0);
+                       if (match == -1)
+                               continue;
+               }
+
+               lov_dump_comp_v1_entry(param, flags, i);
+
+               v1 = lov_comp_entry(comp_v1, i);
+               objects = lov_v1v3_objects(v1);
+               lov_v1v3_pool_name(v1, pool_name);
+
+               lov_dump_user_lmm_v1v3(v1, pool_name, objects, path, obdindex,
+                                      param->fp_max_depth, param->fp_verbose,
+                                      flags);
+       }
+}
+
+static void llapi_lov_dump_user_lmm(struct find_param *param, char *path,
+                                   enum lov_dump_flags flags)
 {
        __u32 magic;
 
@@ -2423,27 +2770,29 @@ void llapi_lov_dump_user_lmm(struct find_param *param, char *path, int is_dir)
        else
                magic = *(__u32 *)&param->fp_lmd->lmd_lmm; /* lum->lmm_magic */
 
+       if (param->fp_raw)
+               flags |= LDF_IS_RAW;
+
        switch (magic) {
-        case LOV_USER_MAGIC_V1:
+       case LOV_USER_MAGIC_V1:
                lov_dump_user_lmm_v1v3(&param->fp_lmd->lmd_lmm, NULL,
                                       param->fp_lmd->lmd_lmm.lmm_objects,
-                                      path, is_dir,
-                                      param->fp_obd_index, param->fp_max_depth,
-                                      param->fp_verbose, param->fp_raw);
-                break;
-        case LOV_USER_MAGIC_V3: {
-                char pool_name[LOV_MAXPOOLNAME + 1];
-                struct lov_user_ost_data_v1 *objects;
+                                      path, param->fp_obd_index,
+                                      param->fp_max_depth, param->fp_verbose,
+                                      flags);
+               break;
+       case LOV_USER_MAGIC_V3: {
+               char pool_name[LOV_MAXPOOLNAME + 1];
+               struct lov_user_ost_data_v1 *objects;
                struct lov_user_md_v3 *lmmv3 = (void *)&param->fp_lmd->lmd_lmm;
 
                strlcpy(pool_name, lmmv3->lmm_pool_name, sizeof(pool_name));
                objects = lmmv3->lmm_objects;
-               lov_dump_user_lmm_v1v3(&param->fp_lmd->lmd_lmm,
-                                      pool_name[0] == '\0' ? NULL : pool_name,
-                                      objects, path, is_dir,
-                                      param->fp_obd_index, param->fp_max_depth,
-                                      param->fp_verbose, param->fp_raw);
-                break;
+               lov_dump_user_lmm_v1v3(&param->fp_lmd->lmd_lmm, pool_name,
+                                      objects, path, param->fp_obd_index,
+                                      param->fp_max_depth, param->fp_verbose,
+                                      flags);
+               break;
         }
        case LMV_MAGIC_V1:
        case LMV_USER_MAGIC: {
@@ -2452,12 +2801,13 @@ void llapi_lov_dump_user_lmm(struct find_param *param, char *path, int is_dir)
 
                lum = (struct lmv_user_md *)param->fp_lmv_md;
                strlcpy(pool_name, lum->lum_pool_name, sizeof(pool_name));
-               lmv_dump_user_lmm(lum,
-                                 pool_name[0] == '\0' ? NULL : pool_name,
-                                 path, param->fp_obd_index,
+               lmv_dump_user_lmm(lum, pool_name, path, param->fp_obd_index,
                                  param->fp_max_depth, param->fp_verbose);
                break;
        }
+       case LOV_USER_MAGIC_COMP_V1:
+               lov_dump_comp_v1(param, path, flags);
+               break;
        default:
                llapi_printf(LLAPI_MSG_NORMAL, "unknown lmm_magic:  %#x "
                             "(expecting one of %#x %#x %#x %#x)\n",
@@ -2539,50 +2889,6 @@ int llapi_file_lookup(int dirfd, const char *name)
         return rc;
 }
 
-/* Check if the value matches 1 of the given criteria (e.g. --atime +/-N).
- * @mds indicates if this is MDS timestamps and there are attributes on OSTs.
- *
- * The result is -1 if it does not match, 0 if not yet clear, 1 if matches.
- * The table below gives the answers for the specified parameters (value and
- * sign), 1st column is the answer for the MDS value, the 2nd is for the OST:
- * --------------------------------------
- * 1 | file > limit; sign > 0 | -1 / -1 |
- * 2 | file = limit; sign > 0 | -1 / -1 |
- * 3 | file < limit; sign > 0 |  ? /  1 |
- * 4 | file > limit; sign = 0 | -1 / -1 |
- * 5 | file = limit; sign = 0 |  ? /  1 |  <- (see the Note below)
- * 6 | file < limit; sign = 0 |  ? / -1 |
- * 7 | file > limit; sign < 0 |  1 /  1 |
- * 8 | file = limit; sign < 0 |  ? / -1 |
- * 9 | file < limit; sign < 0 |  ? / -1 |
- * --------------------------------------
- * Note: 5th actually means that the value is within the interval
- * (limit - margin, limit]. */
-static int find_value_cmp(unsigned long long file, unsigned long long limit,
-                          int sign, int negopt, unsigned long long margin,
-                          int mds)
-{
-        int ret = -1;
-
-        if (sign > 0) {
-                /* Drop the fraction of margin (of days). */
-                if (file + margin <= limit)
-                        ret = mds ? 0 : 1;
-        } else if (sign == 0) {
-                if (file <= limit && file + margin > limit)
-                        ret = mds ? 0 : 1;
-                else if (file + margin <= limit)
-                        ret = mds ? 0 : -1;
-        } else if (sign < 0) {
-                if (file > limit)
-                        ret = 1;
-                else if (mds)
-                        ret = 0;
-        }
-
-        return negopt ? ~ret + 1 : ret;
-}
-
 /* Check if the file time matches all the given criteria (e.g. --atime +/-N).
  * Return -1 or 1 if file timestamp does not or does match the given criteria
  * correspondingly. Return 0 if the MDS time is being checked and there are
@@ -2641,9 +2947,11 @@ static int find_time_check(lstat_t *st, struct find_param *param, int mds)
  */
 static int check_obd_match(struct find_param *param)
 {
+       struct lov_user_ost_data_v1 *objects;
+       struct lov_comp_md_v1 *comp_v1 = NULL;
+       struct lov_user_md_v1 *v1 = &param->fp_lmd->lmd_lmm;
        lstat_t *st = &param->fp_lmd->lmd_st;
-       struct lov_user_ost_data_v1 *lmm_objects;
-       int i, j;
+       int i, j, k, count = 1;
 
        if (param->fp_obd_uuid && param->fp_obd_index == OBD_NOT_FOUND)
                return 0;
@@ -2653,37 +2961,27 @@ static int check_obd_match(struct find_param *param)
 
        /* Only those files should be accepted, which have a
         * stripe on the specified OST. */
-       if (!param->fp_lmd->lmd_lmm.lmm_stripe_count)
-               return 0;
+       if (v1->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
+               comp_v1 = (struct lov_comp_md_v1 *)v1;
+               count = comp_v1->lcm_entry_count;
+       }
 
-       if (param->fp_lmd->lmd_lmm.lmm_magic ==
-           LOV_USER_MAGIC_V3) {
-               struct lov_user_md_v3 *lmmv3 = (void *)&param->fp_lmd->lmd_lmm;
+       for (i = 0; i < count; i++) {
+               if (comp_v1)
+                       v1 = lov_comp_entry(comp_v1, i);
 
-               lmm_objects = lmmv3->lmm_objects;
-       } else if (param->fp_lmd->lmd_lmm.lmm_magic ==  LOV_USER_MAGIC_V1) {
-               lmm_objects = param->fp_lmd->lmd_lmm.lmm_objects;
-       } else {
-               llapi_err_noerrno(LLAPI_MSG_ERROR, "%s:Unknown magic: 0x%08X\n",
-                                 __func__, param->fp_lmd->lmd_lmm.lmm_magic);
-               return -EINVAL;
-       }
+               objects = lov_v1v3_objects(v1);
 
-       for (i = 0; i < param->fp_lmd->lmd_lmm.lmm_stripe_count; i++) {
-               for (j = 0; j < param->fp_num_obds; j++) {
-                       if (param->fp_obd_indexes[j] ==
-                           lmm_objects[i].l_ost_idx) {
-                               if (param->fp_exclude_obd)
-                                       return 0;
-                               return 1;
+               for (j = 0; j < v1->lmm_stripe_count; j++) {
+                       for (k = 0; k < param->fp_num_obds; k++) {
+                               if (param->fp_obd_indexes[k] ==
+                                   objects[j].l_ost_idx)
+                                       return !param->fp_exclude_obd;
                        }
                }
        }
 
-       if (param->fp_exclude_obd)
-                return 1;
-
-       return 0;
+       return param->fp_exclude_obd;
 }
 
 static int check_mdt_match(struct find_param *param)
@@ -2734,22 +3032,257 @@ static int print_failed_tgt(struct find_param *param, char *path, int type)
        return ret;
 }
 
+static int find_check_stripe_size(struct find_param *param)
+{
+       struct lov_comp_md_v1 *comp_v1 = NULL;
+       struct lov_user_md_v1 *v1 = &param->fp_lmd->lmd_lmm;
+       int ret, i, count = 1;
+
+       if (v1->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
+               comp_v1 = (struct lov_comp_md_v1 *)v1;
+               count = comp_v1->lcm_entry_count;
+               ret = param->fp_exclude_stripe_size ? 1 : -1;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (comp_v1)
+                       v1 = lov_comp_entry(comp_v1, i);
+
+               ret = find_value_cmp(v1->lmm_stripe_size, param->fp_stripe_size,
+                                    param->fp_stripe_size_sign,
+                                    param->fp_exclude_stripe_size,
+                                    param->fp_stripe_size_units, 0);
+               /* If any stripe_size matches */
+               if (ret != -1)
+                       break;
+       }
+
+       return ret;
+}
+
+static __u32 find_get_stripe_count(struct find_param *param)
+{
+       struct lov_comp_md_v1 *comp_v1 = NULL;
+       struct lov_user_md_v1 *v1 = &param->fp_lmd->lmd_lmm;
+       int i, count = 1;
+       __u32 stripe_count = 0;
+
+       if (v1->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
+               comp_v1 = (struct lov_comp_md_v1 *)v1;
+               count = comp_v1->lcm_entry_count;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (comp_v1)
+                       v1 = lov_comp_entry(comp_v1, i);
+               stripe_count += v1->lmm_stripe_count;
+       }
+
+       return stripe_count;
+}
+
+#define LOV_PATTERN_INVALID    0xFFFFFFFF
+
+static int find_check_layout(struct find_param *param)
+{
+       struct lov_comp_md_v1 *comp_v1 = NULL;
+       struct lov_user_md_v1 *v1 = &param->fp_lmd->lmd_lmm;
+       int i, count = 1;
+       bool found = false, valid = false;
+
+       if (v1->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
+               comp_v1 = (struct lov_comp_md_v1 *)v1;
+               count = comp_v1->lcm_entry_count;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (comp_v1)
+                       v1 = lov_comp_entry(comp_v1, i);
+
+               if (v1->lmm_pattern == LOV_PATTERN_INVALID)
+                       continue;
+
+               valid = true;
+               if (v1->lmm_pattern & param->fp_layout) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!valid)
+               return -1;
+
+       if ((found && !param->fp_exclude_layout) ||
+           (!found && param->fp_exclude_layout))
+               return 1;
+
+       return -1;
+}
+
+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;
+       int i, count = 1;
+       bool found = false;
+
+       if (v1->lmm_magic == LOV_USER_MAGIC_COMP_V1) {
+               comp_v1 = (struct lov_comp_md_v1 *)v1;
+               count = comp_v1->lcm_entry_count;
+               /* empty requested pool is taken as no pool search */
+               if (count == 0 && param->fp_poolname[0] == '\0')
+                       found = true;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (comp_v1 != NULL)
+                       v1 = lov_comp_entry(comp_v1, i);
+
+               if (((v1->lmm_magic == LOV_USER_MAGIC_V1) &&
+                    (param->fp_poolname[0] == '\0')) ||
+                   ((v1->lmm_magic == LOV_USER_MAGIC_V3) &&
+                    (strncmp(v3->lmm_pool_name,
+                             param->fp_poolname, LOV_MAXPOOLNAME) == 0)) ||
+                   ((v1->lmm_magic == LOV_USER_MAGIC_V3) &&
+                    (strcmp(param->fp_poolname, "*") == 0))) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if ((found && !param->fp_exclude_pool) ||
+           (!found && param->fp_exclude_pool))
+               return 1;
+
+       return -1;
+}
+
+static int find_check_comp_options(struct find_param *param)
+{
+       struct lov_comp_md_v1 *comp_v1;
+       struct lov_user_md_v1 *v1 = &param->fp_lmd->lmd_lmm;
+       struct lov_comp_md_entry_v1 *entry;
+       int i, ret;
+
+       if (v1->lmm_magic != LOV_USER_MAGIC_COMP_V1) {
+               if ((param->fp_check_comp_count &&
+                    !param->fp_exclude_comp_count) ||
+                   (param->fp_check_comp_flags &&
+                    !param->fp_exclude_comp_flags) ||
+                   (param->fp_check_comp_start &&
+                    !param->fp_exclude_comp_start) ||
+                   (param->fp_check_comp_end &&
+                    !param->fp_exclude_comp_end))
+                       return -1;
+               else
+                       return 1;
+       }
+
+       comp_v1 = (struct lov_comp_md_v1 *)v1;
+
+       if (param->fp_check_comp_count) {
+               ret = find_value_cmp(comp_v1->lcm_entry_count,
+                                    param->fp_comp_count,
+                                    param->fp_comp_count_sign,
+                                    param->fp_exclude_comp_count, 1, 0);
+               if (ret == -1)
+                       return ret;
+       }
+
+       if (comp_v1->lcm_entry_count == 0) {
+               if ((param->fp_check_comp_flags &&
+                    !param->fp_exclude_comp_flags) ||
+                   (param->fp_check_comp_start &&
+                    !param->fp_exclude_comp_start) ||
+                   (param->fp_check_comp_end &&
+                    !param->fp_exclude_comp_end))
+                       return -1;
+       }
+
+       if (param->fp_check_comp_flags) {
+               for (i = 0; i < comp_v1->lcm_entry_count; i++) {
+                       entry = &comp_v1->lcm_entries[i];
+
+                       if (((entry->lcme_flags & param->fp_comp_flags) &&
+                            param->fp_exclude_comp_flags) ||
+                           (!(entry->lcme_flags & param->fp_comp_flags) &&
+                            !param->fp_exclude_comp_flags))
+                               ret = -1;
+                       else
+                               ret = 1;
+                       /* If any flags matches */
+                       if (ret != -1)
+                               break;
+               }
+               if (ret == -1)
+                       return ret;
+       }
+
+       if (param->fp_check_comp_start) {
+               for (i = 0; i < comp_v1->lcm_entry_count; i++) {
+                       entry = &comp_v1->lcm_entries[i];
+
+                       ret = find_value_cmp(entry->lcme_extent.e_start,
+                                            param->fp_comp_start,
+                                            param->fp_comp_start_sign,
+                                            param->fp_exclude_comp_start,
+                                            param->fp_comp_start_units, 0);
+                       /* If any extent start matches */
+                       if (ret != -1)
+                               break;
+               }
+               if (ret == -1)
+                       return ret;
+       }
+
+       if (param->fp_check_comp_end) {
+               for (i = 0; i < comp_v1->lcm_entry_count; i++) {
+                       unsigned long long margin;
+                       entry = &comp_v1->lcm_entries[i];
+
+                       margin = entry->lcme_extent.e_end == LUSTRE_EOF ?
+                               0 : param->fp_comp_end_units;
+
+                       ret = find_value_cmp(entry->lcme_extent.e_end,
+                                            param->fp_comp_end,
+                                            param->fp_comp_end_sign,
+                                            param->fp_exclude_comp_end, margin,
+                                            0);
+                       /* If any extent end matches */
+                       if (ret != -1)
+                               break;
+               }
+               if (ret == -1)
+                       return ret;
+       }
+
+       return 1;
+}
+
+static bool find_check_lmm_info(struct find_param *param)
+{
+       return param->fp_check_pool || param->fp_check_stripe_count ||
+              param->fp_check_stripe_size || param->fp_check_layout ||
+              param->fp_check_comp_count || param->fp_check_comp_end ||
+              param->fp_check_comp_start || param->fp_check_comp_flags;
+}
+
 static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                        void *data, struct dirent64 *de)
 {
-        struct find_param *param = (struct find_param *)data;
+       struct find_param *param = (struct find_param *)data;
        DIR *dir = dirp == NULL ? NULL : *dirp;
-        int decision = 1; /* 1 is accepted; -1 is rejected. */
+       int decision = 1; /* 1 is accepted; -1 is rejected. */
        lstat_t *st = &param->fp_lmd->lmd_st;
-        int lustre_fs = 1;
-        int checked_type = 0;
-        int ret = 0;
+       int lustre_fs = 1;
+       int checked_type = 0;
+       int ret = 0;
+       __u32 stripe_count = 0;
 
        if (parent == NULL && dir == NULL)
                return -EINVAL;
 
-       param->fp_lmd->lmd_lmm.lmm_stripe_count = 0;
-
        /* If a regular expression is presented, make the initial decision */
        if (param->fp_pattern != NULL) {
                char *fname = strrchr(path, '/');
@@ -2780,9 +3313,7 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
        if (param->fp_obd_uuid || param->fp_mdt_uuid ||
            param->fp_check_uid || param->fp_check_gid ||
            param->fp_atime || param->fp_mtime || param->fp_ctime ||
-           param->fp_check_pool || param->fp_check_size ||
-           param->fp_check_stripe_count || param->fp_check_stripe_size ||
-           param->fp_check_layout)
+           param->fp_check_size || find_check_lmm_info(param))
                decision = 0;
 
        if (param->fp_type != 0 && checked_type == 0)
@@ -2792,8 +3323,7 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                ret = get_lmd_info(path, parent, dir, param->fp_lmd,
                                   param->fp_lum_size);
                if (ret == 0 && param->fp_lmd->lmd_lmm.lmm_magic == 0 &&
-                   (param->fp_check_pool || param->fp_check_stripe_count ||
-                    param->fp_check_stripe_size || param->fp_check_layout)) {
+                   find_check_lmm_info(param)) {
                        struct lov_user_md *lmm = &param->fp_lmd->lmd_lmm;
 
                        /* We need to "fake" the "use the default" values
@@ -2842,6 +3372,8 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
                                goto decided;
 
                        return ret;
+               } else {
+                       stripe_count = find_get_stripe_count(param);
                }
        }
 
@@ -2882,36 +3414,23 @@ static int cb_find_init(char *path, DIR *parent, DIR **dirp,
         }
 
        if (param->fp_check_stripe_size) {
-               decision = find_value_cmp(
-                               param->fp_lmd->lmd_lmm.lmm_stripe_size,
-                               param->fp_stripe_size,
-                               param->fp_stripe_size_sign,
-                               param->fp_exclude_stripe_size,
-                               param->fp_stripe_size_units, 0);
+               decision = find_check_stripe_size(param);
                if (decision == -1)
                        goto decided;
        }
 
        if (param->fp_check_stripe_count) {
-               decision = find_value_cmp(
-                               param->fp_lmd->lmd_lmm.lmm_stripe_count,
-                               param->fp_stripe_count,
-                               param->fp_stripe_count_sign,
-                               param->fp_exclude_stripe_count, 1, 0);
+               decision = find_value_cmp(stripe_count, param->fp_stripe_count,
+                                         param->fp_stripe_count_sign,
+                                         param->fp_exclude_stripe_count, 1, 0);
                if (decision == -1)
                        goto decided;
-        }
+       }
 
        if (param->fp_check_layout) {
-               __u32 found;
-
-               found = (param->fp_lmd->lmd_lmm.lmm_pattern & param->fp_layout);
-               if ((param->fp_lmd->lmd_lmm.lmm_pattern == 0xFFFFFFFF) ||
-                   (found && param->fp_exclude_layout) ||
-                   (!found && !param->fp_exclude_layout)) {
-                       decision = -1;
+               decision = find_check_layout(param);
+               if (decision == -1)
                        goto decided;
-               }
        }
 
        /* If an OBD UUID is specified but none matches, skip this file. */
@@ -2964,22 +3483,16 @@ obd_matches:
        }
 
        if (param->fp_check_pool) {
-               struct lov_user_md_v3 *lmmv3 = (void *)&param->fp_lmd->lmd_lmm;
+               decision = find_check_pool(param);
+               if (decision == -1)
+                       goto decided;
+       }
 
-               /* empty requested pool is taken as no pool search => V1 */
-               if (((param->fp_lmd->lmd_lmm.lmm_magic == LOV_USER_MAGIC_V1) &&
-                    (param->fp_poolname[0] == '\0')) ||
-                   ((param->fp_lmd->lmd_lmm.lmm_magic == LOV_USER_MAGIC_V3) &&
-                    (strncmp(lmmv3->lmm_pool_name,
-                             param->fp_poolname, LOV_MAXPOOLNAME) == 0)) ||
-                   ((param->fp_lmd->lmd_lmm.lmm_magic == LOV_USER_MAGIC_V3) &&
-                    (strcmp(param->fp_poolname, "*") == 0))) {
-                       if (param->fp_exclude_pool)
-                               goto decided;
-               } else {
-                       if (!param->fp_exclude_pool)
-                               goto decided;
-               }
+       if (param->fp_check_comp_count || param->fp_check_comp_flags ||
+           param->fp_check_comp_start || param->fp_check_comp_end) {
+               decision = find_check_comp_options(param);
+               if (decision == -1)
+                       goto decided;
        }
 
        /* Check the time on mds. */
@@ -2987,9 +3500,8 @@ obd_matches:
        if (param->fp_atime || param->fp_mtime || param->fp_ctime) {
                 int for_mds;
 
-               for_mds = lustre_fs ? (S_ISREG(st->st_mode) &&
-                                      param->fp_lmd->lmd_lmm.lmm_stripe_count)
-                       : 0;
+               for_mds = lustre_fs ?
+                       (S_ISREG(st->st_mode) && stripe_count) : 0;
                 decision = find_time_check(st, param, for_mds);
                 if (decision == -1)
                         goto decided;
@@ -2999,9 +3511,8 @@ obd_matches:
            The regular stat is almost of the same speed as some new
            'glimpse-size-ioctl'. */
 
-       if (param->fp_check_size && S_ISREG(st->st_mode) &&
-           param->fp_lmd->lmd_lmm.lmm_stripe_count)
-                decision = 0;
+       if (param->fp_check_size && S_ISREG(st->st_mode) && stripe_count)
+               decision = 0;
 
        if (param->fp_check_size && S_ISDIR(st->st_mode))
                decision = 0;
@@ -3365,7 +3876,7 @@ err_out:
 
 dump:
        if (!(param->fp_verbose & VERBOSE_MDTINDEX))
-                llapi_lov_dump_user_lmm(param, path, d ? 1 : 0);
+               llapi_lov_dump_user_lmm(param, path, d ? LDF_IS_DIR : 0);
 
 out:
        /* Do not get down anymore? */