+ return rc;
+}
+
+/**
+ * Verify striping.
+ *
+ * Check the validity of all fields including the magic, stripe size,
+ * stripe count, stripe offset and that the pool is present. Also check
+ * that each target index points to an existing target. The additional
+ * \a is_from_disk turns additional checks. In some cases zero fields
+ * are allowed (like pattern=0).
+ *
+ * \param[in] d LOD device
+ * \param[in] buf buffer with LOV EA to verify
+ * \param[in] is_from_disk 0 - from user, allow some fields to be 0
+ * 1 - from disk, do not allow
+ *
+ * \retval 0 if the striping is valid
+ * \retval -EINVAL if striping is invalid
+ */
+int lod_verify_striping(struct lod_device *d, const struct lu_buf *buf,
+ bool is_from_disk)
+{
+ struct lov_user_md_v1 *lum;
+ struct lov_user_md_v3 *lum3;
+ struct pool_desc *pool = NULL;
+ __u32 magic;
+ __u32 stripe_size;
+ __u16 stripe_count;
+ __u16 stripe_offset;
+ size_t lum_size;
+ int rc = 0;
+ ENTRY;
+
+ lum = buf->lb_buf;
+
+ LASSERT(sizeof(*lum) < sizeof(*lum3));
+
+ if (buf->lb_len < sizeof(*lum)) {
+ CDEBUG(D_IOCTL, "buf len %zu too small for lov_user_md\n",
+ buf->lb_len);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ magic = le32_to_cpu(lum->lmm_magic);
+ if (magic != LOV_USER_MAGIC_V1 &&
+ magic != LOV_USER_MAGIC_V3 &&
+ magic != LOV_MAGIC_V1_DEF &&
+ magic != LOV_MAGIC_V3_DEF) {
+ CDEBUG(D_IOCTL, "bad userland LOV MAGIC: %#x\n", magic);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ /* the user uses "0" for default stripe pattern normally. */
+ if (!is_from_disk && lum->lmm_pattern == 0)
+ lum->lmm_pattern = cpu_to_le32(LOV_PATTERN_RAID0);
+
+ if (le32_to_cpu(lum->lmm_pattern) != LOV_PATTERN_RAID0) {
+ CDEBUG(D_IOCTL, "bad userland stripe pattern: %#x\n",
+ le32_to_cpu(lum->lmm_pattern));
+ GOTO(out, rc = -EINVAL);
+ }
+
+ /* 64kB is the largest common page size we see (ia64), and matches the
+ * check in lfs */
+ stripe_size = le32_to_cpu(lum->lmm_stripe_size);
+ if (stripe_size & (LOV_MIN_STRIPE_SIZE - 1)) {
+ CDEBUG(D_IOCTL, "stripe size %u not a multiple of %u\n",
+ stripe_size, LOV_MIN_STRIPE_SIZE);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ stripe_offset = le16_to_cpu(lum->lmm_stripe_offset);
+ if (stripe_offset != LOV_OFFSET_DEFAULT) {
+ /* if offset is not within valid range [0, osts_size) */
+ if (stripe_offset >= d->lod_osts_size) {
+ CDEBUG(D_IOCTL, "stripe offset %u >= bitmap size %u\n",
+ stripe_offset, d->lod_osts_size);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ /* if lmm_stripe_offset is *not* in bitmap */
+ if (!cfs_bitmap_check(d->lod_ost_bitmap, stripe_offset)) {
+ CDEBUG(D_IOCTL, "stripe offset %u not in bitmap\n",
+ stripe_offset);
+ GOTO(out, rc = -EINVAL);
+ }
+ }
+
+ if (magic == LOV_USER_MAGIC_V1 || magic == LOV_MAGIC_V1_DEF)
+ lum_size = offsetof(struct lov_user_md_v1,
+ lmm_objects[0]);
+ else if (magic == LOV_USER_MAGIC_V3 || magic == LOV_MAGIC_V3_DEF)
+ lum_size = offsetof(struct lov_user_md_v3,
+ lmm_objects[0]);
+ else
+ GOTO(out, rc = -EINVAL);
+
+ stripe_count = le16_to_cpu(lum->lmm_stripe_count);
+ if (buf->lb_len != lum_size) {
+ CDEBUG(D_IOCTL, "invalid buf len %zu for lov_user_md with "
+ "magic %#x and stripe_count %u\n",
+ buf->lb_len, magic, stripe_count);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ if (!(magic == LOV_USER_MAGIC_V3 || magic == LOV_MAGIC_V3_DEF))
+ goto out;
+
+ lum3 = buf->lb_buf;
+ if (buf->lb_len < sizeof(*lum3)) {
+ CDEBUG(D_IOCTL, "buf len %zu too small for lov_user_md_v3\n",
+ buf->lb_len);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ /* In the function below, .hs_keycmp resolves to
+ * pool_hashkey_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ pool = lod_find_pool(d, lum3->lmm_pool_name);
+ if (pool == NULL)
+ goto out;
+
+ if (stripe_offset != LOV_OFFSET_DEFAULT) {
+ rc = lod_check_index_in_pool(stripe_offset, pool);
+ if (rc < 0)
+ GOTO(out, rc = -EINVAL);
+ }
+
+ if (is_from_disk && stripe_count > pool_tgt_count(pool)) {
+ CDEBUG(D_IOCTL,
+ "stripe count %u > # OSTs %u in the pool\n",
+ stripe_count, pool_tgt_count(pool));
+ GOTO(out, rc = -EINVAL);
+ }
+
+out:
+ if (pool != NULL)
+ lod_pool_putref(pool);
+