From f311ac161a41f99c62d6182e88e1de6360dc3912 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 7 Apr 2015 14:31:58 +0800 Subject: [PATCH] mds: rename snapshot support Fixes: #3645 Signed-off-by: Yan, Zheng --- src/client/Client.cc | 66 ++++++++++++-------- src/common/ceph_strings.cc | 1 + src/include/ceph_fs.h | 1 + src/mds/Server.cc | 122 ++++++++++++++++++++++++++++++++++++- src/mds/Server.h | 3 + 5 files changed, 166 insertions(+), 27 deletions(-) diff --git a/src/client/Client.cc b/src/client/Client.cc index 7cb71a8e52ba3..5d8f4b82257d0 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -9419,9 +9419,15 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch ldout(cct, 3) << "_rename(" << fromdir->ino << " " << fromname << " to " << todir->ino << " " << toname << " uid " << uid << " gid " << gid << ")" << dendl; - if (fromdir->snapid != CEPH_NOSNAP || - todir->snapid != CEPH_NOSNAP) { - return -EROFS; + if (fromdir->snapid != todir->snapid) + return -EXDEV; + + int op = CEPH_MDS_OP_RENAME; + if (fromdir->snapid != CEPH_NOSNAP) { + if (fromdir == todir && fromdir->snapid == CEPH_SNAPDIR) + op = CEPH_MDS_OP_RENAMESNAP; + else + return -EROFS; } if (cct->_conf->client_quota && fromdir != todir && @@ -9431,7 +9437,7 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch return -EXDEV; } - MetaRequest *req = new MetaRequest(CEPH_MDS_OP_RENAME); + MetaRequest *req = new MetaRequest(op); filepath from; fromdir->make_nosnap_relative_path(from); @@ -9446,35 +9452,43 @@ int Client::_rename(Inode *fromdir, const char *fromname, Inode *todir, const ch int res = get_or_create(fromdir, fromname, &oldde); if (res < 0) goto fail; - req->set_old_dentry(oldde); - req->old_dentry_drop = CEPH_CAP_FILE_SHARED; - req->old_dentry_unless = CEPH_CAP_FILE_EXCL; - Dentry *de; res = get_or_create(todir, toname, &de); if (res < 0) goto fail; - req->set_dentry(de); - req->dentry_drop = CEPH_CAP_FILE_SHARED; - req->dentry_unless = CEPH_CAP_FILE_EXCL; - Inode *oldin; - res = _lookup(fromdir, fromname, &oldin); - if (res < 0) - goto fail; - req->set_old_inode(oldin); - req->old_inode_drop = CEPH_CAP_LINK_SHARED; + if (op == CEPH_MDS_OP_RENAME) { + req->set_old_dentry(oldde); + req->old_dentry_drop = CEPH_CAP_FILE_SHARED; + req->old_dentry_unless = CEPH_CAP_FILE_EXCL; - Inode *otherin; - res = _lookup(todir, toname, &otherin); - if (res != 0 && res != -ENOENT) { - goto fail; - } else if (res == 0) { - req->set_other_inode(otherin); - req->other_inode_drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; - } + req->set_dentry(de); + req->dentry_drop = CEPH_CAP_FILE_SHARED; + req->dentry_unless = CEPH_CAP_FILE_EXCL; + + Inode *oldin; + res = _lookup(fromdir, fromname, &oldin); + if (res < 0) + goto fail; + req->set_old_inode(oldin); + req->old_inode_drop = CEPH_CAP_LINK_SHARED; - req->set_inode(todir); + Inode *otherin; + res = _lookup(todir, toname, &otherin); + if (res != 0 && res != -ENOENT) { + goto fail; + } else if (res == 0) { + req->set_other_inode(otherin); + req->other_inode_drop = CEPH_CAP_LINK_SHARED | CEPH_CAP_LINK_EXCL; + } + + req->set_inode(todir); + } else { + // renamesnap reply contains no tracedn, so we need to invalidate + // dentry manually + unlink(oldde, true, true); + unlink(de, true, true); + } Inode *target; res = make_request(req, uid, gid, &target); diff --git a/src/common/ceph_strings.cc b/src/common/ceph_strings.cc index dd7c9ed40ba28..d1a418d2d5c28 100644 --- a/src/common/ceph_strings.cc +++ b/src/common/ceph_strings.cc @@ -129,6 +129,7 @@ const char *ceph_mds_op_name(int op) case CEPH_MDS_OP_LSSNAP: return "lssnap"; case CEPH_MDS_OP_MKSNAP: return "mksnap"; case CEPH_MDS_OP_RMSNAP: return "rmsnap"; + case CEPH_MDS_OP_RENAMESNAP: return "renamesnap"; case CEPH_MDS_OP_SETFILELOCK: return "setfilelock"; case CEPH_MDS_OP_GETFILELOCK: return "getfilelock"; case CEPH_MDS_OP_FRAGMENTDIR: return "fragmentdir"; diff --git a/src/include/ceph_fs.h b/src/include/ceph_fs.h index 9fd4b1a144c99..63922f5654b05 100644 --- a/src/include/ceph_fs.h +++ b/src/include/ceph_fs.h @@ -342,6 +342,7 @@ enum { CEPH_MDS_OP_MKSNAP = 0x01400, CEPH_MDS_OP_RMSNAP = 0x01401, CEPH_MDS_OP_LSSNAP = 0x00402, + CEPH_MDS_OP_RENAMESNAP = 0x01403, // internal op CEPH_MDS_OP_FRAGMENTDIR= 0x01500, diff --git a/src/mds/Server.cc b/src/mds/Server.cc index eabb152228919..14a80f5805984 100644 --- a/src/mds/Server.cc +++ b/src/mds/Server.cc @@ -1531,7 +1531,9 @@ void Server::dispatch_client_request(MDRequestRef& mdr) case CEPH_MDS_OP_RMSNAP: handle_client_rmsnap(mdr); break; - + case CEPH_MDS_OP_RENAMESNAP: + handle_client_renamesnap(mdr); + break; default: dout(1) << " unknown client op " << req->get_op() << dendl; @@ -7919,6 +7921,124 @@ void Server::_rmsnap_finish(MDRequestRef& mdr, CInode *diri, snapid_t snapid) diri->purge_stale_snap_data(diri->snaprealm->get_snaps()); } +struct C_MDS_renamesnap_finish : public MDSInternalContext { + MDRequestRef mdr; + CInode *diri; + snapid_t snapid; + C_MDS_renamesnap_finish(MDS *m, MDRequestRef& r, CInode *di, snapid_t sn) : + MDSInternalContext(m), mdr(r), diri(di), snapid(sn) {} + void finish(int r) { + mds->server->_renamesnap_finish(mdr, diri, snapid); + } +}; + +/* This function takes responsibility for the passed mdr*/ +void Server::handle_client_renamesnap(MDRequestRef& mdr) +{ + MClientRequest *req = mdr->client_request; + if (req->get_filepath().get_ino() != req->get_filepath2().get_ino()) { + respond_to_request(mdr, -EINVAL); + return; + } + + CInode *diri = mdcache->get_inode(req->get_filepath().get_ino()); + if (!diri || diri->state_test(CInode::STATE_PURGING)) { + respond_to_request(mdr, -ESTALE); + return; + } + + if (!diri->is_auth()) { // fw to auth? + mdcache->request_forward(mdr, diri->authority().first); + return; + } + + if (!diri->is_dir()) { // dir only + respond_to_request(mdr, -ENOTDIR); + return; + } + + if (mdr->client_request->get_caller_uid() < g_conf->mds_snap_min_uid || + mdr->client_request->get_caller_uid() > g_conf->mds_snap_max_uid) { + respond_to_request(mdr, -EPERM); + return; + } + + const string &dstname = req->get_filepath().last_dentry(); + const string &srcname = req->get_filepath2().last_dentry(); + dout(10) << "renamesnap " << srcname << "->" << dstname << " on " << *diri << dendl; + + if (srcname.length() == 0 || srcname[0] == '_') { + respond_to_request(mdr, -EINVAL); // can't rename a parent snap. + return; + } + if (!diri->snaprealm || !diri->snaprealm->exists(srcname)) { + respond_to_request(mdr, -ENOENT); + return; + } + if (dstname.length() == 0 || dstname[0] == '_') { + respond_to_request(mdr, -EINVAL); + return; + } + if (diri->snaprealm->exists(dstname)) { + respond_to_request(mdr, -EEXIST); + return; + } + + snapid_t snapid = diri->snaprealm->resolve_snapname(srcname, diri->ino()); + dout(10) << " snapname " << srcname << " is " << snapid << dendl; + + // lock snap + set rdlocks, wrlocks, xlocks; + + mds->locker->include_snap_rdlocks(rdlocks, diri); + rdlocks.erase(&diri->snaplock); + xlocks.insert(&diri->snaplock); + + if (!mds->locker->acquire_locks(mdr, rdlocks, wrlocks, xlocks)) + return; + + // journal + inode_t *pi = diri->project_inode(); + pi->ctime = mdr->get_op_stamp(); + pi->version = diri->pre_dirty(); + + // project the snaprealm + sr_t *newsnap = diri->project_snaprealm(); + assert(newsnap->snaps.count(snapid)); + newsnap->snaps[snapid].name = dstname; + + // journal the inode changes + mdr->ls = mdlog->get_current_segment(); + EUpdate *le = new EUpdate(mdlog, "renamesnap"); + mdlog->start_entry(le); + + le->metablob.add_client_req(req->get_reqid(), req->get_oldest_client_tid()); + mdcache->predirty_journal_parents(mdr, &le->metablob, diri, 0, PREDIRTY_PRIMARY, false); + mdcache->journal_dirty_inode(mdr.get(), &le->metablob, diri); + + // journal the snaprealm changes + submit_mdlog_entry(le, new C_MDS_renamesnap_finish(mds, mdr, diri, snapid), + mdr, __func__); + mdlog->flush(); +} + +void Server::_renamesnap_finish(MDRequestRef& mdr, CInode *diri, snapid_t snapid) +{ + dout(10) << "_renamesnap_finish " << *mdr << " " << snapid << dendl; + + diri->pop_and_dirty_projected_inode(mdr->ls); + mdr->apply(); + + dout(10) << "snaprealm now " << *diri->snaprealm << dendl; + + mdcache->do_realm_invalidate_and_update_notify(diri, CEPH_SNAP_OP_CREATE, true); + + // yay + mdr->in[0] = diri; + mdr->tracei = diri; + mdr->snapid = snapid; + respond_to_request(mdr, 0); +} /** * Return true if server is in state RECONNECT and this diff --git a/src/mds/Server.h b/src/mds/Server.h index 17dcd94372ba2..d5b9066da7fe9 100644 --- a/src/mds/Server.h +++ b/src/mds/Server.h @@ -243,6 +243,9 @@ public: void _mksnap_finish(MDRequestRef& mdr, CInode *diri, SnapInfo &info); void handle_client_rmsnap(MDRequestRef& mdr); void _rmsnap_finish(MDRequestRef& mdr, CInode *diri, snapid_t snapid); + void handle_client_renamesnap(MDRequestRef& mdr); + void _renamesnap_finish(MDRequestRef& mdr, CInode *diri, snapid_t snapid); + // helpers bool _rename_prepare_witness(MDRequestRef& mdr, mds_rank_t who, set &witnesse, -- 2.39.5