Whamcloud - gitweb
LU-13152 llapi: llapi_layout_get_by_xattr groks DoM
[fs/lustre-release.git] / lustre / utils / liblustreapi_layout.c
index ffee3fc..aefde5d 100644 (file)
@@ -63,6 +63,7 @@ struct llapi_layout_comp {
        uint64_t                llc_timestamp;  /* snapshot timestamp */
        struct list_head        llc_list;       /* linked to the llapi_layout
                                                   components list */
+       bool            llc_ondisk;
 };
 
 /**
@@ -134,11 +135,11 @@ llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int lum_size)
                comp_v1 = (struct lov_comp_md_v1 *)lum;
 
        if (comp_v1 != NULL) {
-               __swab32s(&comp_v1->lcm_magic);
-               __swab32s(&comp_v1->lcm_size);
-               __swab32s(&comp_v1->lcm_layout_gen);
-               __swab16s(&comp_v1->lcm_flags);
-               __swab16s(&comp_v1->lcm_entry_count);
+               comp_v1->lcm_magic = __swab32(comp_v1->lcm_magic);
+               comp_v1->lcm_size = __swab32(comp_v1->lcm_size);
+               comp_v1->lcm_layout_gen = __swab32(comp_v1->lcm_layout_gen);
+               comp_v1->lcm_flags = __swab16(comp_v1->lcm_flags);
+               comp_v1->lcm_entry_count = __swab16(comp_v1->lcm_entry_count);
                ent_count = comp_v1->lcm_entry_count;
        } else {
                ent_count = 1;
@@ -147,13 +148,13 @@ llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int lum_size)
        for (i = 0; i < ent_count; i++) {
                if (comp_v1 != NULL) {
                        ent = &comp_v1->lcm_entries[i];
-                       __swab32s(&ent->lcme_id);
-                       __swab32s(&ent->lcme_flags);
-                       __swab64s(&ent->lcme_timestamp);
-                       __swab64s(&ent->lcme_extent.e_start);
-                       __swab64s(&ent->lcme_extent.e_end);
-                       __swab32s(&ent->lcme_offset);
-                       __swab32s(&ent->lcme_size);
+                       ent->lcme_id = __swab32(ent->lcme_id);
+                       ent->lcme_flags = __swab32(ent->lcme_flags);
+                       ent->lcme_timestamp = __swab64(ent->lcme_timestamp);
+                       ent->lcme_extent.e_start = __swab64(ent->lcme_extent.e_start);
+                       ent->lcme_extent.e_end = __swab64(ent->lcme_extent.e_end);
+                       ent->lcme_offset = __swab32(ent->lcme_offset);
+                       ent->lcme_size = __swab32(ent->lcme_size);
 
                        lum = (struct lov_user_md *)((char *)comp_v1 +
                                        ent->lcme_offset);
@@ -161,11 +162,11 @@ llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int lum_size)
                }
                obj_count = llapi_layout_objects_in_lum(lum, lum_size);
 
-               __swab32s(&lum->lmm_magic);
-               __swab32s(&lum->lmm_pattern);
-               __swab32s(&lum->lmm_stripe_size);
-               __swab16s(&lum->lmm_stripe_count);
-               __swab16s(&lum->lmm_stripe_offset);
+               lum->lmm_magic = __swab32(lum->lmm_magic);
+               lum->lmm_pattern = __swab32(lum->lmm_pattern);
+               lum->lmm_stripe_size = __swab32(lum->lmm_stripe_size);
+               lum->lmm_stripe_count = __swab16(lum->lmm_stripe_count);
+               lum->lmm_stripe_offset = __swab16(lum->lmm_stripe_offset);
 
                if (lum->lmm_magic != LOV_MAGIC_V1) {
                        struct lov_user_md_v3 *v3;
@@ -176,7 +177,7 @@ llapi_layout_swab_lov_user_md(struct lov_user_md *lum, int lum_size)
                }
 
                for (j = 0; j < obj_count; j++)
-                       __swab32s(&lod[j].l_ost_idx);
+                       lod[j].l_ost_idx = __swab32(lod[j].l_ost_idx);
        }
 }
 
@@ -548,8 +549,12 @@ struct llapi_layout *llapi_layout_get_by_xattr(void *lov_xattr,
                else if (v1->lmm_pattern == (LOV_PATTERN_RAID0 |
                                         LOV_PATTERN_OVERSTRIPING))
                        comp->llc_pattern = LLAPI_LAYOUT_OVERSTRIPING;
+               else if (v1->lmm_pattern == LOV_PATTERN_MDT)
+                       comp->llc_pattern = LLAPI_LAYOUT_MDT;
                else
-                       /* Lustre only supports RAID0 for now. */
+                       /* Lustre only supports RAID0, overstripping
+                        * and DoM for now.
+                        */
                        comp->llc_pattern = v1->lmm_pattern;
 
                if (v1->lmm_stripe_size == 0)
