Whamcloud - gitweb
LU-15171 osd-ldiskfs: xattr_sem locking is missing for dquot_transfer
[fs/lustre-release.git] / lustre / osd-ldiskfs / osd_handler.c
index 2c6741a..34f8465 100644 (file)
@@ -75,6 +75,9 @@
 
 #include <lustre_linkea.h>
 
+/* encoding routines */
+#include <lustre_crypto.h>
+
 /* Maximum EA size is limited by LNET_MTU for remote objects */
 #define OSD_MAX_EA_SIZE 1048364
 
@@ -1155,6 +1158,7 @@ static int osd_fid_lookup(const struct lu_env *env, struct osd_object *obj,
        }
 
        id = &info->oti_id;
+       memset(id, 0, sizeof(struct osd_inode_id));
        if (!list_empty(&scrub->os_inconsistent_items)) {
                /* Search order: 2. OI scrub pending list. */
                result = osd_oii_lookup(dev, fid, id);
@@ -1257,7 +1261,7 @@ trigger:
                goto found;
        }
 
-       if (dev->od_auto_scrub_interval == AS_NEVER) {
+       if (dev->od_scrub.os_scrub.os_auto_scrub_interval == AS_NEVER) {
                if (!remote)
                        GOTO(out, result = -EREMCHG);
 
@@ -2572,18 +2576,42 @@ const int osd_dto_credits_noquota[DTO_NR] = {
        [DTO_ATTR_SET_CHOWN] = 0
 };
 
+/* reserve or free quota for some operation */
+static int osd_reserve_or_free_quota(const struct lu_env *env,
+                                    struct dt_device *dev,
+                                    enum quota_type type, __u64 uid,
+                                    __u64 gid, __s64 count, bool is_md)
+{
+       int rc;
+       struct osd_device       *osd = osd_dt_dev(dev);
+       struct osd_thread_info  *info = osd_oti_get(env);
+       struct lquota_id_info   *qi = &info->oti_qi;
+       struct qsd_instance     *qsd = NULL;
+
+       ENTRY;
+
+       if (is_md)
+               qsd = osd->od_quota_slave_md;
+       else
+               qsd = osd->od_quota_slave_dt;
+
+       rc = quota_reserve_or_free(env, qsd, qi, type, uid, gid, count, is_md);
+       RETURN(rc);
+}
+
 static const struct dt_device_operations osd_dt_ops = {
-       .dt_root_get       = osd_root_get,
-       .dt_statfs         = osd_statfs,
-       .dt_trans_create   = osd_trans_create,
-       .dt_trans_start    = osd_trans_start,
-       .dt_trans_stop     = osd_trans_stop,
-       .dt_trans_cb_add   = osd_trans_cb_add,
-       .dt_conf_get       = osd_conf_get,
-       .dt_mnt_sb_get     = osd_mnt_sb_get,
-       .dt_sync           = osd_sync,
-       .dt_ro             = osd_ro,
-       .dt_commit_async   = osd_commit_async,
+       .dt_root_get              = osd_root_get,
+       .dt_statfs                = osd_statfs,
+       .dt_trans_create          = osd_trans_create,
+       .dt_trans_start           = osd_trans_start,
+       .dt_trans_stop            = osd_trans_stop,
+       .dt_trans_cb_add          = osd_trans_cb_add,
+       .dt_conf_get              = osd_conf_get,
+       .dt_mnt_sb_get            = osd_mnt_sb_get,
+       .dt_sync                  = osd_sync,
+       .dt_ro                    = osd_ro,
+       .dt_commit_async          = osd_commit_async,
+       .dt_reserve_or_free_quota = osd_reserve_or_free_quota,
 };
 
 static void osd_read_lock(const struct lu_env *env, struct dt_object *dt,
@@ -2781,7 +2809,7 @@ static int osd_declare_attr_qid(const struct lu_env *env,
                                struct osd_object *obj,
                                struct osd_thandle *oh, long long bspace,
                                qid_t old_id, qid_t new_id, bool enforce,
-                               unsigned int type, bool ignore_edquot)
+                               unsigned int type)
 {
        int rc;
        struct osd_thread_info *info = osd_oti_get(env);
@@ -2796,7 +2824,7 @@ static int osd_declare_attr_qid(const struct lu_env *env,
        qi->lqi_space      = 1;
        /* Reserve credits for the new id */
        rc = osd_declare_qid(env, oh, qi, NULL, enforce, NULL);
-       if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+       if (rc == -EDQUOT || rc == -EINPROGRESS)
                rc = 0;
        if (rc)
                RETURN(rc);
@@ -2805,7 +2833,7 @@ static int osd_declare_attr_qid(const struct lu_env *env,
        qi->lqi_id.qid_uid = old_id;
        qi->lqi_space = -1;
        rc = osd_declare_qid(env, oh, qi, obj, enforce, NULL);
-       if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+       if (rc == -EDQUOT || rc == -EINPROGRESS)
                rc = 0;
        if (rc)
                RETURN(rc);
@@ -2821,7 +2849,7 @@ static int osd_declare_attr_qid(const struct lu_env *env,
         * to save credit reservation.
         */
        rc = osd_declare_qid(env, oh, qi, obj, enforce, NULL);
-       if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+       if (rc == -EDQUOT || rc == -EINPROGRESS)
                rc = 0;
        if (rc)
                RETURN(rc);
@@ -2830,7 +2858,7 @@ static int osd_declare_attr_qid(const struct lu_env *env,
        qi->lqi_id.qid_uid = old_id;
        qi->lqi_space      = -bspace;
        rc = osd_declare_qid(env, oh, qi, obj, enforce, NULL);
-       if (ignore_edquot && (rc == -EDQUOT || rc == -EINPROGRESS))
+       if (rc == -EDQUOT || rc == -EINPROGRESS)
                rc = 0;
 
        RETURN(rc);
@@ -2881,20 +2909,11 @@ static int osd_declare_attr_set(const struct lu_env *env,
         * space adjustment once the operation is completed.
         */
        if (attr->la_valid & LA_UID || attr->la_valid & LA_GID) {
-               bool ignore_edquot = !(attr->la_flags & LUSTRE_SET_SYNC_FL);
-
-               if (!ignore_edquot)
-                       CDEBUG(D_QUOTA,
-                              "%s: enforce quota on UID %u, GID %u (quota space is %lld)\n",
-                              osd_ino2name(obj->oo_inode), attr->la_uid,
-                              attr->la_gid, bspace);
-
                /* USERQUOTA */
                uid = i_uid_read(obj->oo_inode);
                enforce = (attr->la_valid & LA_UID) && (attr->la_uid != uid);
                rc = osd_declare_attr_qid(env, obj, oh, bspace, uid,
-                                         attr->la_uid, enforce, USRQUOTA,
-                                         true);
+                                         attr->la_uid, enforce, USRQUOTA);
                if (rc)
                        RETURN(rc);
 
@@ -2903,8 +2922,7 @@ static int osd_declare_attr_set(const struct lu_env *env,
                       attr->la_uid, gid, attr->la_gid);
                enforce = (attr->la_valid & LA_GID) && (attr->la_gid != gid);
                rc = osd_declare_attr_qid(env, obj, oh, bspace, gid,
-                                         attr->la_gid, enforce, GRPQUOTA,
-                                         ignore_edquot);
+                                         attr->la_gid, enforce, GRPQUOTA);
                if (rc)
                        RETURN(rc);
 
@@ -2917,7 +2935,7 @@ static int osd_declare_attr_set(const struct lu_env *env,
                                        (attr->la_projid != projid);
                rc = osd_declare_attr_qid(env, obj, oh, bspace,
                                          (qid_t)projid, (qid_t)attr->la_projid,
-                                         enforce, PRJQUOTA, true);
+                                         enforce, PRJQUOTA);
                if (rc)
                        RETURN(rc);
        }
@@ -3044,7 +3062,9 @@ static int osd_transfer_project(struct inode *inode, __u32 projid,
        dquot_initialize(inode);
        transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
        if (transfer_to[PRJQUOTA]) {
+               lock_dquot_transfer(inode);
                err = __dquot_transfer(inode, transfer_to);
+               unlock_dquot_transfer(inode);
                dqput(transfer_to[PRJQUOTA]);
                if (err)
                        return err;
@@ -3077,7 +3097,9 @@ static int osd_quota_transfer(struct inode *inode, const struct lu_attr *attr,
                iattr.ia_uid = make_kuid(&init_user_ns, attr->la_uid);
                iattr.ia_gid = make_kgid(&init_user_ns, attr->la_gid);
 
+               lock_dquot_transfer(inode);
                rc = dquot_transfer(inode, &iattr);
+               unlock_dquot_transfer(inode);
                if (rc) {
                        CERROR("%s: quota transfer failed. Is quota enforcement enabled on the ldiskfs filesystem? rc = %d\n",
                               osd_ino2name(inode), rc);
@@ -3088,6 +3110,8 @@ static int osd_quota_transfer(struct inode *inode, const struct lu_attr *attr,
        /* Handle project id transfer here properly */
        if (attr->la_valid & LA_PROJID &&
            attr->la_projid != i_projid_read(inode)) {
+               if (!projid_valid(make_kprojid(&init_user_ns, attr->la_projid)))
+                       return -EINVAL;
 #ifdef HAVE_PROJECT_QUOTA
                rc = osd_transfer_project(inode, attr->la_projid, handle);
 #else
@@ -5342,6 +5366,50 @@ static void osd_take_care_of_agent(const struct lu_env *env,
 }
 
 /**
+ * Utility function to get real name from object name
+ *
+ * \param[in] obj      pointer to the object to be handled
+ * \param[in] name     object name
+ * \param[in] len      object name len
+ * \param[out]ln       pointer to the struct lu_name to hold the real name
+ *
+ * If file is not encrypted, real name is just the object name.
+ * If file is encrypted, object name needs to be decoded. In
+ * this case a new buffer is allocated, and ln->ln_name needs to be freed by
+ * the caller.
+ *
+ * \retval   0, on success
+ * \retval -ve, on error
+ */
+static int obj_name2lu_name(struct osd_object *obj, const char *name,
+                           int len, struct lu_name *ln)
+{
+       struct lu_fid namefid;
+
+       fid_zero(&namefid);
+
+       if (name[0] == '[')
+               sscanf(name + 1, SFID, RFID(&namefid));
+
+       if (fid_is_sane(&namefid) || name_is_dot_or_dotdot(name, len) ||
+           !(obj->oo_lma_flags & LUSTRE_ENCRYPT_FL)) {
+               ln->ln_name = name;
+               ln->ln_namelen = len;
+       } else {
+               char *buf = kmalloc(len, GFP_NOFS);
+
+               if (!buf)
+                       return -ENOMEM;
+
+               len = critical_decode(name, len, buf);
+               ln->ln_name = buf;
+               ln->ln_namelen = len;
+       }
+
+       return 0;
+}
+
+/**
  * Index delete function for interoperability mode (b11826).
  * It will remove the directory entry added by osd_index_ea_insert().
  * This entry is needed to maintain name->fid mapping.
@@ -5362,6 +5430,7 @@ static int osd_index_ea_delete(const struct lu_env *env, struct dt_object *dt,
        struct buffer_head *bh;
        struct htree_lock *hlock = NULL;
        struct osd_device *osd = osd_dev(dt->do_lu.lo_dev);
+       struct lu_name ln;
        int rc;
 
        ENTRY;
@@ -5373,6 +5442,10 @@ static int osd_index_ea_delete(const struct lu_env *env, struct dt_object *dt,
        LASSERT(!dt_object_remote(dt));
        LASSERT(handle != NULL);
 
+       rc = obj_name2lu_name(obj, (char *)key, strlen((char *)key), &ln);
+       if (rc)
+               RETURN(rc);
+
        osd_trans_exec_op(env, handle, OSD_OT_DELETE);
 
        oh = container_of(handle, struct osd_thandle, ot_super);
@@ -5380,8 +5453,7 @@ static int osd_index_ea_delete(const struct lu_env *env, struct dt_object *dt,
        LASSERT(oh->ot_handle->h_transaction != NULL);
 
        dquot_initialize(dir);
-       dentry = osd_child_dentry_get(env, obj,
-                                     (char *)key, strlen((char *)key));
+       dentry = osd_child_dentry_get(env, obj, ln.ln_name, ln.ln_namelen);
 
        if (obj->oo_hl_head != NULL) {
                hlock = osd_oti_get(env)->oti_hlock;
@@ -5435,6 +5507,8 @@ static int osd_index_ea_delete(const struct lu_env *env, struct dt_object *dt,
 out:
        LASSERT(osd_invariant(obj));
        osd_trans_exec_check(env, handle, OSD_OT_DELETE);
+       if (ln.ln_name != (char *)key)
+               kfree(ln.ln_name);
        RETURN(rc);
 }
 
@@ -5609,6 +5683,7 @@ static int __osd_ea_add_rec(struct osd_thread_info *info,
        struct ldiskfs_dentry_param *ldp;
        struct dentry *child;
        struct osd_thandle *oth;
+       struct lu_name ln;
        int rc;
 
        oth = container_of(th, struct osd_thandle, ot_super);
@@ -5616,12 +5691,17 @@ static int __osd_ea_add_rec(struct osd_thread_info *info,
        LASSERT(oth->ot_handle->h_transaction != NULL);
        LASSERT(pobj->oo_inode);
 
+       rc = obj_name2lu_name(pobj, name, strlen(name), &ln);
+       if (rc)
+               RETURN(rc);
+
        ldp = (struct ldiskfs_dentry_param *)info->oti_ldp;
        if (unlikely(osd_object_is_root(pobj)))
                ldp->edp_magic = 0;
        else
                osd_get_ldiskfs_dirent_param(ldp, fid);
-       child = osd_child_dentry_get(info->oti_env, pobj, name, strlen(name));
+       child = osd_child_dentry_get(info->oti_env, pobj,
+                                    ln.ln_name, ln.ln_namelen);
        child->d_fsdata = (void *)ldp;
        dquot_initialize(pobj->oo_inode);
        rc = osd_ldiskfs_add_entry(info, osd_obj2dev(pobj), oth->ot_handle,
@@ -5650,6 +5730,8 @@ static int __osd_ea_add_rec(struct osd_thread_info *info,
                }
        }
 
+       if (ln.ln_name != name)
+               kfree(ln.ln_name);
        RETURN(rc);
 }
 
@@ -5777,8 +5859,10 @@ static int osd_ea_add_rec(const struct lu_env *env, struct osd_object *pobj,
 }
 
 static int
-osd_consistency_check(struct osd_thread_info *oti, struct osd_device *dev,
-                     const struct lu_fid *fid, struct osd_inode_id *id)
+osd_ldiskfs_consistency_check(struct osd_thread_info *oti,
+                             struct osd_device *dev,
+                             const struct lu_fid *fid,
+                             struct osd_inode_id *id)
 {
        struct lustre_scrub *scrub = &dev->od_scrub.os_scrub;
        struct inode *inode = NULL;
@@ -5787,18 +5871,8 @@ osd_consistency_check(struct osd_thread_info *oti, struct osd_device *dev,
        int rc;
 
        ENTRY;
-
-       if (!fid_is_norm(fid) && !fid_is_igif(fid))
-               RETURN(0);
-
-       if (scrub->os_running && scrub->os_pos_current > id->oii_ino)
+       if (!scrub_needs_check(scrub, fid, id->oii_ino))
                RETURN(0);
-
-       if (dev->od_auto_scrub_interval == AS_NEVER ||
-           ktime_get_real_seconds() <
-           scrub->os_file.sf_time_last_complete + dev->od_auto_scrub_interval)
-               RETURN(0);
-
 again:
        rc = osd_oi_lookup(oti, dev, fid, &oti->oti_id, 0);
        if (rc == -ENOENT) {
@@ -5861,7 +5935,8 @@ trigger:
                GOTO(out, rc);
        }
 
-       if (dev->od_auto_scrub_interval != AS_NEVER && ++once == 1) {
+       if (dev->od_scrub.os_scrub.os_auto_scrub_interval != AS_NEVER &&
+           ++once == 1) {
                rc = osd_scrub_start(oti->oti_env, dev, SS_AUTO_PARTIAL |
                                     SS_CLEAR_DRYRUN | SS_CLEAR_FAILOUT);
                CDEBUG_LIMIT(D_LFSCK | D_CONSOLE | D_WARNING,
@@ -6052,6 +6127,7 @@ static int osd_ea_lookup_rec(const struct lu_env *env, struct osd_object *obj,
        struct buffer_head *bh;
        struct lu_fid *fid = (struct lu_fid *)rec;
        struct htree_lock *hlock = NULL;
+       struct lu_name ln;
        int ino;
        int rc;
 
@@ -6060,8 +6136,11 @@ static int osd_ea_lookup_rec(const struct lu_env *env, struct osd_object *obj,
        LASSERT(dir->i_op != NULL);
        LASSERT(dir->i_op->lookup != NULL);
 
-       dentry = osd_child_dentry_get(env, obj,
-                                     (char *)key, strlen((char *)key));
+       rc = obj_name2lu_name(obj, (char *)key, strlen((char *)key), &ln);
+       if (rc)
+               RETURN(rc);
+
+       dentry = osd_child_dentry_get(env, obj, ln.ln_name, ln.ln_namelen);
 
        if (obj->oo_hl_head != NULL) {
                hlock = osd_oti_get(env)->oti_hlock;
@@ -6111,7 +6190,7 @@ static int osd_ea_lookup_rec(const struct lu_env *env, struct osd_object *obj,
                if (rc != 0 || osd_remote_fid(env, dev, fid))
                        GOTO(out, rc);
 
-               rc = osd_consistency_check(oti, dev, fid, id);
+               rc = osd_ldiskfs_consistency_check(oti, dev, fid, id);
                if (rc != -ENOENT) {
                        /* Other error should not affect lookup result. */
                        rc = 0;
@@ -6137,7 +6216,9 @@ out:
                ldiskfs_htree_unlock(hlock);
        else
                up_read(&obj->oo_ext_idx_sem);
-       return rc;
+       if (ln.ln_name != (char *)key)
+               kfree(ln.ln_name);
+       RETURN(rc);
 }
 
 static int osd_index_declare_ea_insert(const struct lu_env *env,
@@ -6211,7 +6292,7 @@ static int osd_index_declare_ea_insert(const struct lu_env *env,
                    i_projid_read(inode) != 0)
                        rc = osd_declare_attr_qid(env, osd_dt_obj(dt), oh,
                                                  0, i_projid_read(inode),
-                                                 0, false, PRJQUOTA, true);
+                                                 0, false, PRJQUOTA);
 #endif
        }
 
@@ -6602,36 +6683,23 @@ static const struct dt_index_operations osd_index_iam_ops = {
        }
 };
 
-
-/**
- * Creates or initializes iterator context.
- *
- * \retval struct osd_it_ea, iterator structure on success
- *
- */
-static struct dt_it *osd_it_ea_init(const struct lu_env *env,
-                                   struct dt_object *dt,
-                                   __u32 attr)
+struct osd_it_ea *osd_it_dir_init(const struct lu_env *env,
+                                 struct inode *inode, __u32 attr)
 {
-       struct osd_object *obj = osd_dt_obj(dt);
        struct osd_thread_info *info = osd_oti_get(env);
        struct osd_it_ea *oie;
        struct file *file;
-       struct lu_object *lo = &dt->do_lu;
        struct dentry *obj_dentry;
 
        ENTRY;
 
-       if (!dt_object_exists(dt) || obj->oo_destroyed)
-               RETURN(ERR_PTR(-ENOENT));
-
        OBD_SLAB_ALLOC_PTR_GFP(oie, osd_itea_cachep, GFP_NOFS);
        if (oie == NULL)
                RETURN(ERR_PTR(-ENOMEM));
        obj_dentry = &oie->oie_dentry;
 
-       obj_dentry->d_inode = obj->oo_inode;
-       obj_dentry->d_sb = osd_sb(osd_obj2dev(obj));
+       obj_dentry->d_inode = inode;
+       obj_dentry->d_sb = inode->i_sb;
        obj_dentry->d_name.hash = 0;
 
        oie->oie_rd_dirent       = 0;
@@ -6645,7 +6713,7 @@ static struct dt_it *osd_it_ea_init(const struct lu_env *env,
                if (oie->oie_buf == NULL)
                        RETURN(ERR_PTR(-ENOMEM));
        }
-       oie->oie_obj = obj;
+       oie->oie_obj = NULL;
 
        file = &oie->oie_file;
 
@@ -6655,14 +6723,56 @@ static struct dt_it *osd_it_ea_init(const struct lu_env *env,
        else
                file->f_mode    = FMODE_32BITHASH;
        file->f_path.dentry     = obj_dentry;
-       file->f_mapping         = obj->oo_inode->i_mapping;
-       file->f_op              = obj->oo_inode->i_fop;
-       file->f_inode = obj->oo_inode;
+       file->f_mapping         = inode->i_mapping;
+       file->f_op              = inode->i_fop;
+       file->f_inode = inode;
+
+       RETURN(oie);
+}
 
+/**
+ * Creates or initializes iterator context.
+ *
+ * \retval struct osd_it_ea, iterator structure on success
+ *
+ */
+static struct dt_it *osd_it_ea_init(const struct lu_env *env,
+                                   struct dt_object *dt,
+                                   __u32 attr)
+{
+       struct osd_object *obj = osd_dt_obj(dt);
+       struct lu_object *lo = &dt->do_lu;
+       struct osd_it_ea *oie;
+
+       ENTRY;
+
+       if (!dt_object_exists(dt) || obj->oo_destroyed)
+               RETURN(ERR_PTR(-ENOENT));
+
+       oie = osd_it_dir_init(env, obj->oo_inode, attr);
+       if (IS_ERR(oie))
+               RETURN((struct dt_it *)oie);
+
+       oie->oie_obj = obj;
        lu_object_get(lo);
        RETURN((struct dt_it *)oie);
 }
 
+void osd_it_dir_fini(const struct lu_env *env, struct osd_it_ea *oie,
+                    struct inode *inode)
+{
+       struct osd_thread_info *info = osd_oti_get(env);
+
+       ENTRY;
+       oie->oie_file.f_op->release(inode, &oie->oie_file);
+       if (unlikely(oie->oie_buf != info->oti_it_ea_buf))
+               OBD_FREE(oie->oie_buf, OSD_IT_EA_BUFSIZE);
+       else
+               info->oti_it_ea_buf_used = 0;
+       OBD_SLAB_FREE_PTR(oie, osd_itea_cachep);
+       EXIT;
+}
+
 /**
  * Destroy or finishes iterator context.
  *
@@ -6670,19 +6780,13 @@ static struct dt_it *osd_it_ea_init(const struct lu_env *env,
  */
 static void osd_it_ea_fini(const struct lu_env *env, struct dt_it *di)
 {
-       struct osd_thread_info *info = osd_oti_get(env);
        struct osd_it_ea *oie = (struct osd_it_ea *)di;
        struct osd_object *obj = oie->oie_obj;
        struct inode *inode = obj->oo_inode;
 
        ENTRY;
-       oie->oie_file.f_op->release(inode, &oie->oie_file);
+       osd_it_dir_fini(env, (struct osd_it_ea *)di, inode);
        osd_object_put(env, obj);
-       if (unlikely(oie->oie_buf != info->oti_it_ea_buf))
-               OBD_FREE(oie->oie_buf, OSD_IT_EA_BUFSIZE);
-       else
-               info->oti_it_ea_buf_used = 0;
-       OBD_SLAB_FREE_PTR(oie, osd_itea_cachep);
        EXIT;
 }
 
@@ -6746,6 +6850,7 @@ static int osd_ldiskfs_filldir(void *buf,
        struct osd_it_ea_dirent *ent = it->oie_dirent;
        struct lu_fid *fid = &ent->oied_fid;
        struct osd_fid_pack *rec;
+       struct lu_fid namefid;
 
        ENTRY;
 
@@ -6759,9 +6864,12 @@ static int osd_ldiskfs_filldir(void *buf,
            OSD_IT_EA_BUFSIZE)
                RETURN(1);
 
+       fid_zero(&namefid);
+
        /* "." is just the object itself. */
        if (namelen == 1 && name[0] == '.') {
-               *fid = obj->oo_dt.do_lu.lo_header->loh_fid;
+               if (obj != NULL)
+                       *fid = obj->oo_dt.do_lu.lo_header->loh_fid;
        } else if (d_type & LDISKFS_DIRENT_LUFID) {
                rec = (struct osd_fid_pack *)(name + namelen + 1);
                if (osd_fid_unpack(fid, rec) != 0)
@@ -6772,18 +6880,34 @@ static int osd_ldiskfs_filldir(void *buf,
        d_type &= ~LDISKFS_DIRENT_LUFID;
 
        /* NOT export local root. */
-       if (unlikely(osd_sb(osd_obj2dev(obj))->s_root->d_inode->i_ino == ino)) {
+       if (obj != NULL &&
+           unlikely(osd_sb(osd_obj2dev(obj))->s_root->d_inode->i_ino == ino)) {
                ino = obj->oo_inode->i_ino;
                *fid = obj->oo_dt.do_lu.lo_header->loh_fid;
        }
 
+       if (name[0] == '[')
+               sscanf(name + 1, SFID, RFID(&namefid));
+       if (fid_is_sane(&namefid) || name_is_dot_or_dotdot(name, namelen) ||
+           !obj || !(obj->oo_lma_flags & LUSTRE_ENCRYPT_FL)) {
+               memcpy(ent->oied_name, name, namelen);
+       } else {
+               int presented_len = critical_chars(name, namelen);
+
+               if (presented_len == namelen)
+                       memcpy(ent->oied_name, name, namelen);
+               else
+                       namelen = critical_encode(name, namelen,
+                                                 ent->oied_name);
+
+               ent->oied_name[namelen] = '\0';
+       }
+
        ent->oied_ino     = ino;
        ent->oied_off     = offset;
        ent->oied_namelen = namelen;
        ent->oied_type    = d_type;
 
-       memcpy(ent->oied_name, name, namelen);
-
        it->oie_rd_dirent++;
        it->oie_dirent = (void *)ent + cfs_size_round(sizeof(*ent) + namelen);
        RETURN(0);
@@ -6799,12 +6923,10 @@ static int osd_ldiskfs_filldir(void *buf,
  * \retval -ve on error
  * \retval +1 reach the end of entry
  */
-static int osd_ldiskfs_it_fill(const struct lu_env *env,
-                              const struct dt_it *di)
+int osd_ldiskfs_it_fill(const struct lu_env *env, const struct dt_it *di)
 {
        struct osd_it_ea *it = (struct osd_it_ea *)di;
        struct osd_object *obj = it->oie_obj;
-       struct inode *inode = obj->oo_inode;
        struct htree_lock *hlock = NULL;
        struct file *filp = &it->oie_file;
        int rc = 0;
@@ -6817,12 +6939,15 @@ static int osd_ldiskfs_it_fill(const struct lu_env *env,
        it->oie_dirent = it->oie_buf;
        it->oie_rd_dirent = 0;
 
-       if (obj->oo_hl_head != NULL) {
-               hlock = osd_oti_get(env)->oti_hlock;
-               ldiskfs_htree_lock(hlock, obj->oo_hl_head,
-                                  inode, LDISKFS_HLOCK_READDIR);
-       } else {
-               down_read(&obj->oo_ext_idx_sem);
+       if (obj) {
+               if (obj->oo_hl_head != NULL) {
+                       hlock = osd_oti_get(env)->oti_hlock;
+                       ldiskfs_htree_lock(hlock, obj->oo_hl_head,
+                                          obj->oo_inode,
+                                          LDISKFS_HLOCK_READDIR);
+               } else {
+                       down_read(&obj->oo_ext_idx_sem);
+               }
        }
 
        filp->f_cred = current_cred();
@@ -6849,10 +6974,12 @@ static int osd_ldiskfs_it_fill(const struct lu_env *env,
                it->oie_it_dirent = 1;
        }
 unlock:
-       if (hlock != NULL)
-               ldiskfs_htree_unlock(hlock);
-       else
-               up_read(&obj->oo_ext_idx_sem);
+       if (obj) {
+               if (hlock != NULL)
+                       ldiskfs_htree_unlock(hlock);
+               else
+                       up_read(&obj->oo_ext_idx_sem);
+       }
 
        RETURN(rc);
 }
@@ -7032,6 +7159,7 @@ osd_dirent_check_repair(const struct lu_env *env, struct osd_object *obj,
        int rc;
        bool dotdot = false;
        bool dirty = false;
+       struct lu_name ln;
 
        ENTRY;
 
@@ -7066,8 +7194,11 @@ osd_dirent_check_repair(const struct lu_env *env, struct osd_object *obj,
                RETURN(rc);
        }
 
-       dentry = osd_child_dentry_by_inode(env, dir, ent->oied_name,
-                                          ent->oied_namelen);
+       rc = obj_name2lu_name(obj, ent->oied_name, ent->oied_namelen, &ln);
+       if (rc)
+               RETURN(rc);
+
+       dentry = osd_child_dentry_by_inode(env, dir, ln.ln_name, ln.ln_namelen);
        rc = osd_get_lma(info, inode, dentry, &info->oti_ost_attrs);
        if (rc == -ENODATA || !fid_is_sane(&lma->lma_self_fid))
                lma = NULL;
@@ -7340,6 +7471,8 @@ out_inode:
        iput(inode);
        if (rc >= 0 && !dirty)
                dev->od_dirent_journal = 0;
+       if (ln.ln_name != ent->oied_name)
+               kfree(ln.ln_name);
 
        return rc;
 }
@@ -7728,6 +7861,20 @@ static void osd_umount(const struct lu_env *env, struct osd_device *o)
        EXIT;
 }
 
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 2, 53, 0)
+# ifndef LDISKFS_HAS_INCOMPAT_FEATURE
+/* Newer kernels provide the ldiskfs_set_feature_largedir() wrapper already,
+ * which calls ldiskfs_update_dynamic_rev() to update ancient filesystems.
+ * All ldiskfs filesystems are already v2, so it is a no-op and unnecessary.
+ * This avoids maintaining patches to export this otherwise-useless function.
+ */
+void ldiskfs_update_dynamic_rev(struct super_block *sb)
+{
+       /* do nothing */
+}
+# endif
+#endif
+
 static int osd_mount(const struct lu_env *env,
                     struct osd_device *o, struct lustre_cfg *cfg)
 {
@@ -7872,6 +8019,7 @@ static int osd_mount(const struct lu_env *env,
                GOTO(out_mnt, rc = -EINVAL);
        }
 
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 2, 53, 0)
 #ifdef LDISKFS_MOUNT_DIRDATA
        if (ldiskfs_has_feature_dirdata(o->od_mnt->mnt_sb))
                LDISKFS_SB(osd_sb(o))->s_mount_opt |= LDISKFS_MOUNT_DIRDATA;
@@ -7881,6 +8029,15 @@ static int osd_mount(const struct lu_env *env,
                      "downgrade to Lustre-1.x again, you can enable it via "
                      "'tune2fs -O dirdata device'\n", name, dev);
 #endif
+       /* enable large_dir on MDTs to avoid REMOTE_PARENT_DIR overflow,
+        * and on very large OSTs to avoid object directory overflow */
+       if (unlikely(!ldiskfs_has_feature_largedir(o->od_mnt->mnt_sb) &&
+                    !strstr(name, "MGS"))) {
+               ldiskfs_set_feature_largedir(o->od_mnt->mnt_sb);
+               LCONSOLE_INFO("%s: enabled 'large_dir' feature on device %s\n",
+                             name, dev);
+       }
+#endif
        inode = osd_sb(o)->s_root->d_inode;
        lu_local_obj_fid(fid, OSD_FS_ROOT_OID);
        rc = osd_ea_fid_set(info, inode, fid, LMAC_NOT_IN_OI, 0);
@@ -7890,7 +8047,7 @@ static int osd_mount(const struct lu_env *env,
        }
 
        if (lmd_flags & LMD_FLG_NOSCRUB)
-               o->od_auto_scrub_interval = AS_NEVER;
+               o->od_scrub.os_scrub.os_auto_scrub_interval = AS_NEVER;
 
        if (blk_queue_nonrot(bdev_get_queue(osd_sb(o)->s_bdev))) {
                /* do not use pagecache with flash-backed storage */
@@ -7967,7 +8124,7 @@ static int osd_device_init0(const struct lu_env *env,
        o->od_readcache_max_filesize = OSD_MAX_CACHE_SIZE;
        o->od_readcache_max_iosize = OSD_READCACHE_MAX_IO_MB << 20;
        o->od_writethrough_max_iosize = OSD_WRITECACHE_MAX_IO_MB << 20;
-       o->od_auto_scrub_interval = AS_DEFAULT;
+       o->od_scrub.os_scrub.os_auto_scrub_interval = AS_DEFAULT;
        /* default fallocate to unwritten extents: LU-14326/LU-14333 */
        o->od_fallocate_zero_blocks = 0;