+
+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_FLR,
+ 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,
+};
+
+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 and FLR are not supported together */
+ if (args->lsa_flr && first_comp) {
+ args->lsa_rc = LSE_DOM_FLR;
+ errno = ENOTSUP;
+ 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 && !(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)
+{
+ char *msg = NULL;
+
+ switch (error) {
+ case LSE_OK:
+ break;
+ case LSE_INCOMPLETE_MIRROR:
+ msg = "Incomplete mirror - must go to EOF";
+ break;
+ case LSE_ADJACENT_EXTENSION:
+ msg = "No adjacent extension space components";
+ break;
+ case LSE_INIT_EXTENSION:
+ msg = "Cannot apply extension flag to init components";
+ break;
+ case LSE_FLAGS:
+ msg = "Wrong flags";
+ break;
+ case LSE_DOM_EXTENSION:
+ msg = "DoM components can't be extension space";
+ break;
+ case LSE_DOM_EXTENSION_FOLLOWING:
+ msg = "DoM components cannot be followed by extension space";
+ break;
+ case LSE_DOM_FLR:
+ msg = "FLR and DoM are not supported together";
+ break;
+ case LSE_SET_COMP_START:
+ msg = "Must set previous component extent before adding next";
+ break;
+ case LSE_NOT_ZERO_LENGTH_EXTENDABLE:
+ msg = "Extendable component must start out zero-length";
+ break;
+ case LSE_END_NOT_GREATER:
+ msg = "Component end is before end of previous component";
+ break;
+ case LSE_ZERO_LENGTH_NORMAL:
+ msg = "Zero length components must be followed by extension";
+ break;
+ case LSE_NOT_ADJACENT_PREV:
+ msg = "Components not adjacent (end != next->start";
+ break;
+ case LSE_START_GT_END:
+ msg = "Component start is > end";
+ case LSE_ALIGN_END:
+ msg = "The component end must be aligned by the stripe size";
+ break;
+ case LSE_ALIGN_EXT:
+ msg = "The extension size must be aligned by the stripe size";
+ break;
+ default:
+ fprintf(stdout, "Invalid layout, unrecognized error: %d\n",
+ error);
+ }
+
+ if (msg)
+ fprintf(stdout, "Invalid layout: %s\n", msg);
+}
+
+/* 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;
+}