@@ -589,6 +594,7 @@ struct llapi_layout *llapi_layout_get_by_xattr(void *lov_xattr,
                        comp->llc_stripe_offset =
                                comp->llc_objects[0].l_ost_idx;
 
+               comp->llc_ondisk = true;
                list_add_tail(&comp->llc_list, &layout->llot_comp_list);
                layout->llot_cur_comp = comp;
        }
@@ -1572,6 +1578,15 @@ int llapi_layout_file_open(const char *path, int open_flags, mode_t mode,
                return -1;
        }
 
+       if (layout) {
+               rc = llapi_layout_sanity((struct llapi_layout *)layout, false,
+                                        !!(layout->llot_mirror_count > 1));
+               if (rc) {
+                       llapi_layout_sanity_perror(rc);
+                       return -1;
+               }
+       }
+
        /* Object creation must be postponed until after layout attributes
         * have been applied. */
        if (layout != NULL && (open_flags & O_CREAT))
@@ -1789,7 +1804,7 @@ int llapi_layout_comp_extent_get(const struct llapi_layout *layout,
 int llapi_layout_comp_extent_set(struct llapi_layout *layout,
                                 uint64_t start, uint64_t end)
 {
-       struct llapi_layout_comp *prev, *next, *comp;
+       struct llapi_layout_comp *comp;
 
        comp = __llapi_layout_cur_comp(layout);
        if (comp == NULL)
@@ -1800,29 +1815,6 @@ int llapi_layout_comp_extent_set(struct llapi_layout *layout,
                return -1;
        }
 
-       /*
-        * We need to make sure the extent to be set is valid: the new
-        * extent must be adjacent with the prev & next component.
-        */
-       if (comp->llc_list.prev != &layout->llot_comp_list) {
-               prev = list_entry(comp->llc_list.prev, typeof(*prev),
-                                 llc_list);
-               if (start != 0 && start != prev->llc_extent.e_end) {
-                       errno = EINVAL;
-                       return -1;
-               }
-       }
-
-       if (comp->llc_list.next != &layout->llot_comp_list) {
-               next = list_entry(comp->llc_list.next, typeof(*next),
-                                 llc_list);
-               if (next->llc_extent.e_start != 0 &&
-                   end != next->llc_extent.e_start) {
-                       errno = EINVAL;
-                       return -1;
-               }
-       }
-
        comp->llc_extent.e_start = start;
        comp->llc_extent.e_end = end;
        layout->llot_is_composite = true;
@@ -1971,6 +1963,7 @@ int llapi_layout_mirror_id_get(const struct llapi_layout *layout, uint32_t *id)
 int llapi_layout_comp_add(struct llapi_layout *layout)
 {
        struct llapi_layout_comp *last, *comp, *new;
+       bool composite = layout->llot_is_composite;
 
        comp = __llapi_layout_cur_comp(layout);
        if (comp == NULL)
@@ -1983,16 +1976,23 @@ int llapi_layout_comp_add(struct llapi_layout *layout)
        last = list_entry(layout->llot_comp_list.prev, typeof(*last),
                          llc_list);
 
-       if (new->llc_extent.e_end <= last->llc_extent.e_end) {
-               __llapi_comp_free(new);
-               errno = EINVAL;
-               return -1;
-       }
-       new->llc_extent.e_start = last->llc_extent.e_end;
-
        list_add_tail(&new->llc_list, &layout->llot_comp_list);
-       layout->llot_cur_comp = new;
+
+       /* We must mark the layout composite for the sanity check, but it may
+        * not stay that way if the check fails */
        layout->llot_is_composite = true;
+       layout->llot_cur_comp = new;
+
+       /* We need to set a temporary non-zero value for "end" when we call
+        * comp_extent_set, so we use LUSTRE_EOF-1, which is > all allowed
+        * for the end of the previous component.  (If we're adding this
+        * component, the end of the previous component cannot be EOF.) */
+       if (llapi_layout_comp_extent_set(layout, last->llc_extent.e_end,
+                                       LUSTRE_EOF - 1)) {
+               llapi_layout_comp_del(layout);
+               layout->llot_is_composite = composite;
+               return -1;
+       }
 
        return 0;
 }
@@ -2057,14 +2057,11 @@ int llapi_layout_comp_del(struct llapi_layout *layout)
                errno = EINVAL;
                return -1;
        }
-       /* It can't be the only one on the list */
-       if (comp->llc_list.prev == &layout->llot_comp_list) {
-               errno = EINVAL;
-               return -1;
-       }
-
        layout->llot_cur_comp =
                list_entry(comp->llc_list.prev, typeof(*comp), llc_list);
+       if (comp->llc_list.prev == &layout->llot_comp_list)
+               layout->llot_cur_comp = NULL;
+
        list_del_init(&comp->llc_list);
        __llapi_comp_free(comp);
 
@@ -2182,8 +2179,9 @@ int llapi_layout_comp_use(struct llapi_layout *layout,
 int llapi_layout_file_comp_add(const char *path,
                               const struct llapi_layout *layout)
 {
-       int rc, fd, lum_size, tmp_errno = 0;
-       struct lov_user_md *lum;
+       int rc, fd = -1, lum_size, tmp_errno = 0;
+       struct llapi_layout *existing_layout = NULL;
+       struct lov_user_md *lum = NULL;
 
        if (path == NULL || layout == NULL ||
            layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
@@ -2191,34 +2189,60 @@ int llapi_layout_file_comp_add(const char *path,
                return -1;
        }
 
-       lum = llapi_layout_to_lum(layout);
-       if (lum == NULL)
-               return -1;
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
 
-       if (lum->lmm_magic != LOV_USER_MAGIC_COMP_V1) {
-               free(lum);
-               errno = EINVAL;
-               return -1;
+       existing_layout = llapi_layout_get_by_fd(fd, 0);
+       if (existing_layout == NULL) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
        }
-       lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
 
-       fd = open(path, O_RDWR);
-       if (fd < 0) {
+       rc = llapi_layout_merge(&existing_layout, layout);
+       if (rc) {
                tmp_errno = errno;
                rc = -1;
                goto out;
        }
 
+       rc = llapi_layout_sanity(existing_layout, false, false);
+       if (rc) {
+               tmp_errno = errno;
+               llapi_layout_sanity_perror(rc);
+               rc = -1;
+               goto out;
+       }
+
+       lum = llapi_layout_to_lum(layout);
+       if (lum == NULL) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
+
+       if (lum->lmm_magic != LOV_USER_MAGIC_COMP_V1) {
+               tmp_errno = EINVAL;
+               rc = -1;
+               goto out;
+       }
+       lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
+
        rc = fsetxattr(fd, XATTR_LUSTRE_LOV".add", lum, lum_size, 0);
        if (rc < 0) {
                tmp_errno = errno;
-               close(fd);
                rc = -1;
                goto out;
        }
-       close(fd);
 out:
+       if (fd >= 0)
+               close(fd);
        free(lum);
+       llapi_layout_free(existing_layout);
        errno = tmp_errno;
        return rc;
 }
@@ -2234,18 +2258,20 @@ out:
  */
 int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags)
 {
-       int rc, fd, lum_size;
+       int rc = 0, fd = -1, lum_size, tmp_errno = 0;
        struct llapi_layout *layout;
-       struct llapi_layout_comp *comp;
-       struct lov_user_md *lum;
+       struct llapi_layout_comp *comp, *next;
+       struct llapi_layout *existing_layout = NULL;
+       struct lov_user_md *lum = NULL;
 
        if (path == NULL || id > LCME_ID_MAX || (flags & ~LCME_KNOWN_FLAGS)) {
                errno = EINVAL;
                return -1;
        }
 
-       /* Can only specify ID or flags, not both. */
-       if (id != 0 && flags != 0) {
+       /* Can only specify ID or flags, not both, not none. */
+       if ((id != LCME_ID_INVAL && flags != 0) ||
+           (id == LCME_ID_INVAL && flags == 0)) {
                errno = EINVAL;
                return -1;
        }
@@ -2257,8 +2283,9 @@ int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags)
        llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF);
        comp = __llapi_layout_cur_comp(layout);
        if (comp == NULL) {
-               llapi_layout_free(layout);
-               return -1;
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
        }
 
        comp->llc_id = id;
@@ -2266,38 +2293,155 @@ int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags)
 
        lum = llapi_layout_to_lum(layout);
        if (lum == NULL) {
-               llapi_layout_free(layout);
-               return -1;
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
        }
        lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
 
        fd = open(path, O_RDWR);
        if (fd < 0) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
+
+       existing_layout = llapi_layout_get_by_fd(fd, 0);
+       if (existing_layout == NULL) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
+
+       comp = NULL;
+       next = NULL;
+       while (rc == 0 && existing_layout->llot_cur_comp != NULL) {
+               rc = llapi_layout_comp_use(existing_layout, comp ?
+                                          LLAPI_LAYOUT_COMP_USE_PREV :
+                                          LLAPI_LAYOUT_COMP_USE_LAST);
+               if (rc != 0)
+                       break;
+
+               next = comp;
+               comp = __llapi_layout_cur_comp(existing_layout);
+               if (comp == NULL) {
+                       rc = -1;
+                       break;
+               }
+
+               if (id != LCME_ID_INVAL && id != comp->llc_id)
+                       continue;
+               else if ((flags & LCME_FL_NEG) && (flags & comp->llc_flags))
+                       continue;
+               else if (flags && !(flags & comp->llc_flags))
+                       continue;
+
+               rc = llapi_layout_comp_del(existing_layout);
+               /* the layout position is moved to previous one, adjust */
+               comp = next;
+       }
+       if (rc < 0) {
+               tmp_errno = errno;
+               goto out;
+       }
+
+       rc = llapi_layout_sanity(existing_layout, false, false);
+       if (rc) {
+               tmp_errno = errno;
+               llapi_layout_sanity_perror(rc);
                rc = -1;
                goto out;
        }
 
        rc = fsetxattr(fd, XATTR_LUSTRE_LOV".del", lum, lum_size, 0);
        if (rc < 0) {
-               int tmp_errno = errno;
-               close(fd);
-               errno = tmp_errno;
+               tmp_errno = errno;
                rc = -1;
                goto out;
        }
-       close(fd);
+
 out:
+       if (fd >= 0)
+               close(fd);
        free(lum);
        llapi_layout_free(layout);
+       llapi_layout_free(existing_layout);
+       errno = tmp_errno;
+
        return rc;
 }
 
+/* Internal utility function to apply flags for sanity checking */
+static void llapi_layout_comp_apply_flags(struct llapi_layout_comp *comp,
+                                         uint32_t flags)
+{
+       if (flags & LCME_FL_NEG)
+               comp->llc_flags &= ~flags;
+       else
+               comp->llc_flags |= flags;
+}
+
+struct llapi_layout_apply_flags_args {
+       uint32_t *lfa_ids;
+       uint32_t *lfa_flags;
+       int lfa_count;
+       int lfa_rc;
+};
+
+
+static int llapi_layout_apply_flags_cb(struct llapi_layout *layout,
+                                      void *arg)
+{
+       struct llapi_layout_apply_flags_args *args = arg;
+       struct llapi_layout_comp *comp;
+       int i = 0;
+
+       comp = __llapi_layout_cur_comp(layout);
+       if (comp == NULL) {
+               args->lfa_rc = -1;
+               return LLAPI_LAYOUT_ITER_STOP;
+       }
+
+       for (i = 0; i < args->lfa_count; i++) {
+               if (comp->llc_id == args->lfa_ids[i])
+                       llapi_layout_comp_apply_flags(comp, args->lfa_flags[i]);
+       }
+
+       return LLAPI_LAYOUT_ITER_CONT;
+}
+
+/* Apply flags to the layout for sanity checking */
+static int llapi_layout_apply_flags(struct llapi_layout *layout, uint32_t *ids,
+                                   uint32_t *flags, int count)
+{
+       struct llapi_layout_apply_flags_args args;
+       int rc = 0;
+
+       if (!ids || !flags || count == 0) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       args.lfa_ids = ids;
+       args.lfa_flags = flags;
+       args.lfa_count = count;
+       args.lfa_rc = 0;
+
+       rc = llapi_layout_comp_iterate(layout,
+                                      llapi_layout_apply_flags_cb,
+                                      &args);
+       if (errno == ENOENT)
+               errno = 0;
+
+       if (rc != LLAPI_LAYOUT_ITER_CONT)
+               rc = args.lfa_rc;
+
+       return rc;
+}
 /**
- * Change flags or other parameters of the component(s) by component ID of an
- * existing file. The component to be modified is specified by the
- * comp->lcme_id value, which must be an unique component ID. The new
- * attributes are passed in by @comp and @valid is used to specify which
- * attributes in the component are going to be changed.
+ * Change flags by component ID of components of an existing file.
+ * The component to be modified is specified by the comp->lcme_id value,
+ * which must be a unique component ID.
  *
  * \param[in] path     path name of the file
  * \param[in] ids      An array of component IDs
@@ -2308,9 +2452,10 @@ out:
 int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags,
                               size_t count)
 {
-       int rc = -1, fd = -1, i;
+       int rc = -1, fd = -1, i, tmp_errno = 0;
        size_t lum_size;
-       struct llapi_layout *layout;
+       struct llapi_layout *existing_layout = NULL;
+       struct llapi_layout *layout = NULL;
        struct llapi_layout_comp *comp;
        struct lov_user_md *lum = NULL;
 
@@ -2340,15 +2485,49 @@ int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags,
                }
        }
 
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
+
+       existing_layout = llapi_layout_get_by_fd(fd, 0);
+       if (existing_layout == NULL) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
+
+       if (llapi_layout_apply_flags(existing_layout, ids, flags, count)) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
+
+       rc = llapi_layout_sanity(existing_layout, false, false);
+       if (rc) {
+               tmp_errno = errno;
+               llapi_layout_sanity_perror(rc);
+               rc = -1;
+               goto out;
+       }
+
        layout = __llapi_layout_alloc();
-       if (layout == NULL)
-               return -1;
+       if (layout == NULL) {
+               tmp_errno = errno;
+               rc = -1;
+               goto out;
+       }
 
        layout->llot_is_composite = true;
        for (i = 0; i < count; i++) {
                comp = __llapi_comp_alloc(0);
-               if (comp == NULL)
+               if (comp == NULL) {
+                       tmp_errno = errno;
+                       rc = -1;
                        goto out;
+               }
 
                comp->llc_id = ids[i];
                comp->llc_flags = flags[i];
@@ -2358,39 +2537,38 @@ int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags,
        }
 
        lum = llapi_layout_to_lum(layout);
-       if (lum == NULL)
+       if (lum == NULL) {
+               tmp_errno = errno;
+               rc = -1;
                goto out;
+       }
 
        lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size;
 
-       fd = open(path, O_RDWR);
-       if (fd < 0)
-               goto out;
-
        /* flush cached pages from clients */
        rc = llapi_file_flush(fd);
        if (rc) {
-               errno = -rc;
+               tmp_errno = -rc;
                rc = -1;
-               goto out_close;
+               goto out;
        }
 
        rc = fsetxattr(fd, XATTR_LUSTRE_LOV".set.flags", lum, lum_size, 0);
-       if (rc < 0)
-               goto out_close;
+       if (rc < 0) {
+               tmp_errno = errno;
+               goto out;
+       }
 
        rc = 0;
 
-out_close:
-       if (fd >= 0) {
-               int tmp_errno = errno;
-               close(fd);
-               errno = tmp_errno;
-       }
 out:
-       if (lum)
-               free(lum);
+       if (fd >= 0)
+               close(fd);
+
+       free(lum);
+       llapi_layout_free(existing_layout);
        llapi_layout_free(layout);
+       errno = tmp_errno;
        return rc;
 }
 
