Whamcloud - gitweb
LU-10830 utils: fix create mode for lfs setstripe
[fs/lustre-release.git] / lustre / utils / liblustreapi_layout.c
index 557765d..e85b39d 100644 (file)
@@ -23,7 +23,7 @@
  * Lustre files while hiding details of the internal data structures
  * from the user.
  *
- * Copyright (c) 2016, Intel Corporation.
+ * Copyright (c) 2016, 2017, Intel Corporation.
  *
  * Author: Ned Bass <bass6@llnl.gov>
  */
 #include <unistd.h>
 #include <errno.h>
 #include <limits.h>
+#include <assert.h>
 #include <sys/xattr.h>
+#include <sys/param.h>
 
 #include <libcfs/util/list.h>
 #include <lustre/lustreapi.h>
-#include <lustre/lustre_idl.h>
 #include "lustreapi_internal.h"
 
 /**
@@ -71,6 +72,7 @@ struct llapi_layout {
        uint32_t        llot_gen;
        uint32_t        llot_flags;
        bool            llot_is_composite;
+       uint16_t        llot_mirror_count;
        /* Cursor pointing to one of the components in llot_comp_list */
        struct llapi_layout_comp *llot_cur_comp;
        struct list_head          llot_comp_list;
@@ -318,6 +320,7 @@ static struct llapi_layout *__llapi_layout_alloc(void)
        layout->llot_gen = 0;
        layout->llot_flags = 0;
        layout->llot_is_composite = false;
+       layout->llot_mirror_count = 1;
        layout->llot_cur_comp = NULL;
        INIT_LIST_HEAD(&layout->llot_comp_list);
 
