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