1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand
32 
33 #include "mongo/platform/basic.h"
34 
35 #include <array>
36 #include <time.h>
37 
38 #include "mongo/base/disallow_copying.h"
39 #include "mongo/base/simple_string_data_comparator.h"
40 #include "mongo/base/status.h"
41 #include "mongo/base/status_with.h"
42 #include "mongo/bson/simple_bsonobj_comparator.h"
43 #include "mongo/bson/util/bson_extract.h"
44 #include "mongo/bson/util/builder.h"
45 #include "mongo/db/audit.h"
46 #include "mongo/db/auth/action_set.h"
47 #include "mongo/db/auth/action_type.h"
48 #include "mongo/db/auth/authorization_manager.h"
49 #include "mongo/db/auth/authorization_session.h"
50 #include "mongo/db/auth/privilege.h"
51 #include "mongo/db/auth/user_management_commands_parser.h"
52 #include "mongo/db/auth/user_name.h"
53 #include "mongo/db/background.h"
54 #include "mongo/db/catalog/coll_mod.h"
55 #include "mongo/db/catalog/collection.h"
56 #include "mongo/db/catalog/collection_catalog_entry.h"
57 #include "mongo/db/catalog/create_collection.h"
58 #include "mongo/db/catalog/drop_collection.h"
59 #include "mongo/db/catalog/drop_database.h"
60 #include "mongo/db/catalog/index_key_validate.h"
61 #include "mongo/db/clientcursor.h"
62 #include "mongo/db/commands.h"
63 #include "mongo/db/commands/server_status.h"
64 #include "mongo/db/commands/shutdown.h"
65 #include "mongo/db/concurrency/write_conflict_exception.h"
66 #include "mongo/db/db_raii.h"
67 #include "mongo/db/dbdirectclient.h"
68 #include "mongo/db/dbhelpers.h"
69 #include "mongo/db/exec/working_set_common.h"
70 #include "mongo/db/index/index_access_method.h"
71 #include "mongo/db/index/index_descriptor.h"
72 #include "mongo/db/index_builder.h"
73 #include "mongo/db/introspect.h"
74 #include "mongo/db/jsobj.h"
75 #include "mongo/db/json.h"
76 #include "mongo/db/keypattern.h"
77 #include "mongo/db/lasterror.h"
78 #include "mongo/db/namespace_string.h"
79 #include "mongo/db/op_observer.h"
80 #include "mongo/db/ops/insert.h"
81 #include "mongo/db/query/collation/collator_factory_interface.h"
82 #include "mongo/db/query/get_executor.h"
83 #include "mongo/db/query/internal_plans.h"
84 #include "mongo/db/query/query_planner.h"
85 #include "mongo/db/read_concern.h"
86 #include "mongo/db/repair_database.h"
87 #include "mongo/db/repl/optime.h"
88 #include "mongo/db/repl/read_concern_args.h"
89 #include "mongo/db/repl/repl_client_info.h"
90 #include "mongo/db/repl/repl_settings.h"
91 #include "mongo/db/repl/replication_coordinator_global.h"
92 #include "mongo/db/server_parameters.h"
93 #include "mongo/db/stats/storage_stats.h"
94 #include "mongo/db/write_concern.h"
95 #include "mongo/s/chunk_version.h"
96 #include "mongo/s/grid.h"
97 #include "mongo/s/stale_exception.h"
98 #include "mongo/scripting/engine.h"
99 #include "mongo/util/fail_point_service.h"
100 #include "mongo/util/log.h"
101 #include "mongo/util/md5.hpp"
102 #include "mongo/util/scopeguard.h"
103 
104 namespace mongo {
105 
106 using std::ostringstream;
107 using std::string;
108 using std::stringstream;
109 using std::unique_ptr;
110 
111 
112 class CmdShutdownMongoD : public CmdShutdown {
113 public:
help(stringstream & help) const114     virtual void help(stringstream& help) const {
115         help << "shutdown the database.  must be ran against admin db and "
116              << "either (1) ran from localhost or (2) authenticated. If "
117              << "this is a primary in a replica set and there is no member "
118              << "within 10 seconds of its optime, it will not shutdown "
119              << "without force : true.  You can also specify timeoutSecs : "
120              << "N to wait N seconds for other members to catch up.";
121     }
122 
run(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,BSONObjBuilder & result)123     virtual bool run(OperationContext* opCtx,
124                      const string& dbname,
125                      const BSONObj& cmdObj,
126                      BSONObjBuilder& result) {
127         bool force = cmdObj.hasField("force") && cmdObj["force"].trueValue();
128 
129         long long timeoutSecs = 10;
130         if (cmdObj.hasField("timeoutSecs")) {
131             timeoutSecs = cmdObj["timeoutSecs"].numberLong();
132         }
133 
134         Status status = repl::getGlobalReplicationCoordinator()->stepDown(
135             opCtx, force, Seconds(timeoutSecs), Seconds(120));
136         if (!status.isOK() && status.code() != ErrorCodes::NotMaster) {  // ignore not master
137             return appendCommandStatus(result, status);
138         }
139 
140         // Never returns
141         shutdownHelper();
142         return true;
143     }
144 
145 } cmdShutdownMongoD;
146 
147 class CmdDropDatabase : public BasicCommand {
148 public:
help(stringstream & help) const149     virtual void help(stringstream& help) const {
150         help << "drop (delete) this database";
151     }
slaveOk() const152     virtual bool slaveOk() const {
153         return false;
154     }
155 
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)156     virtual void addRequiredPrivileges(const std::string& dbname,
157                                        const BSONObj& cmdObj,
158                                        std::vector<Privilege>* out) {
159         ActionSet actions;
160         actions.addAction(ActionType::dropDatabase);
161         out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
162     }
163 
164 
supportsWriteConcern(const BSONObj & cmd) const165     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
166         return true;
167     }
168 
CmdDropDatabase()169     CmdDropDatabase() : BasicCommand("dropDatabase") {}
170 
run(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,BSONObjBuilder & result)171     bool run(OperationContext* opCtx,
172              const string& dbname,
173              const BSONObj& cmdObj,
174              BSONObjBuilder& result) {
175         // disallow dropping the config database
176         if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer &&
177             (dbname == NamespaceString::kConfigDb)) {
178             return appendCommandStatus(result,
179                                        Status(ErrorCodes::IllegalOperation,
180                                               "Cannot drop 'config' database if mongod started "
181                                               "with --configsvr"));
182         }
183 
184         if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() !=
185              repl::ReplicationCoordinator::modeNone) &&
186             (dbname == NamespaceString::kLocalDb)) {
187             return appendCommandStatus(
188                 result,
189                 Status(ErrorCodes::IllegalOperation,
190                        str::stream() << "Cannot drop '" << dbname
191                                      << "' database while replication is active"));
192         }
193         BSONElement e = cmdObj.firstElement();
194         int p = (int)e.number();
195         if (p != 1) {
196             return appendCommandStatus(
197                 result, Status(ErrorCodes::IllegalOperation, "have to pass 1 as db parameter"));
198         }
199 
200         Status status = dropDatabase(opCtx, dbname);
201         if (status == ErrorCodes::NamespaceNotFound) {
202             return appendCommandStatus(result, Status::OK());
203         }
204         if (status.isOK()) {
205             result.append("dropped", dbname);
206         }
207         return appendCommandStatus(result, status);
208     }
209 
210 } cmdDropDatabase;
211 
212 class CmdRepairDatabase : public ErrmsgCommandDeprecated {
213 public:
slaveOk() const214     virtual bool slaveOk() const {
215         return true;
216     }
maintenanceMode() const217     virtual bool maintenanceMode() const {
218         return true;
219     }
help(stringstream & help) const220     virtual void help(stringstream& help) const {
221         help << "repair database.  also compacts. note: slow.";
222     }
223 
224 
supportsWriteConcern(const BSONObj & cmd) const225     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
226         return false;
227     }
228 
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)229     virtual void addRequiredPrivileges(const std::string& dbname,
230                                        const BSONObj& cmdObj,
231                                        std::vector<Privilege>* out) {
232         ActionSet actions;
233         actions.addAction(ActionType::repairDatabase);
234         out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
235     }
236 
CmdRepairDatabase()237     CmdRepairDatabase() : ErrmsgCommandDeprecated("repairDatabase") {}
238 
errmsgRun(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,string & errmsg,BSONObjBuilder & result)239     bool errmsgRun(OperationContext* opCtx,
240                    const string& dbname,
241                    const BSONObj& cmdObj,
242                    string& errmsg,
243                    BSONObjBuilder& result) {
244         BSONElement e = cmdObj.firstElement();
245         if (e.numberInt() != 1) {
246             errmsg = "bad option";
247             return false;
248         }
249 
250         // Closing a database requires a global lock.
251         Lock::GlobalWrite lk(opCtx);
252         auto db = dbHolder().get(opCtx, dbname);
253         if (db) {
254             if (db->isDropPending(opCtx)) {
255                 return appendCommandStatus(
256                     result,
257                     Status(ErrorCodes::DatabaseDropPending,
258                            str::stream() << "Cannot repair database " << dbname
259                                          << " since it is pending being dropped."));
260             }
261         } else {
262             // If the name doesn't make an exact match, check for a case insensitive match.
263             std::set<std::string> otherCasing = dbHolder().getNamesWithConflictingCasing(dbname);
264             if (otherCasing.empty()) {
265                 // Database doesn't exist. Treat this as a success (historical behavior).
266                 return true;
267             }
268 
269             // Database exists with a differing case. Treat this as an error. Report the casing
270             // conflict.
271             errmsg = str::stream() << "Database exists with a different case. Given: `" << dbname
272                                    << "` Found: `" << *otherCasing.begin() << "`";
273             return false;
274         }
275 
276         // TODO (Kal): OldClientContext legacy, needs to be removed
277         {
278             CurOp::get(opCtx)->ensureStarted();
279             stdx::lock_guard<Client> lk(*opCtx->getClient());
280             CurOp::get(opCtx)->setNS_inlock(dbname);
281         }
282 
283         log() << "repairDatabase " << dbname;
284         BackgroundOperation::assertNoBgOpInProgForDb(dbname);
285 
286         e = cmdObj.getField("preserveClonedFilesOnFailure");
287         bool preserveClonedFilesOnFailure = e.isBoolean() && e.boolean();
288         e = cmdObj.getField("backupOriginalFiles");
289         bool backupOriginalFiles = e.isBoolean() && e.boolean();
290 
291         StorageEngine* engine = getGlobalServiceContext()->getGlobalStorageEngine();
292         repl::UnreplicatedWritesBlock uwb(opCtx);
293         Status status = repairDatabase(
294             opCtx, engine, dbname, preserveClonedFilesOnFailure, backupOriginalFiles);
295 
296         // Open database before returning
297         dbHolder().openDb(opCtx, dbname);
298         return appendCommandStatus(result, status);
299     }
300 } cmdRepairDatabase;
301 
302 /* set db profiling level
303    todo: how do we handle profiling information put in the db with replication?
304          sensibly or not?
305 */
306 class CmdProfile : public ErrmsgCommandDeprecated {
307 public:
slaveOk() const308     virtual bool slaveOk() const {
309         return true;
310     }
311 
help(stringstream & help) const312     virtual void help(stringstream& help) const {
313         help << "enable or disable performance profiling\n";
314         help << "{ profile : <n> }\n";
315         help << "0=off 1=log slow ops 2=log all\n";
316         help << "-1 to get current values\n";
317         help << "http://docs.mongodb.org/manual/reference/command/profile/#dbcmd.profile";
318     }
319 
320 
supportsWriteConcern(const BSONObj & cmd) const321     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
322         return false;
323     }
324 
checkAuthForCommand(Client * client,const std::string & dbname,const BSONObj & cmdObj)325     virtual Status checkAuthForCommand(Client* client,
326                                        const std::string& dbname,
327                                        const BSONObj& cmdObj) {
328         AuthorizationSession* authzSession = AuthorizationSession::get(client);
329 
330         if (cmdObj.firstElement().numberInt() == -1 && !cmdObj.hasField("slowms") &&
331             !cmdObj.hasField("sampleRate")) {
332             // If you just want to get the current profiling level you can do so with just
333             // read access to system.profile, even if you can't change the profiling level.
334             if (authzSession->isAuthorizedForActionsOnResource(
335                     ResourcePattern::forExactNamespace(NamespaceString(dbname, "system.profile")),
336                     ActionType::find)) {
337                 return Status::OK();
338             }
339         }
340 
341         if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname),
342                                                            ActionType::enableProfiler)) {
343             return Status::OK();
344         }
345 
346         return Status(ErrorCodes::Unauthorized, "unauthorized");
347     }
348 
CmdProfile()349     CmdProfile() : ErrmsgCommandDeprecated("profile") {}
350 
errmsgRun(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,string & errmsg,BSONObjBuilder & result)351     bool errmsgRun(OperationContext* opCtx,
352                    const string& dbname,
353                    const BSONObj& cmdObj,
354                    string& errmsg,
355                    BSONObjBuilder& result) {
356         BSONElement firstElement = cmdObj.firstElement();
357         int profilingLevel = firstElement.numberInt();
358 
359         // If profilingLevel is 0, 1, or 2, needs to be locked exclusively,
360         // because creates the system.profile collection in the local database.
361 
362         const bool readOnly = (profilingLevel < 0 || profilingLevel > 2);
363         const LockMode dbMode = readOnly ? MODE_S : MODE_X;
364 
365         Status status = Status::OK();
366 
367         AutoGetDb ctx(opCtx, dbname, dbMode);
368         Database* db = ctx.getDb();
369 
370         result.append("was", db ? db->getProfilingLevel() : serverGlobalParams.defaultProfile);
371         result.append("slowms", serverGlobalParams.slowMS);
372         result.append("sampleRate", serverGlobalParams.sampleRate);
373 
374         if (!serverGlobalParams.featureCompatibility.isVersionInitialized()) {
375             errmsg =
376                 "profiling level cannot be set when featureCompatibilityVersion is uninitialized";
377             return false;
378         }
379 
380         if (!readOnly) {
381             if (!db) {
382                 // When setting the profiling level, create the database if it didn't already exist.
383                 // When just reading the profiling level, we do not create the database.
384                 db = dbHolder().openDb(opCtx, dbname);
385             }
386             status = db->setProfilingLevel(opCtx, profilingLevel);
387         }
388 
389         const BSONElement slow = cmdObj["slowms"];
390         if (slow.isNumber()) {
391             serverGlobalParams.slowMS = slow.numberInt();
392         }
393 
394         double newSampleRate;
395         uassertStatusOK(bsonExtractDoubleFieldWithDefault(
396             cmdObj, "sampleRate"_sd, serverGlobalParams.sampleRate, &newSampleRate));
397         uassert(ErrorCodes::BadValue,
398                 "sampleRate must be between 0.0 and 1.0 inclusive",
399                 newSampleRate >= 0.0 && newSampleRate <= 1.0);
400         serverGlobalParams.sampleRate = newSampleRate;
401 
402         if (!status.isOK()) {
403             errmsg = status.reason();
404         }
405 
406         return status.isOK();
407     }
408 
409 } cmdProfile;
410 
411 /* drop collection */
412 class CmdDrop : public ErrmsgCommandDeprecated {
413 public:
CmdDrop()414     CmdDrop() : ErrmsgCommandDeprecated("drop") {}
slaveOk() const415     virtual bool slaveOk() const {
416         return false;
417     }
adminOnly() const418     virtual bool adminOnly() const {
419         return false;
420     }
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)421     virtual void addRequiredPrivileges(const std::string& dbname,
422                                        const BSONObj& cmdObj,
423                                        std::vector<Privilege>* out) {
424         ActionSet actions;
425         actions.addAction(ActionType::dropCollection);
426         out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
427     }
help(stringstream & help) const428     virtual void help(stringstream& help) const {
429         help << "drop a collection\n{drop : <collectionName>}";
430     }
431 
432 
supportsWriteConcern(const BSONObj & cmd) const433     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
434         return true;
435     }
436 
errmsgRun(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,string & errmsg,BSONObjBuilder & result)437     virtual bool errmsgRun(OperationContext* opCtx,
438                            const string& dbname,
439                            const BSONObj& cmdObj,
440                            string& errmsg,
441                            BSONObjBuilder& result) {
442         const NamespaceString nsToDrop = parseNsCollectionRequired(dbname, cmdObj);
443 
444         if (NamespaceString::virtualized(nsToDrop.ns())) {
445             errmsg = "can't drop a virtual collection";
446             return false;
447         }
448 
449         if ((repl::getGlobalReplicationCoordinator()->getReplicationMode() !=
450              repl::ReplicationCoordinator::modeNone) &&
451             nsToDrop.isOplog()) {
452             errmsg = "can't drop live oplog while replicating";
453             return false;
454         }
455 
456         return appendCommandStatus(
457             result,
458             dropCollection(opCtx,
459                            nsToDrop,
460                            result,
461                            {},
462                            DropCollectionSystemCollectionMode::kDisallowSystemCollectionDrops));
463     }
464 
465 } cmdDrop;
466 
467 /* create collection */
468 class CmdCreate : public BasicCommand {
469 public:
CmdCreate()470     CmdCreate() : BasicCommand("create") {}
slaveOk() const471     virtual bool slaveOk() const {
472         return false;
473     }
adminOnly() const474     virtual bool adminOnly() const {
475         return false;
476     }
477 
478 
supportsWriteConcern(const BSONObj & cmd) const479     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
480         return true;
481     }
482 
help(stringstream & help) const483     virtual void help(stringstream& help) const {
484         help << "create a collection explicitly\n"
485                 "{ create: <ns>[, capped: <bool>, size: <collSizeInBytes>, max: <nDocs>] }";
486     }
checkAuthForCommand(Client * client,const std::string & dbname,const BSONObj & cmdObj)487     virtual Status checkAuthForCommand(Client* client,
488                                        const std::string& dbname,
489                                        const BSONObj& cmdObj) {
490         const NamespaceString nss(parseNs(dbname, cmdObj));
491         return AuthorizationSession::get(client)->checkAuthForCreate(nss, cmdObj, false);
492     }
493 
run(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,BSONObjBuilder & result)494     virtual bool run(OperationContext* opCtx,
495                      const string& dbname,
496                      const BSONObj& cmdObj,
497                      BSONObjBuilder& result) {
498         const NamespaceString ns(parseNsCollectionRequired(dbname, cmdObj));
499 
500         if (cmdObj.hasField("autoIndexId")) {
501             const char* deprecationWarning =
502                 "the autoIndexId option is deprecated and will be removed in a future release";
503             warning() << deprecationWarning;
504             result.append("note", deprecationWarning);
505         }
506 
507         // Validate _id index spec and fill in missing fields.
508         if (auto idIndexElem = cmdObj["idIndex"]) {
509             if (cmdObj["viewOn"]) {
510                 return appendCommandStatus(
511                     result,
512                     {ErrorCodes::InvalidOptions,
513                      str::stream() << "'idIndex' is not allowed with 'viewOn': " << idIndexElem});
514             }
515             if (cmdObj["autoIndexId"]) {
516                 return appendCommandStatus(result,
517                                            {ErrorCodes::InvalidOptions,
518                                             str::stream()
519                                                 << "'idIndex' is not allowed with 'autoIndexId': "
520                                                 << idIndexElem});
521             }
522 
523             if (idIndexElem.type() != BSONType::Object) {
524                 return appendCommandStatus(
525                     result,
526                     {ErrorCodes::TypeMismatch,
527                      str::stream() << "'idIndex' has to be a document: " << idIndexElem});
528             }
529 
530             auto idIndexSpec = idIndexElem.Obj();
531 
532             // Perform index spec validation.
533             idIndexSpec = uassertStatusOK(index_key_validate::validateIndexSpec(
534                 opCtx, idIndexSpec, ns, serverGlobalParams.featureCompatibility));
535             uassertStatusOK(index_key_validate::validateIdIndexSpec(idIndexSpec));
536 
537             // Validate or fill in _id index collation.
538             std::unique_ptr<CollatorInterface> defaultCollator;
539             if (auto collationElem = cmdObj["collation"]) {
540                 if (collationElem.type() != BSONType::Object) {
541                     return appendCommandStatus(
542                         result,
543                         {ErrorCodes::TypeMismatch,
544                          str::stream() << "'collation' has to be a document: " << collationElem});
545                 }
546                 auto collatorStatus = CollatorFactoryInterface::get(opCtx->getServiceContext())
547                                           ->makeFromBSON(collationElem.Obj());
548                 if (!collatorStatus.isOK()) {
549                     return appendCommandStatus(result, collatorStatus.getStatus());
550                 }
551                 defaultCollator = std::move(collatorStatus.getValue());
552             }
553             idIndexSpec = uassertStatusOK(index_key_validate::validateIndexSpecCollation(
554                 opCtx, idIndexSpec, defaultCollator.get()));
555             std::unique_ptr<CollatorInterface> idIndexCollator;
556             if (auto collationElem = idIndexSpec["collation"]) {
557                 auto collatorStatus = CollatorFactoryInterface::get(opCtx->getServiceContext())
558                                           ->makeFromBSON(collationElem.Obj());
559                 // validateIndexSpecCollation() should have checked that the _id index collation
560                 // spec is valid.
561                 invariant(collatorStatus.isOK());
562                 idIndexCollator = std::move(collatorStatus.getValue());
563             }
564             if (!CollatorInterface::collatorsMatch(defaultCollator.get(), idIndexCollator.get())) {
565                 return appendCommandStatus(
566                     result,
567                     {ErrorCodes::BadValue,
568                      "'idIndex' must have the same collation as the collection."});
569             }
570 
571             // Remove "idIndex" field from command.
572             auto resolvedCmdObj = cmdObj.removeField("idIndex");
573 
574             return appendCommandStatus(
575                 result, createCollection(opCtx, dbname, resolvedCmdObj, idIndexSpec));
576         }
577 
578         BSONObj idIndexSpec;
579         return appendCommandStatus(result, createCollection(opCtx, dbname, cmdObj, idIndexSpec));
580     }
581 } cmdCreate;
582 
583 
584 class CmdFileMD5 : public BasicCommand {
585 public:
CmdFileMD5()586     CmdFileMD5() : BasicCommand("filemd5") {}
587 
slaveOk() const588     virtual bool slaveOk() const {
589         return true;
590     }
591 
help(stringstream & help) const592     virtual void help(stringstream& help) const {
593         help << " example: { filemd5 : ObjectId(aaaaaaa) , root : \"fs\" }";
594     }
595 
596 
supportsWriteConcern(const BSONObj & cmd) const597     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
598         return false;
599     }
600 
parseNs(const std::string & dbname,const BSONObj & cmdObj) const601     virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const {
602         std::string collectionName;
603         if (const auto rootElt = cmdObj["root"]) {
604             uassert(ErrorCodes::InvalidNamespace,
605                     "'root' must be of type String",
606                     rootElt.type() == BSONType::String);
607             collectionName = rootElt.str();
608         }
609         if (collectionName.empty())
610             collectionName = "fs";
611         collectionName += ".chunks";
612         return NamespaceString(dbname, collectionName).ns();
613     }
614 
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)615     virtual void addRequiredPrivileges(const std::string& dbname,
616                                        const BSONObj& cmdObj,
617                                        std::vector<Privilege>* out) {
618         out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), ActionType::find));
619     }
620 
run(OperationContext * opCtx,const string & dbname,const BSONObj & jsobj,BSONObjBuilder & result)621     bool run(OperationContext* opCtx,
622              const string& dbname,
623              const BSONObj& jsobj,
624              BSONObjBuilder& result) {
625         const NamespaceString nss(parseNs(dbname, jsobj));
626 
627         md5digest d;
628         md5_state_t st;
629         md5_init(&st);
630 
631         int n = 0;
632 
633         bool partialOk = jsobj["partialOk"].trueValue();
634         if (partialOk) {
635             // WARNING: This code depends on the binary layout of md5_state. It will not be
636             // compatible with different md5 libraries or work correctly in an environment with
637             // mongod's of different endians. It is ok for mongos to be a different endian since
638             // it just passes the buffer through to another mongod.
639             BSONElement stateElem = jsobj["md5state"];
640             if (!stateElem.eoo()) {
641                 int len;
642                 const char* data = stateElem.binDataClean(len);
643                 massert(16247, "md5 state not correct size", len == sizeof(st));
644                 memcpy(&st, data, sizeof(st));
645             }
646             n = jsobj["startAt"].numberInt();
647         }
648 
649         BSONObj query = BSON("files_id" << jsobj["filemd5"] << "n" << GTE << n);
650         BSONObj sort = BSON("files_id" << 1 << "n" << 1);
651 
652         return writeConflictRetry(opCtx, "filemd5", dbname, [&] {
653             auto qr = stdx::make_unique<QueryRequest>(nss);
654             qr->setFilter(query);
655             qr->setSort(sort);
656 
657             auto statusWithCQ = CanonicalQuery::canonicalize(opCtx, std::move(qr));
658             if (!statusWithCQ.isOK()) {
659                 uasserted(17240, "Can't canonicalize query " + query.toString());
660                 return false;
661             }
662             unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());
663 
664             // Check shard version at startup.
665             // This will throw before we've done any work if shard version is outdated
666             // We drop and re-acquire these locks every document because md5'ing is expensive
667             unique_ptr<AutoGetCollectionForReadCommand> ctx(
668                 new AutoGetCollectionForReadCommand(opCtx, nss));
669             Collection* coll = ctx->getCollection();
670 
671             auto exec = uassertStatusOK(getExecutor(opCtx,
672                                                     coll,
673                                                     std::move(cq),
674                                                     PlanExecutor::YIELD_MANUAL,
675                                                     QueryPlannerParams::NO_TABLE_SCAN));
676 
677             BSONObj obj;
678             PlanExecutor::ExecState state;
679             while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
680                 BSONElement ne = obj["n"];
681                 verify(ne.isNumber());
682                 int myn = ne.numberInt();
683                 if (n != myn) {
684                     if (partialOk) {
685                         break;  // skipped chunk is probably on another shard
686                     }
687                     log() << "should have chunk: " << n << " have:" << myn;
688                     dumpChunks(opCtx, nss.ns(), query, sort);
689                     uassert(10040, "chunks out of order", n == myn);
690                 }
691 
692                 // make a copy of obj since we access data in it while yielding locks
693                 BSONObj owned = obj.getOwned();
694                 exec->saveState();
695                 // UNLOCKED
696                 ctx.reset();
697 
698                 int len;
699                 const char* data = owned["data"].binDataClean(len);
700                 // This is potentially an expensive operation, so do it out of the lock
701                 md5_append(&st, (const md5_byte_t*)(data), len);
702                 n++;
703 
704                 try {
705                     // RELOCKED
706                     ctx.reset(new AutoGetCollectionForReadCommand(opCtx, nss));
707                 } catch (const StaleConfigException&) {
708                     LOG(1) << "chunk metadata changed during filemd5, will retarget and continue";
709                     break;
710                 }
711 
712                 // Have the lock again. See if we were killed.
713                 if (!exec->restoreState().isOK()) {
714                     if (!partialOk) {
715                         uasserted(13281, "File deleted during filemd5 command");
716                     }
717                 }
718             }
719 
720             if (PlanExecutor::DEAD == state || PlanExecutor::FAILURE == state) {
721                 return appendCommandStatus(result,
722                                            WorkingSetCommon::getMemberObjectStatus(obj).withContext(
723                                                "Executor error during filemd5 command"));
724             }
725 
726             if (partialOk)
727                 result.appendBinData("md5state", sizeof(st), BinDataGeneral, &st);
728 
729             // This must be *after* the capture of md5state since it mutates st
730             md5_finish(&st, d);
731 
732             result.append("numChunks", n);
733             result.append("md5", digestToString(d));
734 
735             return true;
736         });
737     }
738 
dumpChunks(OperationContext * opCtx,const string & ns,const BSONObj & query,const BSONObj & sort)739     void dumpChunks(OperationContext* opCtx,
740                     const string& ns,
741                     const BSONObj& query,
742                     const BSONObj& sort) {
743         DBDirectClient client(opCtx);
744         Query q(query);
745         q.sort(sort);
746         unique_ptr<DBClientCursor> c = client.query(ns, q);
747         while (c->more()) {
748             log() << c->nextSafe();
749         }
750     }
751 
752 } cmdFileMD5;
753 
754 
755 class CmdDatasize : public ErrmsgCommandDeprecated {
parseNs(const string & dbname,const BSONObj & cmdObj) const756     virtual string parseNs(const string& dbname, const BSONObj& cmdObj) const {
757         return parseNsFullyQualified(dbname, cmdObj);
758     }
759 
760 public:
CmdDatasize()761     CmdDatasize() : ErrmsgCommandDeprecated("dataSize", "datasize") {}
762 
slaveOk() const763     virtual bool slaveOk() const {
764         return true;
765     }
supportsWriteConcern(const BSONObj & cmd) const766     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
767         return false;
768     }
help(stringstream & help) const769     virtual void help(stringstream& help) const {
770         help << "determine data size for a set of data in a certain range"
771                 "\nexample: { dataSize:\"blog.posts\", keyPattern:{x:1}, min:{x:10}, max:{x:55} }"
772                 "\nmin and max parameters are optional. They must either both be included or both "
773                 "omitted"
774                 "\nkeyPattern is an optional parameter indicating an index pattern that would be "
775                 "useful"
776                 "for iterating over the min/max bounds. If keyPattern is omitted, it is inferred "
777                 "from "
778                 "the structure of min. "
779                 "\nnote: This command may take a while to run";
780     }
781 
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)782     virtual void addRequiredPrivileges(const std::string& dbname,
783                                        const BSONObj& cmdObj,
784                                        std::vector<Privilege>* out) {
785         ActionSet actions;
786         actions.addAction(ActionType::find);
787         out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
788     }
789 
errmsgRun(OperationContext * opCtx,const string & dbname,const BSONObj & jsobj,string & errmsg,BSONObjBuilder & result)790     bool errmsgRun(OperationContext* opCtx,
791                    const string& dbname,
792                    const BSONObj& jsobj,
793                    string& errmsg,
794                    BSONObjBuilder& result) {
795         Timer timer;
796 
797         string ns = jsobj.firstElement().String();
798         BSONObj min = jsobj.getObjectField("min");
799         BSONObj max = jsobj.getObjectField("max");
800         BSONObj keyPattern = jsobj.getObjectField("keyPattern");
801         bool estimate = jsobj["estimate"].trueValue();
802 
803         AutoGetCollectionForReadCommand ctx(opCtx, NamespaceString(ns));
804 
805         Collection* collection = ctx.getCollection();
806         long long numRecords = 0;
807         if (collection) {
808             numRecords = collection->numRecords(opCtx);
809         }
810 
811         if (numRecords == 0) {
812             result.appendNumber("size", 0);
813             result.appendNumber("numObjects", 0);
814             result.append("millis", timer.millis());
815             return true;
816         }
817 
818         result.appendBool("estimate", estimate);
819 
820         unique_ptr<PlanExecutor, PlanExecutor::Deleter> exec;
821         if (min.isEmpty() && max.isEmpty()) {
822             if (estimate) {
823                 result.appendNumber("size", static_cast<long long>(collection->dataSize(opCtx)));
824                 result.appendNumber("numObjects", numRecords);
825                 result.append("millis", timer.millis());
826                 return 1;
827             }
828             exec = InternalPlanner::collectionScan(opCtx, ns, collection, PlanExecutor::NO_YIELD);
829         } else if (min.isEmpty() || max.isEmpty()) {
830             errmsg = "only one of min or max specified";
831             return false;
832         } else {
833             if (keyPattern.isEmpty()) {
834                 // if keyPattern not provided, try to infer it from the fields in 'min'
835                 keyPattern = Helpers::inferKeyPattern(min);
836             }
837 
838             IndexDescriptor* idx =
839                 collection->getIndexCatalog()->findShardKeyPrefixedIndex(opCtx,
840                                                                          keyPattern,
841                                                                          true);  // requireSingleKey
842 
843             if (idx == NULL) {
844                 errmsg = "couldn't find valid index containing key pattern";
845                 return false;
846             }
847             // If both min and max non-empty, append MinKey's to make them fit chosen index
848             KeyPattern kp(idx->keyPattern());
849             min = Helpers::toKeyFormat(kp.extendRangeBound(min, false));
850             max = Helpers::toKeyFormat(kp.extendRangeBound(max, false));
851 
852             exec = InternalPlanner::indexScan(opCtx,
853                                               collection,
854                                               idx,
855                                               min,
856                                               max,
857                                               BoundInclusion::kIncludeStartKeyOnly,
858                                               PlanExecutor::NO_YIELD);
859         }
860 
861         long long avgObjSize = collection->dataSize(opCtx) / numRecords;
862 
863         long long maxSize = jsobj["maxSize"].numberLong();
864         long long maxObjects = jsobj["maxObjects"].numberLong();
865 
866         long long size = 0;
867         long long numObjects = 0;
868 
869         RecordId loc;
870         BSONObj obj;
871         PlanExecutor::ExecState state;
872         while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, &loc))) {
873             if (estimate)
874                 size += avgObjSize;
875             else
876                 size += collection->getRecordStore()->dataFor(opCtx, loc).size();
877 
878             numObjects++;
879 
880             if ((maxSize && size > maxSize) || (maxObjects && numObjects > maxObjects)) {
881                 result.appendBool("maxReached", true);
882                 break;
883             }
884         }
885 
886         if (PlanExecutor::FAILURE == state || PlanExecutor::DEAD == state) {
887             warning() << "Internal error while reading " << ns;
888             return appendCommandStatus(result,
889                                        WorkingSetCommon::getMemberObjectStatus(obj).withContext(
890                                            "Executor error while reading during dataSize command"));
891         }
892 
893         ostringstream os;
894         os << "Finding size for ns: " << ns;
895         if (!min.isEmpty()) {
896             os << " between " << min << " and " << max;
897         }
898 
899         result.appendNumber("size", size);
900         result.appendNumber("numObjects", numObjects);
901         result.append("millis", timer.millis());
902         return true;
903     }
904 
905 } cmdDatasize;
906 
907 class CollectionStats : public ErrmsgCommandDeprecated {
908 public:
CollectionStats()909     CollectionStats() : ErrmsgCommandDeprecated("collStats", "collstats") {}
910 
slaveOk() const911     virtual bool slaveOk() const {
912         return true;
913     }
maintenanceOk() const914     bool maintenanceOk() const override {
915         return false;
916     }
supportsWriteConcern(const BSONObj & cmd) const917     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
918         return false;
919     }
help(stringstream & help) const920     virtual void help(stringstream& help) const {
921         help
922             << "{ collStats:\"blog.posts\" , scale : 1 } scale divides sizes e.g. for KB use 1024\n"
923                "    avgObjSize - in bytes";
924     }
925 
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)926     virtual void addRequiredPrivileges(const std::string& dbname,
927                                        const BSONObj& cmdObj,
928                                        std::vector<Privilege>* out) {
929         ActionSet actions;
930         actions.addAction(ActionType::collStats);
931         out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions));
932     }
933 
errmsgRun(OperationContext * opCtx,const string & dbname,const BSONObj & jsobj,string & errmsg,BSONObjBuilder & result)934     bool errmsgRun(OperationContext* opCtx,
935                    const string& dbname,
936                    const BSONObj& jsobj,
937                    string& errmsg,
938                    BSONObjBuilder& result) {
939         const NamespaceString nss(parseNsCollectionRequired(dbname, jsobj));
940 
941         if (nss.coll().empty()) {
942             errmsg = "No collection name specified";
943             return false;
944         }
945 
946         result.append("ns", nss.ns());
947         Status status = appendCollectionStorageStats(opCtx, nss, jsobj, &result);
948         if (!status.isOK()) {
949             errmsg = status.reason();
950             return false;
951         }
952 
953         return true;
954     }
955 
956 } cmdCollectionStats;
957 
958 class CollectionModCommand : public BasicCommand {
959 public:
CollectionModCommand()960     CollectionModCommand() : BasicCommand("collMod") {}
961 
slaveOk() const962     virtual bool slaveOk() const {
963         return false;
964     }
supportsWriteConcern(const BSONObj & cmd) const965     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
966         return true;
967     }
help(stringstream & help) const968     virtual void help(stringstream& help) const {
969         help << "Sets collection options.\n"
970                 "Example: { collMod: 'foo', usePowerOf2Sizes:true }\n"
971                 "Example: { collMod: 'foo', index: {keyPattern: {a: 1}, expireAfterSeconds: 600} "
972                 "Example: { collMod: 'foo', index: {name: 'bar', expireAfterSeconds: 600} }\n";
973     }
974 
checkAuthForCommand(Client * client,const std::string & dbname,const BSONObj & cmdObj)975     virtual Status checkAuthForCommand(Client* client,
976                                        const std::string& dbname,
977                                        const BSONObj& cmdObj) {
978         const NamespaceString nss(parseNs(dbname, cmdObj));
979         return AuthorizationSession::get(client)->checkAuthForCollMod(nss, cmdObj, false);
980     }
981 
run(OperationContext * opCtx,const string & dbname,const BSONObj & jsobj,BSONObjBuilder & result)982     bool run(OperationContext* opCtx,
983              const string& dbname,
984              const BSONObj& jsobj,
985              BSONObjBuilder& result) {
986         const NamespaceString nss(parseNsCollectionRequired(dbname, jsobj));
987         return appendCommandStatus(result, collMod(opCtx, nss, jsobj, &result));
988     }
989 
990 } collectionModCommand;
991 
992 class DBStats : public ErrmsgCommandDeprecated {
993 public:
DBStats()994     DBStats() : ErrmsgCommandDeprecated("dbStats", "dbstats") {}
995 
slaveOk() const996     virtual bool slaveOk() const {
997         return true;
998     }
maintenanceOk() const999     bool maintenanceOk() const override {
1000         return false;
1001     }
supportsWriteConcern(const BSONObj & cmd) const1002     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
1003         return false;
1004     }
help(stringstream & help) const1005     virtual void help(stringstream& help) const {
1006         help << "Get stats on a database. Not instantaneous. Slower for databases with large "
1007                 ".ns files.\n"
1008                 "Example: { dbStats:1, scale:1 }";
1009     }
1010 
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)1011     virtual void addRequiredPrivileges(const std::string& dbname,
1012                                        const BSONObj& cmdObj,
1013                                        std::vector<Privilege>* out) {
1014         ActionSet actions;
1015         actions.addAction(ActionType::dbStats);
1016         out->push_back(Privilege(ResourcePattern::forDatabaseName(dbname), actions));
1017     }
1018 
errmsgRun(OperationContext * opCtx,const string & dbname,const BSONObj & jsobj,string & errmsg,BSONObjBuilder & result)1019     bool errmsgRun(OperationContext* opCtx,
1020                    const string& dbname,
1021                    const BSONObj& jsobj,
1022                    string& errmsg,
1023                    BSONObjBuilder& result) {
1024         int scale = 1;
1025         if (jsobj["scale"].isNumber()) {
1026             scale = jsobj["scale"].numberInt();
1027             if (scale <= 0) {
1028                 errmsg = "scale has to be > 0";
1029                 return false;
1030             }
1031         } else if (jsobj["scale"].trueValue()) {
1032             errmsg = "scale has to be a number > 0";
1033             return false;
1034         }
1035 
1036         const string ns = parseNs(dbname, jsobj);
1037         uassert(ErrorCodes::InvalidNamespace,
1038                 str::stream() << "Invalid db name: " << ns,
1039                 NamespaceString::validDBName(ns, NamespaceString::DollarInDbNameBehavior::Allow));
1040 
1041         // TODO (Kal): OldClientContext legacy, needs to be removed
1042         {
1043             CurOp::get(opCtx)->ensureStarted();
1044             stdx::lock_guard<Client> lk(*opCtx->getClient());
1045             CurOp::get(opCtx)->setNS_inlock(dbname);
1046         }
1047 
1048         AutoGetDb autoDb(opCtx, ns, MODE_IS);
1049 
1050         result.append("db", ns);
1051 
1052         Database* db = autoDb.getDb();
1053         if (!db) {
1054             // TODO: This preserves old behaviour where we used to create an empty database
1055             // metadata even when the database is accessed for read. Without this several
1056             // unit-tests will fail, which are fairly easy to fix. If backwards compatibility
1057             // is not needed for the missing DB case, we can just do the same that's done in
1058             // CollectionStats.
1059             result.appendNumber("collections", 0);
1060             result.appendNumber("views", 0);
1061             result.appendNumber("objects", 0);
1062             result.append("avgObjSize", 0);
1063             result.appendNumber("dataSize", 0);
1064             result.appendNumber("storageSize", 0);
1065             result.appendNumber("numExtents", 0);
1066             result.appendNumber("indexes", 0);
1067             result.appendNumber("indexSize", 0);
1068             result.appendNumber("fileSize", 0);
1069             if (!getGlobalServiceContext()->getGlobalStorageEngine()->isEphemeral()) {
1070                 result.appendNumber("fsUsedSize", 0);
1071                 result.appendNumber("fsTotalSize", 0);
1072             }
1073         } else {
1074             {
1075                 stdx::lock_guard<Client> lk(*opCtx->getClient());
1076                 // TODO: OldClientContext legacy, needs to be removed
1077                 CurOp::get(opCtx)->enter_inlock(dbname.c_str(), db->getProfilingLevel());
1078             }
1079 
1080             db->getStats(opCtx, &result, scale);
1081         }
1082 
1083         return true;
1084     }
1085 
1086 } cmdDBStats;
1087 
1088 /* Returns client's uri */
1089 class CmdWhatsMyUri : public BasicCommand {
1090 public:
CmdWhatsMyUri()1091     CmdWhatsMyUri() : BasicCommand("whatsmyuri") {}
slaveOk() const1092     virtual bool slaveOk() const {
1093         return true;
1094     }
supportsWriteConcern(const BSONObj & cmd) const1095     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
1096         return false;
1097     }
help(stringstream & help) const1098     virtual void help(stringstream& help) const {
1099         help << "{whatsmyuri:1}";
1100     }
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)1101     virtual void addRequiredPrivileges(const std::string& dbname,
1102                                        const BSONObj& cmdObj,
1103                                        std::vector<Privilege>* out) {}  // No auth required
run(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,BSONObjBuilder & result)1104     virtual bool run(OperationContext* opCtx,
1105                      const string& dbname,
1106                      const BSONObj& cmdObj,
1107                      BSONObjBuilder& result) {
1108         result << "you" << opCtx->getClient()->clientAddress(true /*includePort*/);
1109         return true;
1110     }
1111 } cmdWhatsMyUri;
1112 
1113 class AvailableQueryOptions : public BasicCommand {
1114 public:
AvailableQueryOptions()1115     AvailableQueryOptions() : BasicCommand("availableQueryOptions", "availablequeryoptions") {}
1116 
slaveOk() const1117     virtual bool slaveOk() const {
1118         return true;
1119     }
supportsWriteConcern(const BSONObj & cmd) const1120     virtual bool supportsWriteConcern(const BSONObj& cmd) const override {
1121         return false;
1122     }
checkAuthForCommand(Client * client,const std::string & dbname,const BSONObj & cmdObj)1123     virtual Status checkAuthForCommand(Client* client,
1124                                        const std::string& dbname,
1125                                        const BSONObj& cmdObj) {
1126         return Status::OK();
1127     }
1128 
run(OperationContext * opCtx,const string & dbname,const BSONObj & cmdObj,BSONObjBuilder & result)1129     virtual bool run(OperationContext* opCtx,
1130                      const string& dbname,
1131                      const BSONObj& cmdObj,
1132                      BSONObjBuilder& result) {
1133         result << "options" << QueryOption_AllSupported;
1134         return true;
1135     }
1136 } availableQueryOptionsCmd;
1137 
1138 }  // namespace mongo
1139