@@ -2681,6 +2859,7 @@ int llapi_mirror_resync_many(int fd, struct llapi_layout *layout,
        uint64_t pos = start;
        int i;
        int rc;
+       int rc2 = 0;
 
        rc = posix_memalign(&buf, page_size, buflen);
        if (rc)
@@ -2759,6 +2938,11 @@ int llapi_mirror_resync_many(int fd, struct llapi_layout *layout,
                                 * meanings.
                                 */
                                comp_array[i].lrc_synced = true;
+                               llapi_error(LLAPI_MSG_ERROR, written,
+                                           "component %u not synced",
+                                           comp_array[i].lrc_id);
+                               if (rc2 == 0)
+                                       rc2 = (int)written;
                                continue;
                        }
                        assert(written == to_write2);
@@ -2771,11 +2955,17 @@ int llapi_mirror_resync_many(int fd, struct llapi_layout *layout,
        free(buf);
 
        if (rc < 0) {
+               /* fatal error happens */
                for (i = 0; i < comp_size; i++)
                        comp_array[i].lrc_synced = false;
                return rc;
        }
 
+       /**
+        * no fatal error happens, each lrc_synced tells whether the component
+        * has been resync successfully (note: we'd reverse the value to
+        * reflect its true meaning.
+        */
        for (i = 0; i < comp_size; i++) {
                comp_array[i].lrc_synced = !comp_array[i].lrc_synced;
                if (comp_array[i].lrc_synced && pos & (page_size - 1)) {
@@ -2786,6 +2976,460 @@ int llapi_mirror_resync_many(int fd, struct llapi_layout *layout,
                }
        }
 
-       /* partially successful is successful */
+       /**
+        * returns the first error code for partially successful resync if
+        * possible.
+        */
+       return rc2;
+}
+
+enum llapi_layout_comp_sanity_error {
+       LSE_OK,
+       LSE_INCOMPLETE_MIRROR,
+       LSE_ADJACENT_EXTENSION,
+       LSE_INIT_EXTENSION,
+       LSE_FLAGS,
+       LSE_DOM_EXTENSION,
+       LSE_DOM_EXTENSION_FOLLOWING,
+       LSE_DOM_FIRST,
+       LSE_SET_COMP_START,
+       LSE_NOT_ZERO_LENGTH_EXTENDABLE,
+       LSE_END_NOT_GREATER,
+       LSE_ZERO_LENGTH_NORMAL,
+       LSE_NOT_ADJACENT_PREV,
+       LSE_START_GT_END,
+       LSE_ALIGN_END,
+       LSE_ALIGN_EXT,
+       LSE_LAST,
+};
+
+const char *llapi_layout_strerror[] =
+{
+       [LSE_OK] = "",
+       [LSE_INCOMPLETE_MIRROR] =
+               "Incomplete mirror - must go to EOF",
+       [LSE_ADJACENT_EXTENSION] =
+               "No adjacent extension space components",
+       [LSE_INIT_EXTENSION] =
+               "Cannot apply extension flag to init components",
+       [LSE_FLAGS] =
+               "Wrong flags",
+       [LSE_DOM_EXTENSION] =
+               "DoM components can't be extension space",
+       [LSE_DOM_EXTENSION_FOLLOWING] =
+               "DoM components cannot be followed by extension space",
+       [LSE_DOM_FIRST] =
+               "DoM component should be the first one in a file/mirror",
+       [LSE_SET_COMP_START] =
+               "Must set previous component extent before adding next",
+       [LSE_NOT_ZERO_LENGTH_EXTENDABLE] =
+               "Extendable component must start out zero-length",
+       [LSE_END_NOT_GREATER] =
+               "Component end is before end of previous component",
+       [LSE_ZERO_LENGTH_NORMAL] =
+               "Zero length components must be followed by extension",
+       [LSE_NOT_ADJACENT_PREV] =
+               "Components not adjacent (end != next->start",
+       [LSE_START_GT_END] =
+               "Component start is > end",
+       [LSE_ALIGN_END] =
+               "The component end must be aligned by the stripe size",
+       [LSE_ALIGN_EXT] =
+               "The extension size must be aligned by the stripe size",
+};
+
+struct llapi_layout_sanity_args {
+       bool lsa_incomplete;
+       bool lsa_flr;
+       bool lsa_ondisk;
+       int lsa_rc;
+};
+
+static int llapi_layout_sanity_cb(struct llapi_layout *layout,
+                                 void *arg)
+{
+       struct llapi_layout_comp *comp, *next, *prev;
+       struct llapi_layout_sanity_args *args = arg;
+       bool first_comp = false;
+
+       comp = __llapi_layout_cur_comp(layout);
+       if (comp == NULL) {
+               args->lsa_rc = -1;
+               goto out_err;
+       }
+
+       if (comp->llc_list.prev != &layout->llot_comp_list)
+               prev = list_entry(comp->llc_list.prev, typeof(*prev),
+                                 llc_list);
+       else
+               prev = NULL;
+
+       if (comp->llc_list.next != &layout->llot_comp_list)
+               next = list_entry(comp->llc_list.next, typeof(*next),
+                                 llc_list);
+       else
+               next = NULL;
+
+       /* Start of zero implies a new mirror */
+       if (comp->llc_extent.e_start == 0) {
+               first_comp = true;
+               /* Most checks apply only within one mirror, this is an
+                * exception. */
+               if (prev && prev->llc_extent.e_end != LUSTRE_EOF) {
+                       args->lsa_rc = LSE_INCOMPLETE_MIRROR;
+                       goto out_err;
+               }
+
+               prev = NULL;
+       }
+
+       if (next && next->llc_extent.e_start == 0)
+               next = NULL;
+
+       /* Flag sanity checks */
+       /* No adjacent extension components */
+       if ((comp->llc_flags & LCME_FL_EXTENSION) && next &&
+           (next->llc_flags & LCME_FL_EXTENSION)) {
+               args->lsa_rc = LSE_ADJACENT_EXTENSION;
+               goto out_err;
+       }
+
+       /* Extension flag cannot be applied to init components and the first
+        * component of each mirror is automatically init */
+       if ((comp->llc_flags & LCME_FL_EXTENSION) &&
+           (comp->llc_flags & LCME_FL_INIT || first_comp)) {
+               args->lsa_rc = LSE_INIT_EXTENSION;
+               goto out_err;
+       }
+
+       if (comp->llc_ondisk) {
+               if (comp->llc_flags & LCME_FL_NEG)
+                       args->lsa_rc = LSE_FLAGS;
+       } else if (!args->lsa_incomplete) {
+               if (args->lsa_flr) {
+                       if (comp->llc_flags & ~LCME_USER_COMP_FLAGS)
+                               args->lsa_rc = LSE_FLAGS;
+               } else {
+                       if (comp->llc_flags & ~LCME_FL_EXTENSION)
+                               args->lsa_rc = LSE_FLAGS;
+               }
+       }
+       if (args->lsa_rc)
+               goto out_err;
+
+       /* DoM sanity checks */
+       if (comp->llc_pattern == LLAPI_LAYOUT_MDT ||
+           comp->llc_pattern == LOV_PATTERN_MDT) {
+               /* DoM components can't be extension components */
+               if (comp->llc_flags & LCME_FL_EXTENSION) {
+                       args->lsa_rc = LSE_DOM_EXTENSION;
+                       goto out_err;
+               }
+               /* DoM components cannot be followed by an extension comp */
+               if (next && (next->llc_flags & LCME_FL_EXTENSION)) {
+                       args->lsa_rc = LSE_DOM_EXTENSION_FOLLOWING;
+                       goto out_err;
+               }
+
+               /* DoM should be the first component in a mirror */
+               if (!first_comp) {
+                       args->lsa_rc = LSE_DOM_FIRST;
+                       errno = EINVAL;
+                       goto out_err;
+               }
+       }
+
+       /* Extent sanity checks */
+       /* Must set previous component extent before adding another */
+       if (prev && prev->llc_extent.e_start == 0 &&
+           prev->llc_extent.e_end == 0) {
+               args->lsa_rc = LSE_SET_COMP_START;
+               goto out_err;
+       }
+
+       if (!args->lsa_incomplete) {
+               /* Components followed by extension space (extendable
+                * components) must be zero length before initialization.
+                * (Except for first comp, which will be initialized on
+                * creation). */
+               if (next && (next->llc_flags & LCME_FL_EXTENSION) &&
+                   !first_comp && !(comp->llc_flags & LCME_FL_INIT) &&
+                   comp->llc_extent.e_start != comp->llc_extent.e_end) {
+                       args->lsa_rc = LSE_NOT_ZERO_LENGTH_EXTENDABLE;
+                       goto out_err;
+               }
+
+               /* End must come after end of previous comp */
+               if (prev && comp->llc_extent.e_end < prev->llc_extent.e_end) {
+                       args->lsa_rc = LSE_END_NOT_GREATER;
+                       goto out_err;
+               }
+
+               /* Components not followed by ext space must have length > 0. */
+               if (comp->llc_extent.e_start == comp->llc_extent.e_end &&
+                   (next == NULL || !(next->llc_flags & LCME_FL_EXTENSION))) {
+                       args->lsa_rc = LSE_ZERO_LENGTH_NORMAL;
+                       goto out_err;
+               }
+
+               /* The component end must be aligned by the stripe size */
+               if ((comp->llc_flags & LCME_FL_EXTENSION) &&
+                   (prev->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) {
+                       if (comp->llc_extent.e_end != LUSTRE_EOF &&
+                           comp->llc_extent.e_end % prev->llc_stripe_size) {
+                               args->lsa_rc = LSE_ALIGN_END;
+                               goto out_err;
+                       }
+                       if ((comp->llc_stripe_size * SEL_UNIT_SIZE) %
+                           prev->llc_stripe_size) {
+                               args->lsa_rc = LSE_ALIGN_EXT;
+                               goto out_err;
+                       }
+               } else if (!(comp->llc_flags & LCME_FL_EXTENSION) &&
+                          (comp->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) {
+                       if (comp->llc_extent.e_end != LUSTRE_EOF &&
+                           comp->llc_extent.e_end % comp->llc_stripe_size) {
+                               args->lsa_rc = LSE_ALIGN_END;
+                               goto out_err;
+                       }
+               }
+       }
+
+       /* Components must have start == prev->end */
+       if (prev && comp->llc_extent.e_start != 0 &&
+           comp->llc_extent.e_start != prev->llc_extent.e_end) {
+               args->lsa_rc = LSE_NOT_ADJACENT_PREV;
+               goto out_err;
+       }
+
+       /* Components must have start <= end */
+       if (comp->llc_extent.e_start > comp->llc_extent.e_end) {
+               args->lsa_rc = LSE_START_GT_END;
+               goto out_err;
+       }
+
+       return LLAPI_LAYOUT_ITER_CONT;
+
+out_err:
+       errno = errno ? errno : EINVAL;
+       return LLAPI_LAYOUT_ITER_STOP;
+}
+
+/* Print explanation of layout error */
+void llapi_layout_sanity_perror(int error)
+{
+       if (error >= LSE_LAST || error < 0) {
+               fprintf(stdout, "Invalid layout, unrecognized error: %d\n",
+                       error);
+       } else {
+               fprintf(stdout, "Invalid layout: %s\n",
+                       llapi_layout_strerror[error]);
+       }
+}
+
+/* Walk a layout and enforce sanity checks that apply to > 1 component
+ *
+ * The core idea here is that of sanity checking individual tokens vs semantic
+ * checking.
+ * We cannot check everything at the individual component level ('token'),
+ * instead we must check whether or not the full layout has a valid meaning.
+ *
+ * An example of a component level check is "is stripe size valid?".  That is
+ * handled when setting stripe size.
+ *
+ * An example of a layout level check is "are the extents of these components
+ * valid when adjacent to one another", or "can we set these flags on adjacent
+ * components"?
+ *
+ * \param[in] layout            component layout list.
+ * \param[in] incomplete        if layout is complete or not - some checks can
+ *                              only be done on complete layouts.
+ * \param[in] flr              set when this is called from FLR mirror create
+ *
+ * \retval                      0, success, positive: various errors, see
+ *                              llapi_layout_sanity_perror, -1, failure
+ */
+int llapi_layout_sanity(struct llapi_layout *layout, bool incomplete, bool flr)
+{
+       struct llapi_layout_sanity_args args;
+       struct llapi_layout_comp *curr;
+       int rc = 0;
+
+       if (!layout)
+               return 0;
+
+       curr = layout->llot_cur_comp;
+       if (!curr)
+               return 0;
+
+       /* Set up args */
+       args.lsa_rc = 0;
+       args.lsa_flr = flr;
+       args.lsa_incomplete = incomplete;
+
+       /* When we modify an existing layout, this tells us if it's FLR */
+       if (mirror_id_of(curr->llc_id) > 0)
+               args.lsa_flr = true;
+
+       errno = 0;
+       rc = llapi_layout_comp_iterate(layout,
+                                      llapi_layout_sanity_cb,
+                                      &args);
+       if (errno == ENOENT)
+               errno = 0;
+
+       if (rc != LLAPI_LAYOUT_ITER_CONT)
+               rc = args.lsa_rc;
+
+       layout->llot_cur_comp = curr;
+
+       return rc;
+}
+
+int llapi_layout_dom_size(struct llapi_layout *layout, uint64_t *size)
+{
+       uint64_t pattern, start;
+       int rc;
+
+       if (!layout || !llapi_layout_is_composite(layout)) {
+               *size = 0;
+               return 0;
+       }
+
+       rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
+       if (rc)
+               return -errno;
+
+       rc = llapi_layout_pattern_get(layout, &pattern);
+       if (rc)
+               return -errno;
+
+       if (pattern != LOV_PATTERN_MDT && pattern != LLAPI_LAYOUT_MDT) {
+               *size = 0;
+               return 0;
+       }
+
+       rc = llapi_layout_comp_extent_get(layout, &start, size);
+       if (rc)
+               return -errno;
+       if (start)
+               return -ERANGE;
+       return 0;
+}
+
+int lov_comp_md_size(struct lov_comp_md_v1 *lcm)
+{
+       if (lcm->lcm_magic == LOV_MAGIC_V1 || lcm->lcm_magic == LOV_MAGIC_V3) {
+               struct lov_user_md *lum = (void *)lcm;
+
+               return lov_user_md_size(lum->lmm_stripe_count, lum->lmm_magic);
+       }
+
+       if (lcm->lcm_magic == LOV_MAGIC_FOREIGN) {
+               struct lov_foreign_md *lfm = (void *)lcm;
+
+               return lfm->lfm_length;
+       }
+
+       if (lcm->lcm_magic != LOV_MAGIC_COMP_V1)
+               return -EOPNOTSUPP;
+
+       return lcm->lcm_size;
+}
+
+int llapi_get_lum_file_fd(int dir_fd, const char *fname, __u64 *valid,
+                         lstatx_t *statx, struct lov_user_md *lum,
+                         size_t lumsize)
+{
+       struct lov_user_mds_data *lmd;
+       char buf[65536 + offsetof(typeof(*lmd), lmd_lmm)];
+       int parent_fd = -1;
+       int rc;
+
+       if (lum && lumsize < sizeof(*lum))
+               return -EINVAL;
+
+       /* If a file name is provided, it is relative to the parent directory */
+       if (fname) {
+               parent_fd = dir_fd;
+               dir_fd = -1;
+       }
+
+       lmd = (struct lov_user_mds_data *)buf;
+       rc = get_lmd_info_fd(fname, parent_fd, dir_fd, buf, sizeof(buf),
+                            GET_LMD_INFO);
+       if (rc)
+               return rc;
+
+       *valid = lmd->lmd_flags;
+       if (statx)
+               memcpy(statx, &lmd->lmd_stx, sizeof(*statx));
+
+       if (lum) {
+               if (lmd->lmd_lmmsize > lumsize)
+                       return -EOVERFLOW;
+               memcpy(lum, &lmd->lmd_lmm, lmd->lmd_lmmsize);
+       }
+
        return 0;
 }
+
+int llapi_get_lum_dir_fd(int dir_fd, __u64 *valid, lstatx_t *statx,
+                        struct lov_user_md *lum, size_t lumsize)
+{
+       return llapi_get_lum_file_fd(dir_fd, NULL, valid, statx, lum, lumsize);
+}
+
+int llapi_get_lum_file(const char *path, __u64 *valid, lstatx_t *statx,
+                      struct lov_user_md *lum, size_t lumsize)
+{
+       char parent[PATH_MAX];
+       const char *fname;
+       char *tmp;
+       int offset;
+       int dir_fd;
+       int rc;
+
+       tmp = strrchr(path, '/');
+       if (!tmp) {
+               strncpy(parent, ".", sizeof(parent) - 1);
+               offset = -1;
+       } else {
+               strncpy(parent, path, tmp - path);
+               offset = tmp - path - 1;
+               parent[tmp - path] = 0;
+       }
+
+       fname = path;
+       if (offset >= 0)
+               fname += offset + 2;
+
+       dir_fd = open(parent, O_RDONLY);
+       if (dir_fd < 0) {
+               rc = -errno;
+               llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
+               return rc;
+       }
+
+       rc = llapi_get_lum_file_fd(dir_fd, fname, valid, statx, lum, lumsize);
+       close(dir_fd);
+       return rc;
+}
+
+int llapi_get_lum_dir(const char *path, __u64 *valid, lstatx_t *statx,
+                     struct lov_user_md *lum, size_t lumsize)
+{
+       int dir_fd;
+       int rc;
+
+       dir_fd = open(path, O_RDONLY);
+       if (dir_fd < 0) {
+               rc = -errno;
+               llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'", path);
+               return rc;
+       }
+
+       rc = llapi_get_lum_dir_fd(dir_fd, valid, statx, lum, lumsize);
+       close(dir_fd);
+       return rc;
+}