@@ -378,7 +381,9 @@ llapi_layout_from_lum(const struct lov_user_md *lum, int lum_size)
        if (lum->lmm_magic == LOV_MAGIC_COMP_V1) {
                comp_v1 = (struct lov_comp_md_v1 *)lum;
                ent_count = comp_v1->lcm_entry_count;
+               layout->llot_gen = comp_v1->lcm_layout_gen;
                layout->llot_is_composite = true;
+               layout->llot_mirror_count = comp_v1->lcm_mirror_count + 1;
                layout->llot_gen = comp_v1->lcm_layout_gen;
                layout->llot_flags = comp_v1->lcm_flags;
        } else if (lum->lmm_magic == LOV_MAGIC_V1 ||
@@ -507,7 +512,7 @@ llapi_layout_to_lum(const struct llapi_layout *layout)
                        comp_cnt++;
 
                lum_size = sizeof(*comp_v1) + comp_cnt * sizeof(*ent);
-               lum = malloc(lum_size);
+               lum = calloc(lum_size, 1);
                if (lum == NULL) {
                        errno = ENOMEM;
                        return NULL;
@@ -516,8 +521,9 @@ llapi_layout_to_lum(const struct llapi_layout *layout)
                comp_v1->lcm_magic = LOV_USER_MAGIC_COMP_V1;
                comp_v1->lcm_size = lum_size;
                comp_v1->lcm_layout_gen = 0;
-               comp_v1->lcm_flags = 0;
+               comp_v1->lcm_flags = layout->llot_flags;
                comp_v1->lcm_entry_count = comp_cnt;
+               comp_v1->lcm_mirror_count = layout->llot_mirror_count - 1;
                offset += lum_size;
        }
 
@@ -567,9 +573,9 @@ llapi_layout_to_lum(const struct llapi_layout *layout)
 
                blob->lmm_magic = magic;
                if (pattern == LLAPI_LAYOUT_DEFAULT)
-                       blob->lmm_pattern = 0;
-               else if (pattern == LLAPI_LAYOUT_RAID0)
                        blob->lmm_pattern = LOV_PATTERN_RAID0;
+               else if (pattern == LLAPI_LAYOUT_MDT)
+                       blob->lmm_pattern = LOV_PATTERN_MDT;
                else
                        blob->lmm_pattern = pattern;
 
@@ -732,7 +738,7 @@ static bool is_any_specified(const struct llapi_layout *layout)
        if (comp == NULL)
                return false;
 
-       if (layout->llot_is_composite)
+       if (layout->llot_is_composite || layout->llot_mirror_count != 1)
                return true;
 
        return comp->llc_pattern != LLAPI_LAYOUT_DEFAULT ||
@@ -1011,7 +1017,7 @@ struct llapi_layout *llapi_layout_get_by_path(const char *path, uint32_t flags)
  * \retval     NULL if an error occurs
  */
 struct llapi_layout *llapi_layout_get_by_fid(const char *lustre_dir,
-                                            const lustre_fid *fid,
+                                            const struct lu_fid *fid,
                                             uint32_t flags)
 {
        int fd;
@@ -1207,7 +1213,7 @@ int llapi_layout_pattern_get(const struct llapi_layout *layout,
 }
 
 /**
- * Set the RAID pattern of \a layout.
+ * Set the pattern of \a layout.
  *
  * \param[in] layout   layout to set pattern in
  * \param[in] pattern  value to be set
@@ -1225,7 +1231,7 @@ int llapi_layout_pattern_set(struct llapi_layout *layout, uint64_t pattern)
                return -1;
 
        if (pattern != LLAPI_LAYOUT_DEFAULT &&
-           pattern != LLAPI_LAYOUT_RAID0) {
+           pattern != LLAPI_LAYOUT_RAID0 && pattern != LLAPI_LAYOUT_MDT) {
                errno = EOPNOTSUPP;
                return -1;
        }
@@ -1425,7 +1431,7 @@ int llapi_layout_pool_name_set(struct llapi_layout *layout,
  *
  * \param[in] path             name of the file to open
  * \param[in] open_flags       open() flags
- * \param[in] mode             permissions to create new file with
+ * \param[in] mode             permissions to create file, filtered by umask
  * \param[in] layout           layout to create new file with
  *
  * \retval             non-negative file descriptor on successful open
@@ -1508,6 +1514,118 @@ int llapi_layout_file_create(const char *path, int open_flags, int mode,
                                      layout);
 }
 
+int llapi_layout_flags_get(struct llapi_layout *layout, uint32_t *flags)
+{
+       if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *flags = layout->llot_flags;
+       return 0;
+}
+
+/**
+ * Set flags to the header of a component layout.
+ */
+int llapi_layout_flags_set(struct llapi_layout *layout, uint32_t flags)
+{
+       if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       layout->llot_flags = flags;
+       return 0;
+}
+
+const char *llapi_layout_flags_string(uint32_t flags)
+{
+       switch (flags & LCM_FL_FLR_MASK) {
+       case LCM_FL_RDONLY:
+               return "ro";
+       case LCM_FL_WRITE_PENDING:
+               return "wp";
+       case LCM_FL_SYNC_PENDING:
+               return "sp";
+       }
+
+       return "0";
+}
+
+const __u16 llapi_layout_string_flags(char *string)
+{
+       if (strncmp(string, "ro", strlen(string)) == 0)
+               return LCM_FL_RDONLY;
+       if (strncmp(string, "wp", strlen(string)) == 0)
+               return LCM_FL_WRITE_PENDING;
+       if (strncmp(string, "sp", strlen(string)) == 0)
+               return LCM_FL_SYNC_PENDING;
+
+       return 0;
+}
+
+/**
+ * llapi_layout_mirror_count_is_valid() - Check the validity of mirror count.
+ * @count: Mirror count value to be checked.
+ *
+ * This function checks the validity of mirror count.
+ *
+ * Return: true on success or false on failure.
+ */
+static bool llapi_layout_mirror_count_is_valid(uint16_t count)
+{
+       return count >= 0 && count <= LUSTRE_MIRROR_COUNT_MAX;
+}
+
+/**
+ * llapi_layout_mirror_count_get() - Get mirror count from the header of
+ *                                  a layout.
+ * @layout: Layout to get mirror count from.
+ * @count:  Returned mirror count value.
+ *
+ * This function gets mirror count from the header of a layout.
+ *
+ * Return: 0 on success or -1 on failure.
+ */
+int llapi_layout_mirror_count_get(struct llapi_layout *layout,
+                                 uint16_t *count)
+{
+       if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *count = layout->llot_mirror_count;
+       return 0;
+}
+
+/**
+ * llapi_layout_mirror_count_set() - Set mirror count to the header of a layout.
+ * @layout: Layout to set mirror count in.
+ * @count:  Mirror count value to be set.
+ *
+ * This function sets mirror count to the header of a layout.
+ *
+ * Return: 0 on success or -1 on failure.
+ */
+int llapi_layout_mirror_count_set(struct llapi_layout *layout,
+                                 uint16_t count)
+{
+       if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (!llapi_layout_mirror_count_is_valid(count)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       layout->llot_mirror_count = count;
+       return 0;
+}
+
 /**
  * Fetch the start and end offset of the current layout component.
  *
@@ -1569,7 +1687,7 @@ int llapi_layout_comp_extent_set(struct llapi_layout *layout,
        if (comp->llc_list.prev != &layout->llot_comp_list) {
                prev = list_entry(comp->llc_list.prev, typeof(*prev),
                                  llc_list);
-               if (start != prev->llc_extent.e_end) {
+               if (start != 0 && start != prev->llc_extent.e_end) {
                        errno = EINVAL;
                        return -1;
                }
@@ -1578,7 +1696,8 @@ int llapi_layout_comp_extent_set(struct llapi_layout *layout,
        if (comp->llc_list.next != &layout->llot_comp_list) {
                next = list_entry(comp->llc_list.next, typeof(*next),
                                  llc_list);
-               if (end != next->llc_extent.e_start) {
+               if (next->llc_extent.e_start != 0 &&
+                   end != next->llc_extent.e_start) {
                        errno = EINVAL;
                        return -1;
                }
@@ -1691,6 +1810,33 @@ int llapi_layout_comp_id_get(const struct llapi_layout *layout, uint32_t *id)
 }
 
 /**
+ * Return the mirror id of the current layout component.
+ *
+ * \param[in] layout   the layout component
+ * \param[out] id      stored the returned mirror ID
+ *
+ * \retval     0 on success
+ * \retval     <0 if error occurs
+ */
+int llapi_layout_mirror_id_get(const struct llapi_layout *layout, uint32_t *id)
+{
+       struct llapi_layout_comp *comp;
+
+       comp = __llapi_layout_cur_comp(layout);
+       if (comp == NULL)
+               return -1;
+
+       if (id == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       *id = mirror_id_of(comp->llc_id);
+
+       return 0;
+}
+
+/**
  * Adds a component to \a layout, the new component will be added to
  * the tail of components list and it'll inherit attributes of existing
  * ones. The \a layout will change it's current component pointer to
@@ -1717,12 +1863,6 @@ int llapi_layout_comp_add(struct llapi_layout *layout)
        last = list_entry(layout->llot_comp_list.prev, typeof(*last),
                          llc_list);
 
-       /* Inherit some attributes from existing component */
-       new->llc_stripe_size = comp->llc_stripe_size;
-       new->llc_stripe_count = comp->llc_stripe_count;
-       if (comp->llc_pool_name[0] != '\0')
-               strncpy(new->llc_pool_name, comp->llc_pool_name,
-                       sizeof(comp->llc_pool_name));
        if (new->llc_extent.e_end <= last->llc_extent.e_end) {
                __llapi_comp_free(new);
                errno = EINVAL;
@@ -1736,6 +1876,37 @@ int llapi_layout_comp_add(struct llapi_layout *layout)
 
        return 0;
 }
+/**
+ * Adds a first component of a mirror to \a layout.
+ * The \a layout will change it's current component pointer to
+ * the newly added component, and it'll be turned into a composite
+ * layout if it was not before the adding.
+ *
+ * \param[in] layout           existing composite or plain layout
+ *
+ * \retval     0 on success
+ * \retval     <0 if error occurs
+ */
+int llapi_layout_add_first_comp(struct llapi_layout *layout)
+{
+       struct llapi_layout_comp *comp, *new;
+
+       comp = __llapi_layout_cur_comp(layout);
+       if (comp == NULL)
+               return -1;
+
+       new = __llapi_comp_alloc(0);
+       if (new == NULL)
+               return -1;
+
+       new->llc_extent.e_start = 0;
+
+       list_add_tail(&new->llc_list, &layout->llot_comp_list);
+       layout->llot_cur_comp = new;
+       layout->llot_is_composite = true;
+
+       return 0;
+}
 
 /**
  * Deletes current component from the composite layout. The component
@@ -1842,8 +2013,11 @@ int llapi_layout_comp_use(struct llapi_layout *layout,
                return -1;
 
        if (!layout->llot_is_composite) {
-               errno = EINVAL;
-               return -1;
+               if (pos == LLAPI_LAYOUT_COMP_USE_FIRST ||
+                   pos == LLAPI_LAYOUT_COMP_USE_LAST)
+                       return 0;
+               errno = ENOENT;
+               return 1;
        }
 
        head = list_entry(layout->llot_comp_list.next, typeof(*head), llc_list);
@@ -1962,6 +2136,11 @@ 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;
+       }
+
        comp->llc_id = id;
        comp->llc_flags = flags;
 
@@ -1999,11 +2178,412 @@ out:
  * 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.
+ *
+ * \param[in] path     path name of the file
+ * \param[in] ids      An array of component IDs
+ * \param[in] flags    flags: LCME_FL_* or;
+ *                     negative flags: (LCME_FL_NEG|LCME_FL_*)
+ * \param[in] count    Number of elements in ids and flags array
  */
-int llapi_layout_file_comp_set(const char *path,
-                              const struct llapi_layout *comp,
-                              uint32_t valid)
+int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags,
+                              size_t count)
 {
-       errno = EOPNOTSUPP;
+       int rc = -1, fd = -1, i;
+       size_t lum_size;
+       struct llapi_layout *layout;
+       struct llapi_layout_comp *comp;
+       struct lov_user_md *lum = NULL;
+
+       if (path == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (!count)
+               return 0;
+
+       for (i = 0; i < count; i++) {
+               if (!ids[i] || !flags[i]) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               if (ids[i] > LCME_ID_MAX || (flags[i] & ~LCME_KNOWN_FLAGS)) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               /* do not allow to set or clear INIT flag */
+               if (flags[i] & LCME_FL_INIT) {
+                       errno = EINVAL;
+                       return -1;
+               }
+       }
+
+       layout = __llapi_layout_alloc();
+       if (layout == NULL)
+               return -1;
+
+       layout->llot_is_composite = true;
+       for (i = 0; i < count; i++) {
+               comp = __llapi_comp_alloc(0);
+               if (comp == NULL)
+                       goto out;
+
+               comp->llc_id = ids[i];
+               comp->llc_flags = flags[i];
+
+               list_add_tail(&comp->llc_list, &layout->llot_comp_list);
+               layout->llot_cur_comp = comp;
+       }
+
+       lum = llapi_layout_to_lum(layout);
+       if (lum == NULL)
+               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;
+               rc = -1;
+               goto out_close;
+       }
+
+       rc = fsetxattr(fd, XATTR_LUSTRE_LOV".set.flags", lum, lum_size, 0);
+       if (rc < 0)
+               goto out_close;
+
+       rc = 0;
+
+out_close:
+       if (fd >= 0) {
+               int tmp_errno = errno;
+               close(fd);
+               errno = tmp_errno;
+       }
+out:
+       if (lum)
+               free(lum);
+       llapi_layout_free(layout);
+       return rc;
+}
+
+/**
+ * Check if the file layout is composite.
+ *
+ * \param[in] layout   the file layout to check
+ *
+ * \retval true                composite
+ * \retval false       not composite
+ */
+bool llapi_layout_is_composite(struct llapi_layout *layout)
+{
+       return layout->llot_is_composite;
+}
+
+/**
+ * Iterate every components in the @layout and call callback function @cb.
+ *
+ * \param[in] layout   component layout list.
+ * \param[in] cb       callback for each component
+ * \param[in] cbdata   callback data
+ *
+ * \retval < 0                         error happens during the iteration
+ * \retval LLAPI_LAYOUT_ITER_CONT      finished the iteration w/o error
+ * \retval LLAPI_LAYOUT_ITER_STOP      got something, stop the iteration
+ */
+int llapi_layout_comp_iterate(struct llapi_layout *layout,
+                             llapi_layout_iter_cb cb, void *cbdata)
+{
+       int rc;
+
+       rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
+       if (rc < 0)
+               return rc;
+
+       /**
+        * make sure on success llapi_layout_comp_use() API returns 0 with
+        * USE_FIRST.
+        */
+       assert(rc == 0);
+
+       while (1) {
+               rc = cb(layout, cbdata);
+               if (rc != LLAPI_LAYOUT_ITER_CONT)
+                       break;
+
+               rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
+               if (rc < 0)
+                       return rc;
+               else if (rc == 1)       /* reached the last comp */
+                       return LLAPI_LAYOUT_ITER_CONT;
+       }
+
+       return rc;
+}
+
+/**
+ * llapi_layout_merge() - Merge a composite layout into another one.
+ * @dst_layout: Destination composite layout.
+ * @src_layout: Source composite layout.
+ *
+ * This function copies all of the components from @src_layout and
+ * appends them to @dst_layout.
+ *
+ * Return: 0 on success or -1 on failure.
+ */
+int llapi_layout_merge(struct llapi_layout **dst_layout,
+                      const struct llapi_layout *src_layout)
+{
+       struct llapi_layout *new_layout = *dst_layout;
+       struct llapi_layout_comp *new = NULL;
+       struct llapi_layout_comp *comp = NULL;
+       int i = 0;
+
+       if (src_layout == NULL ||
+           list_empty((struct list_head *)&src_layout->llot_comp_list))
+               return 0;
+
+       if (new_layout == NULL) {
+               new_layout = __llapi_layout_alloc();
+               if (new_layout == NULL) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+       }
+
+       list_for_each_entry(comp, &src_layout->llot_comp_list, llc_list) {
+               new = __llapi_comp_alloc(0);
+               if (new == NULL) {
+                       errno = ENOMEM;
+                       goto error;
+               }
+
+               new->llc_pattern = comp->llc_pattern;
+               new->llc_stripe_size = comp->llc_stripe_size;
+               new->llc_stripe_count = comp->llc_stripe_count;
+               new->llc_stripe_offset = comp->llc_stripe_offset;
+
+               if (comp->llc_pool_name[0] != '\0')
+                       strncpy(new->llc_pool_name, comp->llc_pool_name,
+                               sizeof(new->llc_pool_name));
+
+               for (i = 0; i < comp->llc_objects_count; i++) {
+                       if (__llapi_comp_objects_realloc(new,
+                           stripe_number_roundup(i)) < 0) {
+                               errno = EINVAL;
+                               __llapi_comp_free(new);
+                               goto error;
+                       }
+                       new->llc_objects[i].l_ost_idx = \
+                               comp->llc_objects[i].l_ost_idx;
+               }
+
+               new->llc_objects_count = comp->llc_objects_count;
+               new->llc_extent.e_start = comp->llc_extent.e_start;
+               new->llc_extent.e_end = comp->llc_extent.e_end;
+               new->llc_id = comp->llc_id;
+               new->llc_flags = comp->llc_flags;
+
+               list_add_tail(&new->llc_list, &new_layout->llot_comp_list);
+               new_layout->llot_cur_comp = new;
+       }
+       new_layout->llot_is_composite = true;
+
+       *dst_layout = new_layout;
+       return 0;
+error:
+       llapi_layout_free(new_layout);
        return -1;
 }
+
+/**
+ * Find all stale components.
+ *
+ * \param[in] layout           component layout list.
+ * \param[out] comp            array of stale component info.
+ * \param[in] comp_size                array size of @comp.
+ * \param[in] mirror_ids       array of mirror id that only components
+ *                             belonging to these mirror will be collected.
+ * \param[in] ids_nr           number of mirror ids array.
+ *
+ * \retval             number of component info collected on sucess or
+ *                     an error code on failure.
+ */
+int llapi_mirror_find_stale(struct llapi_layout *layout,
+               struct llapi_resync_comp *comp, size_t comp_size,
+               __u16 *mirror_ids, int ids_nr)
+{
+       int idx = 0;
+       int rc;
+
+       rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
+       if (rc < 0)
+               goto error;
+
+       while (rc == 0) {
+               uint32_t id;
+               uint32_t mirror_id;
+               uint32_t flags;
+               uint64_t start, end;
+
+               rc = llapi_layout_comp_flags_get(layout, &flags);
+               if (rc < 0)
+                       goto error;
+
+               if (!(flags & LCME_FL_STALE))
+                       goto next;
+
+               rc = llapi_layout_mirror_id_get(layout, &mirror_id);
+               if (rc < 0)
+                       goto error;
+
+               /* the caller only wants stale components from specific
+                * mirrors */
+               if (ids_nr > 0) {
+                       int j;
+
+                       for (j = 0; j < ids_nr; j++) {
+                               if (mirror_ids[j] == mirror_id)
+                                       break;
+                       }
+
+                       /* not in the specified mirror */
+                       if (j == ids_nr)
+                               goto next;
+               }
+
+               rc = llapi_layout_comp_id_get(layout, &id);
+               if (rc < 0)
+                       goto error;
+
+               rc = llapi_layout_comp_extent_get(layout, &start, &end);
+               if (rc < 0)
+                       goto error;
+
+               /* pack this component into @comp array */
+               comp[idx].lrc_id = id;
+               comp[idx].lrc_mirror_id = mirror_id;
+               comp[idx].lrc_start = start;
+               comp[idx].lrc_end = end;
+               idx++;
+
+               if (idx >= comp_size) {
+                       rc = -EINVAL;
+                       goto error;
+               }
+
+       next:
+               rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
+               if (rc < 0) {
+                       rc = -EINVAL;
+                       goto error;
+               }
+       }
+error:
+       return rc < 0 ? rc : idx;
+}
+
+/* locate @layout to a valid component covering file [file_start, file_end) */
+static uint32_t llapi_mirror_find(struct llapi_layout *layout,
+                                 uint64_t file_start, uint64_t file_end,
+                                 uint64_t *endp)
+{
+       uint32_t mirror_id = 0;
+       int rc;
+
+       rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
+       if (rc < 0)
+               return rc;
+
+       *endp = 0;
+       while (rc == 0) {
+               uint64_t start, end;
+               uint32_t flags, id, rid;
+
+               rc = llapi_layout_comp_flags_get(layout, &flags);
+               if (rc < 0)
+                       return rc;
+
+               if (flags & LCME_FL_STALE)
+                       goto next;
+
+               rc = llapi_layout_mirror_id_get(layout, &rid);
+               if (rc < 0)
+                       return rc;
+
+               rc = llapi_layout_comp_id_get(layout, &id);
+               if (rc < 0)
+                       return rc;
+
+               rc = llapi_layout_comp_extent_get(layout, &start, &end);
+               if (rc < 0)
+                       return rc;
+
+               if (file_start >= start && file_start < end) {
+                       if (!mirror_id)
+                               mirror_id = rid;
+                       else if (mirror_id != rid || *endp != start)
+                               break;
+
+                       file_start = *endp = end;
+                       if (end >= file_end)
+                               break;
+               }
+
+       next:
+               rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
+               if (rc < 0)
+                       return rc;
+       }
+
+       return mirror_id;
+}
+
+ssize_t llapi_mirror_resync_one(int fd, struct llapi_layout *layout,
+                               uint32_t dst, uint64_t start, uint64_t end)
+{
+       uint64_t mirror_end = 0;
+       ssize_t result = 0;
+       size_t count;
+
+       if (end == OBD_OBJECT_EOF)
+               count = OBD_OBJECT_EOF;
+       else
+               count = end - start;
+
+       while (count > 0) {
+               uint32_t src;
+               size_t to_copy;
+               ssize_t copied;
+
+               src = llapi_mirror_find(layout, start, end, &mirror_end);
+               if (src == 0)
+                       return -ENOENT;
+
+               if (mirror_end == OBD_OBJECT_EOF)
+                       to_copy = count;
+               else
+                       to_copy = MIN(count, mirror_end - start);
+
+               copied = llapi_mirror_copy(fd, src, dst, start, to_copy);
+               if (copied < 0)
+                       return copied;
+
+               result += copied;
+               if (copied < to_copy) /* end of file */
+                       break;
+
+               if (count != OBD_OBJECT_EOF)
+                       count -= copied;
+               start += copied;
+       }
+
+       return result;
+}