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::kStorage
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/db/storage/storage_engine_lock_file.h"
36 
37 #include <boost/filesystem.hpp>
38 #include <io.h>
39 #include <ostream>
40 #include <sstream>
41 
42 #include "mongo/platform/process_id.h"
43 #include "mongo/util/log.h"
44 #include "mongo/util/mongoutils/str.h"
45 #include "mongo/util/text.h"
46 
47 namespace mongo {
48 
49 namespace {
50 
_truncateFile(HANDLE handle)51 Status _truncateFile(HANDLE handle) {
52     invariant(handle != INVALID_HANDLE_VALUE);
53 
54     LARGE_INTEGER largeint;
55     largeint.QuadPart = 0;
56     if (::SetFilePointerEx(handle, largeint, NULL, FILE_BEGIN) == FALSE) {
57         int errorcode = GetLastError();
58         return Status(ErrorCodes::FileStreamFailed,
59                       str::stream() << "Unable to truncate lock file (SetFilePointerEx failed) "
60                                     << errnoWithDescription(errorcode));
61     }
62 
63     if (::SetEndOfFile(handle) == FALSE) {
64         int errorcode = GetLastError();
65         return Status(ErrorCodes::FileStreamFailed,
66                       str::stream() << "Unable to truncate lock file (SetEndOfFile failed) "
67                                     << errnoWithDescription(errorcode));
68     }
69 
70     return Status::OK();
71 }
72 
73 }  // namespace
74 
75 class StorageEngineLockFile::LockFileHandle {
76 public:
LockFileHandle()77     LockFileHandle() : _handle(INVALID_HANDLE_VALUE) {}
isValid() const78     bool isValid() const {
79         return _handle != INVALID_HANDLE_VALUE;
80     }
clear()81     void clear() {
82         _handle = INVALID_HANDLE_VALUE;
83     }
84     HANDLE _handle;
85 };
86 
StorageEngineLockFile(const std::string & dbpath,StringData fileName)87 StorageEngineLockFile::StorageEngineLockFile(const std::string& dbpath, StringData fileName)
88     : _dbpath(dbpath),
89       _filespec((boost::filesystem::path(_dbpath) / fileName.toString()).string()),
90       _uncleanShutdown(boost::filesystem::exists(_filespec) &&
91                        boost::filesystem::file_size(_filespec) > 0),
92       _lockFileHandle(new LockFileHandle()) {}
93 
~StorageEngineLockFile()94 StorageEngineLockFile::~StorageEngineLockFile() {}
95 
getFilespec() const96 std::string StorageEngineLockFile::getFilespec() const {
97     return _filespec;
98 }
99 
createdByUncleanShutdown() const100 bool StorageEngineLockFile::createdByUncleanShutdown() const {
101     return _uncleanShutdown;
102 }
103 
open()104 Status StorageEngineLockFile::open() {
105     try {
106         if (!boost::filesystem::exists(_dbpath)) {
107             return Status(
108                 ErrorCodes::NonExistentPath,
109                 str::stream() << "Data directory " << _dbpath
110                               << " not found. Create the missing directory or specify another path "
111                                  "using (1) the --dbpath command line option, or (2) by adding the "
112                                  "'storage.dbPath' option in the configuration file.");
113         }
114     } catch (const std::exception& ex) {
115         return Status(ErrorCodes::UnknownError,
116                       str::stream() << "Unable to check existence of data directory " << _dbpath
117                                     << ": "
118                                     << ex.what());
119     }
120 
121     HANDLE lockFileHandle = CreateFileW(toNativeString(_filespec.c_str()).c_str(),
122                                         GENERIC_READ | GENERIC_WRITE,
123                                         FILE_SHARE_READ /* only allow readers access */,
124                                         NULL,
125                                         OPEN_ALWAYS /* success if fh can open */,
126                                         0,
127                                         NULL);
128 
129     if (lockFileHandle == INVALID_HANDLE_VALUE) {
130         int errorcode = GetLastError();
131         if (errorcode == ERROR_ACCESS_DENIED) {
132             return Status(ErrorCodes::IllegalOperation,
133                           str::stream()
134                               << "Attempted to create a lock file on a read-only directory: "
135                               << _dbpath);
136         }
137         return Status(ErrorCodes::DBPathInUse,
138                       str::stream() << "Unable to create/open the lock file: " << _filespec << " ("
139                                     << errnoWithDescription(errorcode)
140                                     << ")."
141                                     << " Ensure the user executing mongod is the owner of the lock "
142                                        "file and has the appropriate permissions. Also make sure "
143                                        "that another mongod instance is not already running on the "
144                                     << _dbpath
145                                     << " directory");
146     }
147     _lockFileHandle->_handle = lockFileHandle;
148     return Status::OK();
149 }
150 
close()151 void StorageEngineLockFile::close() {
152     if (!_lockFileHandle->isValid()) {
153         return;
154     }
155     CloseHandle(_lockFileHandle->_handle);
156     _lockFileHandle->clear();
157 }
158 
writePid()159 Status StorageEngineLockFile::writePid() {
160     if (!_lockFileHandle->isValid()) {
161         return Status(ErrorCodes::FileNotOpen,
162                       str::stream() << "Unable to write process ID to " << _filespec
163                                     << " because file has not been opened.");
164     }
165 
166     Status status = _truncateFile(_lockFileHandle->_handle);
167     if (!status.isOK()) {
168         return status;
169     }
170 
171     ProcessId pid = ProcessId::getCurrent();
172     std::stringstream ss;
173     ss << pid << std::endl;
174     std::string pidStr = ss.str();
175     DWORD bytesWritten = 0;
176     if (::WriteFile(_lockFileHandle->_handle,
177                     static_cast<LPCVOID>(pidStr.c_str()),
178                     static_cast<DWORD>(pidStr.size()),
179                     &bytesWritten,
180                     NULL) == FALSE) {
181         int errorcode = GetLastError();
182         return Status(ErrorCodes::FileStreamFailed,
183                       str::stream() << "Unable to write process id " << pid.toString()
184                                     << " to file: "
185                                     << _filespec
186                                     << ' '
187                                     << errnoWithDescription(errorcode));
188     } else if (bytesWritten == 0) {
189         return Status(ErrorCodes::FileStreamFailed,
190                       str::stream() << "Unable to write process id " << pid.toString()
191                                     << " to file: "
192                                     << _filespec
193                                     << " no data written.");
194     }
195 
196     ::FlushFileBuffers(_lockFileHandle->_handle);
197 
198     return Status::OK();
199 }
200 
clearPidAndUnlock()201 void StorageEngineLockFile::clearPidAndUnlock() {
202     if (!_lockFileHandle->isValid()) {
203         return;
204     }
205     log() << "shutdown: removing fs lock...";
206     // This ought to be an unlink(), but Eliot says the last
207     // time that was attempted, there was a race condition
208     // with StorageEngineLockFile::open().
209     Status status = _truncateFile(_lockFileHandle->_handle);
210     if (!status.isOK()) {
211         log() << "couldn't remove fs lock " << status.toString();
212     }
213     CloseHandle(_lockFileHandle->_handle);
214     _lockFileHandle->clear();
215 }
216 
217 }  // namespace mongo
218