- /*
- * XXX huge struct allocated on stack.
- */
- struct lov_user_md lum;
- struct lov_mds_md *lmmk = NULL;
- int rc, lmm_size;
- mm_segment_t seg;
- ENTRY;
-
- if (!lsm)
- RETURN(-ENODATA);
-
- /*
- * "Switch to kernel segment" to allow copying from kernel space by
- * copy_{to,from}_user().
- */
- seg = get_fs();
- set_fs(KERNEL_DS);
- rc = copy_from_user(&lum, lump, sizeof(lum));
- if (rc)
- rc = -EFAULT;
- else if (lum.lmm_magic != LOV_USER_MAGIC)
- rc = -EINVAL;
- else {
- rc = lov_packmd(exp, &lmmk, lsm);
- if (rc < 0)
- RETURN(rc);
- lmm_size = rc;
- rc = 0;
-
- /* FIXME: Bug 1185 - copy fields properly when structs change */
- CLASSERT(sizeof lum == sizeof *lmmk);
- CLASSERT(sizeof lum.lmm_objects[0] ==
- sizeof lmmk->lmm_objects[0]);
-
- /* User wasn't expecting this many OST entries */
- if (lum.lmm_stripe_count == 0) {
- if (copy_to_user(lump, lmmk, sizeof lum))
- rc = -EFAULT;
- } else if (lum.lmm_stripe_count < lmmk->lmm_stripe_count) {
- rc = -EOVERFLOW;
- } else if (copy_to_user(lump, lmmk, sizeof lum))
- rc = -EFAULT;
-
- obd_free_diskmd(exp, &lmmk);
- }
- set_fs(seg);
- RETURN(rc);
+ /* we use lov_user_md_v3 because it is larger than lov_user_md_v1 */
+ struct lov_mds_md *lmmk, *lmm;
+ struct lov_foreign_md *lfm;
+ struct lov_user_md_v1 lum;
+ size_t lmmk_size;
+ ssize_t lmm_size, lum_size = 0;
+ static bool printed;
+ int rc = 0;
+
+ ENTRY;
+
+ if (lsm->lsm_magic != LOV_MAGIC_V1 && lsm->lsm_magic != LOV_MAGIC_V3 &&
+ lsm->lsm_magic != LOV_MAGIC_COMP_V1 &&
+ lsm->lsm_magic != LOV_MAGIC_FOREIGN) {
+ CERROR("bad LSM MAGIC: 0x%08X != 0x%08X nor 0x%08X\n",
+ lsm->lsm_magic, LOV_MAGIC_V1, LOV_MAGIC_V3);
+ GOTO(out, rc = -EIO);
+ }
+
+ if (!printed) {
+ LCONSOLE_WARN("%s: using old ioctl(LL_IOC_LOV_GETSTRIPE) on "
+ DFID", use llapi_layout_get_by_path()\n",
+ current->comm,
+ PFID(&obj->lo_cl.co_lu.lo_header->loh_fid));
+ printed = true;
+ }
+
+ lmmk_size = lov_comp_md_size(lsm);
+
+ OBD_ALLOC_LARGE(lmmk, lmmk_size);
+ if (!lmmk)
+ GOTO(out, rc = -ENOMEM);
+
+ lmm_size = lov_lsm_pack(lsm, lmmk, lmmk_size);
+ if (lmm_size < 0)
+ GOTO(out_free, rc = lmm_size);
+
+ if (cpu_to_le32(LOV_MAGIC) != LOV_MAGIC) {
+ if (lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_V1) ||
+ lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_V3)) {
+ lustre_swab_lov_mds_md(lmmk);
+ lustre_swab_lov_user_md_objects(
+ (struct lov_user_ost_data *)lmmk->lmm_objects,
+ lmmk->lmm_stripe_count);
+ } else if (lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_COMP_V1)) {
+ lustre_swab_lov_comp_md_v1(
+ (struct lov_comp_md_v1 *)lmmk);
+ } else if (lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_FOREIGN)) {
+ lfm = (struct lov_foreign_md *)lmmk;
+ __swab32s(&lfm->lfm_magic);
+ __swab32s(&lfm->lfm_length);
+ __swab32s(&lfm->lfm_type);
+ __swab32s(&lfm->lfm_flags);
+ }
+ }
+
+ /*
+ * Legacy appication passes limited buffer, we need to figure out
+ * the user buffer size by the passed in lmm_stripe_count.
+ */
+ if (lsm->lsm_magic != LOV_MAGIC_FOREIGN)
+ if (copy_from_user(&lum, lump, sizeof(struct lov_user_md_v1)))
+ GOTO(out_free, rc = -EFAULT);
+
+ if (lum.lmm_magic == LOV_USER_MAGIC_V1 ||
+ lum.lmm_magic == LOV_USER_MAGIC_V3)
+ lum_size = lov_user_md_size(lum.lmm_stripe_count,
+ lum.lmm_magic);
+
+ if (lum_size != 0) {
+ struct lov_mds_md *comp_md = lmmk;
+
+ /*
+ * Legacy app (ADIO for instance) treats the layout as V1/V3
+ * blindly, we'd return a reasonable V1/V3 for them.
+ */
+ if (lmmk->lmm_magic == LOV_MAGIC_COMP_V1) {
+ struct lov_comp_md_v1 *comp_v1;
+ struct cl_object *cl_obj;
+ struct cl_attr attr;
+ int i;
+
+ attr.cat_size = 0;
+ cl_obj = cl_object_top(&obj->lo_cl);
+ cl_object_attr_lock(cl_obj);
+ cl_object_attr_get(env, cl_obj, &attr);
+ cl_object_attr_unlock(cl_obj);
+
+ /*
+ * return the last instantiated component if file size
+ * is non-zero, otherwise, return the last component.
+ */
+ comp_v1 = (struct lov_comp_md_v1 *)lmmk;
+ i = attr.cat_size == 0 ? comp_v1->lcm_entry_count : 0;
+ for (; i < comp_v1->lcm_entry_count; i++) {
+ if (!(comp_v1->lcm_entries[i].lcme_flags &
+ LCME_FL_INIT))
+ break;
+ }
+ if (i > 0)
+ i--;
+ comp_md = (struct lov_mds_md *)((char *)comp_v1 +
+ comp_v1->lcm_entries[i].lcme_offset);
+ }
+
+ lmm = comp_md;
+ lmm_size = lum_size;
+ } else {
+ lmm = lmmk;
+ lmm_size = lmmk_size;
+ }
+ /**
+ * User specified limited buffer size, usually the buffer is
+ * from ll_lov_setstripe(), and the buffer can only hold basic
+ * layout template info.
+ */
+ if (size == 0 || size > lmm_size)
+ size = lmm_size;
+ if (copy_to_user(lump, lmm, size))
+ GOTO(out_free, rc = -EFAULT);
+
+out_free:
+ OBD_FREE_LARGE(lmmk, lmmk_size);
+out:
+ RETURN(rc);