summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2018-12-17 15:15:43 -0500
committerRandolph Tan <randolph@10gen.com>2019-01-11 14:32:51 -0500
commit891ca0c23f979268fa0b9403500a8a582646613b (patch)
tree36040158a601bd52385ff01effb78f4deeb9d56b /src/mongo
parent3b028cfc6f5ced60c180c340a46d4d06f7ac9815 (diff)
downloadmongo-891ca0c23f979268fa0b9403500a8a582646613b.tar.gz
SERVER-38179 Refactor RemoveSaver out of dbhelpers
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/dbhelpers.cpp128
-rw-r--r--src/mongo/db/dbhelpers.h46
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/rollback_impl.cpp4
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp7
-rw-r--r--src/mongo/db/s/SConscript1
-rw-r--r--src/mongo/db/s/collection_range_deleter.cpp3
-rw-r--r--src/mongo/db/s/migration_destination_manager.cpp3
-rw-r--r--src/mongo/db/storage/SConscript12
-rw-r--r--src/mongo/db/storage/remove_saver.cpp179
-rw-r--r--src/mongo/db/storage/remove_saver.h85
11 files changed, 288 insertions, 181 deletions
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index d1b5b9518fa..6598b445dcc 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -34,9 +34,6 @@
#include "mongo/db/dbhelpers.h"
-#include <boost/filesystem/operations.hpp>
-#include <fstream>
-
#include "mongo/db/db_raii.h"
#include "mongo/db/exec/working_set_common.h"
#include "mongo/db/index/btree_access_method.h"
@@ -55,9 +52,6 @@
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/service_context.h"
-#include "mongo/db/storage/data_protector.h"
-#include "mongo/db/storage/encryption_hooks.h"
-#include "mongo/db/storage/storage_options.h"
#include "mongo/db/write_concern.h"
#include "mongo/db/write_concern_options.h"
#include "mongo/util/log.h"
@@ -67,11 +61,8 @@
namespace mongo {
using std::unique_ptr;
-using std::ios_base;
-using std::ofstream;
using std::set;
using std::string;
-using std::stringstream;
/* fetch a single object from collection ns that matches query
set your db SavedContext first
@@ -276,123 +267,4 @@ void Helpers::emptyCollection(OperationContext* opCtx, const NamespaceString& ns
deleteObjects(opCtx, collection, nss, BSONObj(), false);
}
-Helpers::RemoveSaver::RemoveSaver(const string& a, const string& b, const string& why) {
- static int NUM = 0;
-
- _root = storageGlobalParams.dbpath;
- if (a.size())
- _root /= a;
- if (b.size())
- _root /= b;
- verify(a.size() || b.size());
-
- _file = _root;
-
- stringstream ss;
- ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson";
- _file /= ss.str();
-
- auto encryptionHooks = EncryptionHooks::get(getGlobalServiceContext());
- if (encryptionHooks->enabled()) {
- _protector = encryptionHooks->getDataProtector();
- _file += encryptionHooks->getProtectedPathSuffix();
- }
-}
-
-Helpers::RemoveSaver::~RemoveSaver() {
- if (_protector && _out) {
- auto encryptionHooks = EncryptionHooks::get(getGlobalServiceContext());
- invariant(encryptionHooks->enabled());
-
- size_t protectedSizeMax = encryptionHooks->additionalBytesForProtectedBuffer();
- std::unique_ptr<uint8_t[]> protectedBuffer(new uint8_t[protectedSizeMax]);
-
- size_t resultLen;
- Status status = _protector->finalize(protectedBuffer.get(), protectedSizeMax, &resultLen);
- if (!status.isOK()) {
- severe() << "Unable to finalize DataProtector while closing RemoveSaver: "
- << redact(status);
- fassertFailed(34350);
- }
-
- _out->write(reinterpret_cast<const char*>(protectedBuffer.get()), resultLen);
- if (_out->fail()) {
- severe() << "Couldn't write finalized DataProtector data to: " << _file.string()
- << " for remove saving: " << redact(errnoWithDescription());
- fassertFailed(34351);
- }
-
- protectedBuffer.reset(new uint8_t[protectedSizeMax]);
- status = _protector->finalizeTag(protectedBuffer.get(), protectedSizeMax, &resultLen);
- if (!status.isOK()) {
- severe() << "Unable to get finalizeTag from DataProtector while closing RemoveSaver: "
- << redact(status);
- fassertFailed(34352);
- }
- if (resultLen != _protector->getNumberOfBytesReservedForTag()) {
- severe() << "Attempted to write tag of size " << resultLen
- << " when DataProtector only reserved "
- << _protector->getNumberOfBytesReservedForTag() << " bytes";
- fassertFailed(34353);
- }
- _out->seekp(0);
- _out->write(reinterpret_cast<const char*>(protectedBuffer.get()), resultLen);
- if (_out->fail()) {
- severe() << "Couldn't write finalizeTag from DataProtector to: " << _file.string()
- << " for remove saving: " << redact(errnoWithDescription());
- fassertFailed(34354);
- }
- }
-}
-
-Status Helpers::RemoveSaver::goingToDelete(const BSONObj& o) {
- if (!_out) {
- // We don't expect to ever pass "" to create_directories below, but catch
- // this anyway as per SERVER-26412.
- invariant(!_root.empty());
- boost::filesystem::create_directories(_root);
- _out.reset(new ofstream(_file.string().c_str(), ios_base::out | ios_base::binary));
- if (_out->fail()) {
- string msg = str::stream() << "couldn't create file: " << _file.string()
- << " for remove saving: " << redact(errnoWithDescription());
- error() << msg;
- _out.reset();
- _out = 0;
- return Status(ErrorCodes::FileNotOpen, msg);
- }
- }
-
- const uint8_t* data = reinterpret_cast<const uint8_t*>(o.objdata());
- size_t dataSize = o.objsize();
-
- std::unique_ptr<uint8_t[]> protectedBuffer;
- if (_protector) {
- auto encryptionHooks = EncryptionHooks::get(getGlobalServiceContext());
- invariant(encryptionHooks->enabled());
-
- size_t protectedSizeMax = dataSize + encryptionHooks->additionalBytesForProtectedBuffer();
- protectedBuffer.reset(new uint8_t[protectedSizeMax]);
-
- size_t resultLen;
- Status status = _protector->protect(
- data, dataSize, protectedBuffer.get(), protectedSizeMax, &resultLen);
- if (!status.isOK()) {
- return status;
- }
-
- data = protectedBuffer.get();
- dataSize = resultLen;
- }
-
- _out->write(reinterpret_cast<const char*>(data), dataSize);
- if (_out->fail()) {
- string msg = str::stream() << "couldn't write document to file: " << _file.string()
- << " for remove saving: " << redact(errnoWithDescription());
- error() << msg;
- return Status(ErrorCodes::OperationFailed, msg);
- }
- return Status::OK();
-}
-
-
} // namespace mongo
diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h
index 5f7344bb3b5..ecaa5f7af8e 100644
--- a/src/mongo/db/dbhelpers.h
+++ b/src/mongo/db/dbhelpers.h
@@ -30,18 +30,13 @@
#pragma once
-#include <boost/filesystem/path.hpp>
-#include <memory>
-
#include "mongo/db/namespace_string.h"
#include "mongo/db/record_id.h"
-#include "mongo/db/storage/data_protector.h"
namespace mongo {
class Collection;
class Database;
-class DataProtector;
class OperationContext;
class QueryRequest;
@@ -52,7 +47,6 @@ class QueryRequest;
* all helpers assume locking is handled above them
*/
struct Helpers {
- class RemoveSaver;
/* fetch a single object from collection ns that matches query.
set your db SavedContext first.
@@ -150,46 +144,6 @@ struct Helpers {
* Does not oplog the operation.
*/
static void emptyCollection(OperationContext* opCtx, const NamespaceString& nss);
-
- /**
- * for saving deleted bson objects to a flat file
- */
- class RemoveSaver {
- MONGO_DISALLOW_COPYING(RemoveSaver);
-
- public:
- RemoveSaver(const std::string& type, const std::string& ns, const std::string& why);
- ~RemoveSaver();
-
- /**
- * Writes document to file. File is created lazily before writing the first document.
- * Returns error status if the file could not be created or if there were errors writing
- * to the file.
- */
- Status goingToDelete(const BSONObj& o);
-
- /**
- * A path object describing the directory containing the file with deleted documents.
- */
- const auto& root() const& {
- return _root;
- }
- void root() && = delete;
-
- /**
- * A path object describing the actual file containing BSON documents.
- */
- const auto& file() const& {
- return _file;
- }
- void file() && = delete;
-
- private:
- boost::filesystem::path _root;
- boost::filesystem::path _file;
- std::unique_ptr<DataProtector> _protector;
- std::unique_ptr<std::ostream> _out;
- };
};
} // namespace mongo
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index ae8989e9702..1221abc160d 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -522,6 +522,7 @@ env.Library(
'$BUILD_DIR/mongo/db/catalog/catalog_helpers',
'$BUILD_DIR/mongo/db/catalog/database_holder',
'$BUILD_DIR/mongo/db/s/sharding_runtime_d',
+ '$BUILD_DIR/mongo/db/storage/remove_saver',
'$BUILD_DIR/mongo/util/fail_point',
'$BUILD_DIR/mongo/db/dbhelpers',
'$BUILD_DIR/mongo/db/query_exec',
diff --git a/src/mongo/db/repl/rollback_impl.cpp b/src/mongo/db/repl/rollback_impl.cpp
index 511660973a0..7c7bc94cf38 100644
--- a/src/mongo/db/repl/rollback_impl.cpp
+++ b/src/mongo/db/repl/rollback_impl.cpp
@@ -41,7 +41,6 @@
#include "mongo/db/commands.h"
#include "mongo/db/concurrency/d_concurrency.h"
#include "mongo/db/db_raii.h"
-#include "mongo/db/dbhelpers.h"
#include "mongo/db/kill_sessions_local.h"
#include "mongo/db/logical_time_validator.h"
#include "mongo/db/operation_context.h"
@@ -57,6 +56,7 @@
#include "mongo/db/server_parameters.h"
#include "mongo/db/server_recovery.h"
#include "mongo/db/server_transactions_metrics.h"
+#include "mongo/db/storage/remove_saver.h"
#include "mongo/s/catalog/type_config_version.h"
#include "mongo/util/log.h"
#include "mongo/util/scopeguard.h"
@@ -973,7 +973,7 @@ void RollbackImpl::_writeRollbackFileForNamespace(OperationContext* opCtx,
UUID uuid,
NamespaceString nss,
const SimpleBSONObjUnorderedSet& idSet) {
- Helpers::RemoveSaver removeSaver(kRollbackRemoveSaverType, nss.ns(), kRollbackRemoveSaverWhy);
+ RemoveSaver removeSaver(kRollbackRemoveSaverType, nss.ns(), kRollbackRemoveSaverWhy);
log() << "Preparing to write deleted documents to a rollback file for collection " << nss.ns()
<< " with uuid " << uuid.toString() << " to " << removeSaver.file().generic_string();
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index eae4a7b87cb..6896955ec78 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -72,6 +72,7 @@
#include "mongo/db/repl/rslog.h"
#include "mongo/db/s/shard_identity_rollback_notifier.h"
#include "mongo/db/session_catalog_mongod.h"
+#include "mongo/db/storage/remove_saver.h"
#include "mongo/db/transaction_participant.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
@@ -747,7 +748,7 @@ void dropCollection(OperationContext* opCtx,
Collection* collection,
Database* db) {
if (RollbackImpl::shouldCreateDataFiles()) {
- Helpers::RemoveSaver removeSaver("rollback", "", nss.ns());
+ RemoveSaver removeSaver("rollback", "", nss.ns());
// Performs a collection scan and writes all documents in the collection to disk
// in order to keep an archive of items that were rolled back.
@@ -1259,13 +1260,13 @@ void rollback_internal::syncFixUp(OperationContext* opCtx,
// while rolling back createCollection operations.
const auto& uuid = nsAndGoodVersionsByDocID.first;
- unique_ptr<Helpers::RemoveSaver> removeSaver;
+ unique_ptr<RemoveSaver> removeSaver;
invariant(!fixUpInfo.collectionsToDrop.count(uuid));
NamespaceString nss = catalog.lookupNSSByUUID(uuid);
if (RollbackImpl::shouldCreateDataFiles()) {
- removeSaver = std::make_unique<Helpers::RemoveSaver>("rollback", "", nss.ns());
+ removeSaver = std::make_unique<RemoveSaver>("rollback", "", nss.ns());
}
const auto& goodVersionsByDocID = nsAndGoodVersionsByDocID.second;
diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript
index e348ad6e4ad..128f6985853 100644
--- a/src/mongo/db/s/SConscript
+++ b/src/mongo/db/s/SConscript
@@ -75,6 +75,7 @@ env.Library(
'$BUILD_DIR/mongo/db/ops/write_ops_exec',
'$BUILD_DIR/mongo/db/repl/oplog',
'$BUILD_DIR/mongo/db/repl/repl_coordinator_interface',
+ '$BUILD_DIR/mongo/db/storage/remove_saver',
'$BUILD_DIR/mongo/db/rw_concern_d',
'$BUILD_DIR/mongo/s/client/shard_local',
'$BUILD_DIR/mongo/s/sharding_initialization',
diff --git a/src/mongo/db/s/collection_range_deleter.cpp b/src/mongo/db/s/collection_range_deleter.cpp
index 8856d53d8a2..ead8f441758 100644
--- a/src/mongo/db/s/collection_range_deleter.cpp
+++ b/src/mongo/db/s/collection_range_deleter.cpp
@@ -55,6 +55,7 @@
#include "mongo/db/s/sharding_statistics.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/service_context.h"
+#include "mongo/db/storage/remove_saver.h"
#include "mongo/db/write_concern.h"
#include "mongo/executor/task_executor.h"
#include "mongo/util/log.h"
@@ -386,7 +387,7 @@ StatusWith<int> CollectionRangeDeleter::_doDeletion(OperationContext* opCtx,
return {ErrorCodes::InternalError, msg};
}
- boost::optional<Helpers::RemoveSaver> saver;
+ boost::optional<RemoveSaver> saver;
if (serverGlobalParams.moveParanoia) {
saver.emplace("moveChunk", nss.ns(), "cleaning");
}
diff --git a/src/mongo/db/s/migration_destination_manager.cpp b/src/mongo/db/s/migration_destination_manager.cpp
index cacf25a608c..69f62df8751 100644
--- a/src/mongo/db/s/migration_destination_manager.cpp
+++ b/src/mongo/db/s/migration_destination_manager.cpp
@@ -57,6 +57,7 @@
#include "mongo/db/s/start_chunk_clone_request.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/service_context.h"
+#include "mongo/db/storage/remove_saver.h"
#include "mongo/s/catalog/type_chunk.h"
#include "mongo/s/client/shard_registry.h"
#include "mongo/s/grid.h"
@@ -1040,7 +1041,7 @@ bool MigrationDestinationManager::_applyMigrateOp(OperationContext* opCtx,
// Deleted documents
if (xfer["deleted"].isABSONObj()) {
- boost::optional<Helpers::RemoveSaver> rs;
+ boost::optional<RemoveSaver> rs;
if (serverGlobalParams.moveParanoia) {
rs.emplace("moveChunk", _nss.ns(), "removedDuring");
}
diff --git a/src/mongo/db/storage/SConscript b/src/mongo/db/storage/SConscript
index 8cd5b9cb4f4..e82a86d5cdb 100644
--- a/src/mongo/db/storage/SConscript
+++ b/src/mongo/db/storage/SConscript
@@ -341,3 +341,15 @@ env.Benchmark(
'$BUILD_DIR/mongo/base',
],
)
+
+env.Library(
+ target='remove_saver',
+ source=[
+ 'remove_saver.cpp',
+ ],
+ LIBDEPS=[
+ 'encryption_hooks',
+ 'storage_options',
+ '$BUILD_DIR/mongo/base',
+ ]
+)
diff --git a/src/mongo/db/storage/remove_saver.cpp b/src/mongo/db/storage/remove_saver.cpp
new file mode 100644
index 00000000000..505e9083b75
--- /dev/null
+++ b/src/mongo/db/storage/remove_saver.cpp
@@ -0,0 +1,179 @@
+
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/storage/remove_saver.h"
+
+#include <boost/filesystem/operations.hpp>
+#include <fstream>
+
+#include "mongo/db/service_context.h"
+#include "mongo/db/storage/encryption_hooks.h"
+#include "mongo/db/storage/storage_options.h"
+#include "mongo/util/errno_util.h"
+#include "mongo/util/log.h"
+
+
+using std::ios_base;
+using std::ofstream;
+using std::stringstream;
+using std::string;
+
+namespace mongo {
+
+RemoveSaver::RemoveSaver(const string& a, const string& b, const string& why) {
+ static int NUM = 0;
+
+ _root = storageGlobalParams.dbpath;
+ if (a.size())
+ _root /= a;
+ if (b.size())
+ _root /= b;
+ verify(a.size() || b.size());
+
+ _file = _root;
+
+ stringstream ss;
+ ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson";
+ _file /= ss.str();
+
+ auto encryptionHooks = EncryptionHooks::get(getGlobalServiceContext());
+ if (encryptionHooks->enabled()) {
+ _protector = encryptionHooks->getDataProtector();
+ _file += encryptionHooks->getProtectedPathSuffix();
+ }
+}
+
+RemoveSaver::~RemoveSaver() {
+ if (_protector && _out) {
+ auto encryptionHooks = EncryptionHooks::get(getGlobalServiceContext());
+ invariant(encryptionHooks->enabled());
+
+ size_t protectedSizeMax = encryptionHooks->additionalBytesForProtectedBuffer();
+ std::unique_ptr<uint8_t[]> protectedBuffer(new uint8_t[protectedSizeMax]);
+
+ size_t resultLen;
+ Status status = _protector->finalize(protectedBuffer.get(), protectedSizeMax, &resultLen);
+ if (!status.isOK()) {
+ severe() << "Unable to finalize DataProtector while closing RemoveSaver: "
+ << redact(status);
+ fassertFailed(34350);
+ }
+
+ _out->write(reinterpret_cast<const char*>(protectedBuffer.get()), resultLen);
+ if (_out->fail()) {
+ severe() << "Couldn't write finalized DataProtector data to: " << _file.string()
+ << " for remove saving: " << redact(errnoWithDescription());
+ fassertFailed(34351);
+ }
+
+ protectedBuffer.reset(new uint8_t[protectedSizeMax]);
+ status = _protector->finalizeTag(protectedBuffer.get(), protectedSizeMax, &resultLen);
+ if (!status.isOK()) {
+ severe() << "Unable to get finalizeTag from DataProtector while closing RemoveSaver: "
+ << redact(status);
+ fassertFailed(34352);
+ }
+
+ if (resultLen != _protector->getNumberOfBytesReservedForTag()) {
+ severe() << "Attempted to write tag of size " << resultLen
+ << " when DataProtector only reserved "
+ << _protector->getNumberOfBytesReservedForTag() << " bytes";
+ fassertFailed(34353);
+ }
+
+ _out->seekp(0);
+ _out->write(reinterpret_cast<const char*>(protectedBuffer.get()), resultLen);
+
+ if (_out->fail()) {
+ severe() << "Couldn't write finalizeTag from DataProtector to: " << _file.string()
+ << " for remove saving: " << redact(errnoWithDescription());
+ fassertFailed(34354);
+ }
+ }
+}
+
+Status RemoveSaver::goingToDelete(const BSONObj& o) {
+ if (!_out) {
+ // We don't expect to ever pass "" to create_directories below, but catch
+ // this anyway as per SERVER-26412.
+ invariant(!_root.empty());
+ boost::filesystem::create_directories(_root);
+ _out.reset(new ofstream(_file.string().c_str(), ios_base::out | ios_base::binary));
+
+ if (_out->fail()) {
+ string msg = str::stream() << "couldn't create file: " << _file.string()
+ << " for remove saving: " << redact(errnoWithDescription());
+ error() << msg;
+ _out.reset();
+ _out = 0;
+ return Status(ErrorCodes::FileNotOpen, msg);
+ }
+ }
+
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(o.objdata());
+ size_t dataSize = o.objsize();
+
+ std::unique_ptr<uint8_t[]> protectedBuffer;
+ if (_protector) {
+ auto encryptionHooks = EncryptionHooks::get(getGlobalServiceContext());
+ invariant(encryptionHooks->enabled());
+
+ size_t protectedSizeMax = dataSize + encryptionHooks->additionalBytesForProtectedBuffer();
+ protectedBuffer.reset(new uint8_t[protectedSizeMax]);
+
+ size_t resultLen;
+ Status status = _protector->protect(
+ data, dataSize, protectedBuffer.get(), protectedSizeMax, &resultLen);
+
+ if (!status.isOK()) {
+ return status;
+ }
+
+ data = protectedBuffer.get();
+ dataSize = resultLen;
+ }
+
+ _out->write(reinterpret_cast<const char*>(data), dataSize);
+
+ if (_out->fail()) {
+ string msg = str::stream() << "couldn't write document to file: " << _file.string()
+ << " for remove saving: " << redact(errnoWithDescription());
+ error() << msg;
+ return Status(ErrorCodes::OperationFailed, msg);
+ }
+
+ return Status::OK();
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/storage/remove_saver.h b/src/mongo/db/storage/remove_saver.h
new file mode 100644
index 00000000000..78057622444
--- /dev/null
+++ b/src/mongo/db/storage/remove_saver.h
@@ -0,0 +1,85 @@
+
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <boost/filesystem/path.hpp>
+#include <memory>
+#include <ostream>
+#include <string>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/db/storage/data_protector.h"
+
+namespace mongo {
+
+/**
+ * for saving deleted bson objects to a flat file
+ */
+class RemoveSaver {
+ MONGO_DISALLOW_COPYING(RemoveSaver);
+
+public:
+ RemoveSaver(const std::string& type, const std::string& ns, const std::string& why);
+ ~RemoveSaver();
+
+ /**
+ * Writes document to file. File is created lazily before writing the first document.
+ * Returns error status if the file could not be created or if there were errors writing
+ * to the file.
+ */
+ Status goingToDelete(const BSONObj& o);
+
+ /**
+ * A path object describing the directory containing the file with deleted documents.
+ */
+ const auto& root() const& {
+ return _root;
+ }
+ void root() && = delete;
+
+ /**
+ * A path object describing the actual file containing BSON documents.
+ */
+ const auto& file() const& {
+ return _file;
+ }
+ void file() && = delete;
+
+private:
+ boost::filesystem::path _root;
+ boost::filesystem::path _file;
+ std::unique_ptr<DataProtector> _protector;
+ std::unique_ptr<std::ostream> _out;
+};
+
+} // namespace mongo