/** * Get version of object by fid. * * Return real version or ENOENT_VERSION if object doesn't exist */ static void mdt_obj_version_get(struct mdt_thread_info *info, struct mdt_object *o, __u64 *version) { LASSERT(o); LASSERT(mdt_object_exists(o) >= 0); if (mdt_object_exists(o) > 0) *version = dt_version_get(info->mti_env, mdt_obj2dt(o)); else *version = ENOENT_VERSION; CDEBUG(D_INODE, "FID "DFID" version is "LPX64"\n", PFID(mdt_object_fid(o)), *version); }
int mdt_attr_set(struct mdt_thread_info *info, struct mdt_object *mo, struct md_attr *ma, int flags) { struct mdt_lock_handle *lh; int do_vbr = ma->ma_attr.la_valid & (LA_MODE|LA_UID|LA_GID|LA_FLAGS); __u64 lockpart = MDS_INODELOCK_UPDATE; int rc; ENTRY; /* attr shouldn't be set on remote object */ LASSERT(mdt_object_exists(mo) >= 0); lh = &info->mti_lh[MDT_LH_PARENT]; mdt_lock_reg_init(lh, LCK_PW); if (ma->ma_attr.la_valid & (LA_MODE|LA_UID|LA_GID)) lockpart |= MDS_INODELOCK_LOOKUP; rc = mdt_object_lock(info, mo, lh, lockpart, MDT_LOCAL_LOCK); if (rc != 0) RETURN(rc); if (mdt_object_exists(mo) == 0) GOTO(out_unlock, rc = -ENOENT); /* all attrs are packed into mti_attr in unpack_setattr */ mdt_fail_write(info->mti_env, info->mti_mdt->mdt_bottom, OBD_FAIL_MDS_REINT_SETATTR_WRITE); /* This is only for set ctime when rename's source is on remote MDS. */ if (unlikely(ma->ma_attr.la_valid == LA_CTIME)) ma->ma_attr_flags |= MDS_VTX_BYPASS; /* VBR: update version if attr changed are important for recovery */ if (do_vbr) { /* update on-disk version of changed object */ info->mti_mos = mo; rc = mdt_version_get_check_save(info, mo, 0); if (rc) GOTO(out_unlock, rc); } /* all attrs are packed into mti_attr in unpack_setattr */ rc = mo_attr_set(info->mti_env, mdt_object_child(mo), ma); if (rc != 0) GOTO(out_unlock, rc); EXIT; out_unlock: mdt_object_unlock(info, mo, lh, rc); return rc; }
/* if object is dying, pack the lov/llog data, * parameter info->mti_attr should be valid at this point! */ int mdt_handle_last_unlink(struct mdt_thread_info *info, struct mdt_object *mo, const struct md_attr *ma) { struct mdt_body *repbody; const struct lu_attr *la = &ma->ma_attr; int rc; ENTRY; repbody = req_capsule_server_get(info->mti_pill, &RMF_MDT_BODY); LASSERT(repbody != NULL); if (ma->ma_valid & MA_INODE) mdt_pack_attr2body(info, repbody, la, mdt_object_fid(mo)); if (ma->ma_valid & MA_LOV) { __u32 mode; if (mdt_object_exists(mo) < 0) /* If it is a remote object, and we do not retrieve * EA back unlink reg file*/ mode = S_IFREG; else mode = lu_object_attr(&mo->mot_obj.mo_lu); LASSERT(ma->ma_lmm_size); mdt_dump_lmm(D_INFO, ma->ma_lmm); repbody->eadatasize = ma->ma_lmm_size; if (S_ISREG(mode)) repbody->valid |= OBD_MD_FLEASIZE; else if (S_ISDIR(mode)) repbody->valid |= OBD_MD_FLDIREA; else LBUG(); } if (ma->ma_cookie_size && (ma->ma_valid & MA_COOKIE)) { repbody->aclsize = ma->ma_cookie_size; repbody->valid |= OBD_MD_FLCOOKIE; } if (info->mti_mdt->mdt_opts.mo_oss_capa && info->mti_exp->exp_connect_flags & OBD_CONNECT_OSS_CAPA && repbody->valid & OBD_MD_FLEASIZE) { struct lustre_capa *capa; capa = req_capsule_server_get(info->mti_pill, &RMF_CAPA2); LASSERT(capa); capa->lc_opc = CAPA_OPC_OSS_DESTROY; rc = mo_capa_get(info->mti_env, mdt_object_child(mo), capa, 0); if (rc) RETURN(rc); repbody->valid |= OBD_MD_FLOSSCAPA; } RETURN(0); }
int mdt_attr_set(struct mdt_thread_info *info, struct mdt_object *mo, struct md_attr *ma, int flags) { struct mdt_lock_handle *lh; int do_vbr = ma->ma_attr.la_valid & (LA_MODE|LA_UID|LA_GID|LA_FLAGS); __u64 lockpart = MDS_INODELOCK_UPDATE; int rc; ENTRY; /* attr shouldn't be set on remote object */ LASSERT(!mdt_object_remote(mo)); lh = &info->mti_lh[MDT_LH_PARENT]; mdt_lock_reg_init(lh, LCK_PW); /* Even though the new MDT will grant PERM lock to the old * client, but the old client will almost ignore that during * So it needs to revoke both LOOKUP and PERM lock here, so * both new and old client can cancel the dcache */ if (ma->ma_attr.la_valid & (LA_MODE|LA_UID|LA_GID)) lockpart |= MDS_INODELOCK_LOOKUP | MDS_INODELOCK_PERM; rc = mdt_object_lock(info, mo, lh, lockpart, MDT_LOCAL_LOCK); if (rc != 0) RETURN(rc); if (mdt_object_exists(mo) == 0) GOTO(out_unlock, rc = -ENOENT); /* all attrs are packed into mti_attr in unpack_setattr */ mdt_fail_write(info->mti_env, info->mti_mdt->mdt_bottom, OBD_FAIL_MDS_REINT_SETATTR_WRITE); /* This is only for set ctime when rename's source is on remote MDS. */ if (unlikely(ma->ma_attr.la_valid == LA_CTIME)) ma->ma_attr_flags |= MDS_VTX_BYPASS; /* VBR: update version if attr changed are important for recovery */ if (do_vbr) { /* update on-disk version of changed object */ tgt_vbr_obj_set(info->mti_env, mdt_obj2dt(mo)); rc = mdt_version_get_check_save(info, mo, 0); if (rc) GOTO(out_unlock, rc); } /* all attrs are packed into mti_attr in unpack_setattr */ rc = mo_attr_set(info->mti_env, mdt_object_child(mo), ma); if (rc != 0) GOTO(out_unlock, rc); EXIT; out_unlock: mdt_object_unlock(info, mo, lh, rc); return rc; }
/* Partial request to create object only */ static int mdt_md_mkobj(struct mdt_thread_info *info) { struct mdt_device *mdt = info->mti_mdt; struct mdt_object *o; struct mdt_body *repbody; struct md_attr *ma = &info->mti_attr; int rc; ENTRY; DEBUG_REQ(D_INODE, mdt_info_req(info), "Partial create "DFID"", PFID(info->mti_rr.rr_fid2)); repbody = req_capsule_server_get(info->mti_pill, &RMF_MDT_BODY); o = mdt_object_find(info->mti_env, mdt, info->mti_rr.rr_fid2, MDT_OBJ_MAY_NOT_EXIST); if (!IS_ERR(o)) { struct md_object *next = mdt_object_child(o); ma->ma_need = MA_INODE; ma->ma_valid = 0; /* * Cross-ref create can encounter already created obj in case of * recovery, just get attr in that case. */ if (mdt_object_exists(o) == 1) { rc = mo_attr_get(info->mti_env, next, ma); } else { /* * Here, NO permission check for object_create, * such check has been done on the original MDS. */ rc = mo_object_create(info->mti_env, next, &info->mti_spec, ma); } if (rc == 0) { /* Return fid & attr to client. */ if (ma->ma_valid & MA_INODE) mdt_pack_attr2body(info, repbody, &ma->ma_attr, mdt_object_fid(o)); } mdt_object_put(info->mti_env, o); } else rc = PTR_ERR(o); mdt_create_pack_capa(info, rc, o, repbody); RETURN(rc); }
static int mdt_lvbo_fill(struct ldlm_lock *lock, void *lvb, int lvblen) { struct lu_env env; struct mdt_thread_info *info; struct mdt_device *mdt; struct lu_fid *fid; struct mdt_object *obj = NULL; struct md_object *child = NULL; int rc; ENTRY; mdt = ldlm_lock_to_ns(lock)->ns_lvbp; if (IS_LQUOTA_RES(lock->l_resource)) { if (mdt->mdt_qmt_dev == NULL) RETURN(0); /* call lvbo fill function of quota master */ rc = qmt_hdls.qmth_lvbo_fill(mdt->mdt_qmt_dev, lock, lvb, lvblen); RETURN(rc); } if (!ldlm_has_layout(lock)) RETURN(0); /* layout lock will be granted to client, fill in lvb with layout */ /* XXX create an env to talk to mdt stack. We should get this env from * ptlrpc_thread->t_env. */ rc = lu_env_init(&env, LCT_MD_THREAD); /* Likely ENOMEM */ if (rc) RETURN(rc); info = lu_context_key_get(&env.le_ctx, &mdt_thread_key); /* Likely ENOMEM */ if (info == NULL) GOTO(out, rc = -ENOMEM); memset(info, 0, sizeof *info); info->mti_env = &env; info->mti_exp = lock->l_export; info->mti_mdt = mdt; /* XXX get fid by resource id. why don't include fid in ldlm_resource */ fid = &info->mti_tmp_fid2; fid_extract_from_res_name(fid, &lock->l_resource->lr_name); obj = mdt_object_find(&env, info->mti_mdt, fid); if (IS_ERR(obj)) GOTO(out, rc = PTR_ERR(obj)); if (!mdt_object_exists(obj) || mdt_object_remote(obj)) GOTO(out, rc = -ENOENT); child = mdt_object_child(obj); /* get the length of lsm */ rc = mo_xattr_get(&env, child, &LU_BUF_NULL, XATTR_NAME_LOV); if (rc < 0) GOTO(out, rc); if (rc > 0) { struct lu_buf *lmm = NULL; if (lvblen < rc) { CERROR("%s: expected %d actual %d.\n", mdt_obd_name(mdt), rc, lvblen); GOTO(out, rc = -ERANGE); } lmm = &info->mti_buf; lmm->lb_buf = lvb; lmm->lb_len = rc; rc = mo_xattr_get(&env, child, lmm, XATTR_NAME_LOV); if (rc < 0) GOTO(out, rc); } out: if (obj != NULL && !IS_ERR(obj)) mdt_object_put(&env, obj); lu_env_fini(&env); RETURN(rc < 0 ? 0 : rc); }
/* * VBR: rename versions in reply: 0 - src parent; 1 - tgt parent; * 2 - src child; 3 - tgt child. * Update on disk version of src child. */ static int mdt_reint_rename(struct mdt_thread_info *info, struct mdt_lock_handle *lhc) { struct mdt_reint_record *rr = &info->mti_rr; struct md_attr *ma = &info->mti_attr; struct ptlrpc_request *req = mdt_info_req(info); struct mdt_object *msrcdir; struct mdt_object *mtgtdir; struct mdt_object *mold; struct mdt_object *mnew = NULL; struct mdt_lock_handle *lh_srcdirp; struct mdt_lock_handle *lh_tgtdirp; struct mdt_lock_handle *lh_oldp; struct mdt_lock_handle *lh_newp; struct lu_fid *old_fid = &info->mti_tmp_fid1; struct lu_fid *new_fid = &info->mti_tmp_fid2; struct lustre_handle rename_lh = { 0 }; struct lu_name slname = { 0 }; struct lu_name *lname; int rc; ENTRY; if (info->mti_dlm_req) ldlm_request_cancel(req, info->mti_dlm_req, 0); DEBUG_REQ(D_INODE, req, "rename "DFID"/%s to "DFID"/%s", PFID(rr->rr_fid1), rr->rr_name, PFID(rr->rr_fid2), rr->rr_tgt); rc = mdt_rename_lock(info, &rename_lh); if (rc) { CERROR("Can't lock FS for rename, rc %d\n", rc); RETURN(rc); } lh_newp = &info->mti_lh[MDT_LH_NEW]; /* step 1: lock the source dir. */ lh_srcdirp = &info->mti_lh[MDT_LH_PARENT]; mdt_lock_pdo_init(lh_srcdirp, LCK_PW, rr->rr_name, rr->rr_namelen); msrcdir = mdt_object_find_lock(info, rr->rr_fid1, lh_srcdirp, MDS_INODELOCK_UPDATE); if (IS_ERR(msrcdir)) GOTO(out_rename_lock, rc = PTR_ERR(msrcdir)); if (mdt_object_obf(msrcdir)) GOTO(out_unlock_source, rc = -EPERM); rc = mdt_version_get_check_save(info, msrcdir, 0); if (rc) GOTO(out_unlock_source, rc); /* step 2: find & lock the target dir. */ lh_tgtdirp = &info->mti_lh[MDT_LH_CHILD]; mdt_lock_pdo_init(lh_tgtdirp, LCK_PW, rr->rr_tgt, rr->rr_tgtlen); if (lu_fid_eq(rr->rr_fid1, rr->rr_fid2)) { mdt_object_get(info->mti_env, msrcdir); mtgtdir = msrcdir; if (lh_tgtdirp->mlh_pdo_hash != lh_srcdirp->mlh_pdo_hash) { rc = mdt_pdir_hash_lock(info, lh_tgtdirp, mtgtdir, MDS_INODELOCK_UPDATE); if (rc) GOTO(out_unlock_source, rc); OBD_FAIL_TIMEOUT(OBD_FAIL_MDS_PDO_LOCK2, 10); } } else { mtgtdir = mdt_object_find(info->mti_env, info->mti_mdt, rr->rr_fid2); if (IS_ERR(mtgtdir)) GOTO(out_unlock_source, rc = PTR_ERR(mtgtdir)); if (mdt_object_obf(mtgtdir)) GOTO(out_put_target, rc = -EPERM); /* check early, the real version will be saved after locking */ rc = mdt_version_get_check(info, mtgtdir, 1); if (rc) GOTO(out_put_target, rc); if (unlikely(mdt_object_remote(mtgtdir))) { CDEBUG(D_INFO, "Source dir "DFID" target dir "DFID "on different MDTs\n", PFID(rr->rr_fid1), PFID(rr->rr_fid2)); GOTO(out_put_target, rc = -EXDEV); } else { if (likely(mdt_object_exists(mtgtdir))) { /* we lock the target dir if it is local */ rc = mdt_object_lock(info, mtgtdir, lh_tgtdirp, MDS_INODELOCK_UPDATE, MDT_LOCAL_LOCK); if (rc != 0) GOTO(out_put_target, rc); /* get and save correct version after locking */ mdt_version_get_save(info, mtgtdir, 1); } else { GOTO(out_put_target, rc = -ESTALE); } } } /* step 3: find & lock the old object. */ lname = mdt_name(info->mti_env, (char *)rr->rr_name, rr->rr_namelen); mdt_name_copy(&slname, lname); fid_zero(old_fid); rc = mdt_lookup_version_check(info, msrcdir, &slname, old_fid, 2); if (rc != 0) GOTO(out_unlock_target, rc); if (lu_fid_eq(old_fid, rr->rr_fid1) || lu_fid_eq(old_fid, rr->rr_fid2)) GOTO(out_unlock_target, rc = -EINVAL); mold = mdt_object_find(info->mti_env, info->mti_mdt, old_fid); if (IS_ERR(mold)) GOTO(out_unlock_target, rc = PTR_ERR(mold)); if (mdt_object_remote(mold)) { mdt_object_put(info->mti_env, mold); CDEBUG(D_INFO, "Source child "DFID" is on another MDT\n", PFID(old_fid)); GOTO(out_unlock_target, rc = -EXDEV); } if (mdt_object_obf(mold)) { mdt_object_put(info->mti_env, mold); GOTO(out_unlock_target, rc = -EPERM); } lh_oldp = &info->mti_lh[MDT_LH_OLD]; mdt_lock_reg_init(lh_oldp, LCK_EX); rc = mdt_object_lock(info, mold, lh_oldp, MDS_INODELOCK_LOOKUP, MDT_CROSS_LOCK); if (rc != 0) { mdt_object_put(info->mti_env, mold); GOTO(out_unlock_target, rc); } info->mti_mos = mold; /* save version after locking */ mdt_version_get_save(info, mold, 2); mdt_set_capainfo(info, 2, old_fid, BYPASS_CAPA); /* step 4: find & lock the new object. */ /* new target object may not exist now */ lname = mdt_name(info->mti_env, (char *)rr->rr_tgt, rr->rr_tgtlen); /* lookup with version checking */ fid_zero(new_fid); rc = mdt_lookup_version_check(info, mtgtdir, lname, new_fid, 3); if (rc == 0) { /* the new_fid should have been filled at this moment */ if (lu_fid_eq(old_fid, new_fid)) GOTO(out_unlock_old, rc); if (lu_fid_eq(new_fid, rr->rr_fid1) || lu_fid_eq(new_fid, rr->rr_fid2)) GOTO(out_unlock_old, rc = -EINVAL); mdt_lock_reg_init(lh_newp, LCK_EX); mnew = mdt_object_find(info->mti_env, info->mti_mdt, new_fid); if (IS_ERR(mnew)) GOTO(out_unlock_old, rc = PTR_ERR(mnew)); if (mdt_object_obf(mnew)) { mdt_object_put(info->mti_env, mnew); GOTO(out_unlock_old, rc = -EPERM); } if (mdt_object_remote(mnew)) { mdt_object_put(info->mti_env, mnew); CDEBUG(D_INFO, "src child "DFID" is on another MDT\n", PFID(new_fid)); GOTO(out_unlock_old, rc = -EXDEV); } rc = mdt_object_lock(info, mnew, lh_newp, MDS_INODELOCK_FULL, MDT_CROSS_LOCK); if (rc != 0) { mdt_object_put(info->mti_env, mnew); GOTO(out_unlock_old, rc); } /* get and save version after locking */ mdt_version_get_save(info, mnew, 3); mdt_set_capainfo(info, 3, new_fid, BYPASS_CAPA); } else if (rc != -EREMOTE && rc != -ENOENT) { GOTO(out_unlock_old, rc); } else { mdt_enoent_version_save(info, 3); } /* step 5: rename it */ mdt_reint_init_ma(info, ma); mdt_fail_write(info->mti_env, info->mti_mdt->mdt_bottom, OBD_FAIL_MDS_REINT_RENAME_WRITE); /* Check if @dst is subdir of @src. */ rc = mdt_rename_sanity(info, old_fid); if (rc) GOTO(out_unlock_new, rc); rc = mdo_rename(info->mti_env, mdt_object_child(msrcdir), mdt_object_child(mtgtdir), old_fid, &slname, (mnew ? mdt_object_child(mnew) : NULL), lname, ma); /* handle last link of tgt object */ if (rc == 0) { mdt_counter_incr(req, LPROC_MDT_RENAME); if (mnew) mdt_handle_last_unlink(info, mnew, ma); mdt_rename_counter_tally(info, info->mti_mdt, req, msrcdir, mtgtdir); } EXIT; out_unlock_new: if (mnew) mdt_object_unlock_put(info, mnew, lh_newp, rc); out_unlock_old: mdt_object_unlock_put(info, mold, lh_oldp, rc); out_unlock_target: mdt_object_unlock(info, mtgtdir, lh_tgtdirp, rc); out_put_target: mdt_object_put(info->mti_env, mtgtdir); out_unlock_source: mdt_object_unlock_put(info, msrcdir, lh_srcdirp, rc); out_rename_lock: if (lustre_handle_is_used(&rename_lh)) mdt_rename_unlock(&rename_lh); return rc; }
/* * VBR: save parent version in reply and child version getting by its name. * Version of child is getting and checking during its lookup. If */ static int mdt_reint_unlink(struct mdt_thread_info *info, struct mdt_lock_handle *lhc) { struct mdt_reint_record *rr = &info->mti_rr; struct ptlrpc_request *req = mdt_info_req(info); struct md_attr *ma = &info->mti_attr; struct lu_fid *child_fid = &info->mti_tmp_fid1; struct mdt_object *mp; struct mdt_object *mc; struct mdt_lock_handle *parent_lh; struct mdt_lock_handle *child_lh; struct lu_name *lname; int rc; ENTRY; DEBUG_REQ(D_INODE, req, "unlink "DFID"/%s", PFID(rr->rr_fid1), rr->rr_name); if (info->mti_dlm_req) ldlm_request_cancel(req, info->mti_dlm_req, 0); if (OBD_FAIL_CHECK(OBD_FAIL_MDS_REINT_UNLINK)) RETURN(err_serious(-ENOENT)); /* * step 1: lock the parent. Note, this may be child in case of * remote operation denoted by ->mti_cross_ref flag. */ parent_lh = &info->mti_lh[MDT_LH_PARENT]; if (info->mti_cross_ref) { /* * Init reg lock for cross ref case when we need to do only * ref del locally. */ mdt_lock_reg_init(parent_lh, LCK_PW); } else { mdt_lock_pdo_init(parent_lh, LCK_PW, rr->rr_name, rr->rr_namelen); } mp = mdt_object_find_lock(info, rr->rr_fid1, parent_lh, MDS_INODELOCK_UPDATE, MDT_OBJ_MUST_EXIST); if (IS_ERR(mp)) { rc = PTR_ERR(mp); /* errors are possible here in cross-ref cases, see below */ if (info->mti_cross_ref) rc = 0; GOTO(out, rc); } rc = mdt_version_get_check_save(info, mp, 0); if (rc) GOTO(out_unlock_parent, rc); mdt_reint_init_ma(info, ma); if (!ma->ma_lmm || !ma->ma_cookie) GOTO(out_unlock_parent, rc = -EINVAL); if (info->mti_cross_ref) { /* * Remote partial operation. It is possible that replay may * happen on parent MDT and this operation will be repeated. * Therefore the object absense is allowed case and nothing * should be done here. */ if (mdt_object_exists(mp) > 0) { mdt_set_capainfo(info, 0, rr->rr_fid1, BYPASS_CAPA); rc = mo_ref_del(info->mti_env, mdt_object_child(mp), ma); if (rc == 0) mdt_handle_last_unlink(info, mp, ma); } else rc = 0; GOTO(out_unlock_parent, rc); } /* step 2: find & lock the child */ lname = mdt_name(info->mti_env, (char *)rr->rr_name, rr->rr_namelen); /* lookup child object along with version checking */ rc = mdt_lookup_version_check(info, mp, lname, child_fid, 1); if (rc != 0) GOTO(out_unlock_parent, rc); /* We will lock the child regardless it is local or remote. No harm. */ mc = mdt_object_find(info->mti_env, info->mti_mdt, child_fid, MDT_OBJ_MUST_EXIST); if (IS_ERR(mc)) GOTO(out_unlock_parent, rc = PTR_ERR(mc)); child_lh = &info->mti_lh[MDT_LH_CHILD]; mdt_lock_reg_init(child_lh, LCK_EX); rc = mdt_object_lock(info, mc, child_lh, MDS_INODELOCK_FULL, MDT_CROSS_LOCK); if (rc != 0) { mdt_object_put(info->mti_env, mc); GOTO(out_unlock_parent, rc); } mdt_fail_write(info->mti_env, info->mti_mdt->mdt_bottom, OBD_FAIL_MDS_REINT_UNLINK_WRITE); /* save version when object is locked */ mdt_version_get_save(info, mc, 1); /* * Now we can only make sure we need MA_INODE, in mdd layer, will check * whether need MA_LOV and MA_COOKIE. */ ma->ma_need = MA_INODE; ma->ma_valid = 0; mdt_set_capainfo(info, 1, child_fid, BYPASS_CAPA); rc = mdo_unlink(info->mti_env, mdt_object_child(mp), mdt_object_child(mc), lname, ma); if (rc == 0) mdt_handle_last_unlink(info, mc, ma); if (ma->ma_valid & MA_INODE) { switch (ma->ma_attr.la_mode & S_IFMT) { case S_IFDIR: mdt_counter_incr(req->rq_export, LPROC_MDT_RMDIR); break; case S_IFREG: case S_IFLNK: case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: mdt_counter_incr(req->rq_export, LPROC_MDT_UNLINK); break; default: LASSERTF(0, "bad file type %o unlinking\n", ma->ma_attr.la_mode); } } EXIT; mdt_object_unlock_put(info, mc, child_lh, rc); out_unlock_parent: mdt_object_unlock_put(info, mp, parent_lh, rc); out: return rc; }