Coverage Report

Created: 2026-03-31 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mysql-server/mysys/my_file.cc
Line
Count
Source
1
/* Copyright (c) 2000, 2025, Oracle and/or its affiliates.
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License, version 2.0,
5
   as published by the Free Software Foundation.
6
7
   This program is designed to work with certain software (including
8
   but not limited to OpenSSL) that is licensed under separate terms,
9
   as designated in a particular file or component or in included license
10
   documentation.  The authors of MySQL hereby grant you an additional
11
   permission to link the program and your derivative works with the
12
   separately licensed software that they have either included with
13
   the program or referenced in the documentation.
14
15
   Without limiting anything contained in the foregoing, this file,
16
   which is part of C Driver for MySQL (Connector/C), is also subject to the
17
   Universal FOSS Exception, version 1.0, a copy of which can be found at
18
   http://oss.oracle.com/licenses/universal-foss-exception.
19
20
   This program is distributed in the hope that it will be useful,
21
   but WITHOUT ANY WARRANTY; without even the implied warranty of
22
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
   GNU General Public License, version 2.0, for more details.
24
25
   You should have received a copy of the GNU General Public License
26
   along with this program; if not, write to the Free Software
27
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
28
29
/**
30
  @file mysys/my_file.cc
31
*/
32
33
#include "my_config.h"
34
35
#include <errno.h>  // IWYU pragma: keep errno
36
#include <sys/types.h>
37
38
#include <algorithm>
39
#include <cassert>
40
#include <cstring>
41
#include <iostream>
42
#include <limits>
43
#include <utility>
44
#include <vector>
45
46
#include "sql/malloc_allocator.h"
47
48
#include "mutex_lock.h"  // MUTEX_LOCK
49
#include "my_dbug.h"
50
#include "my_inttypes.h"
51
#include "my_io.h"
52
#include "my_sys.h"
53
#include "mysql/service_mysql_alloc.h"
54
#include "mysys/mysys_priv.h"
55
#ifdef HAVE_SYS_RESOURCE_H
56
#include <sys/resource.h> /* RLIMIT_NOFILE */
57
#endif
58
59
namespace {
60
/**
61
  Set the OS limit on the number of open files. On POSIX systems this
62
  calls setrlimit(RLIMIT_NOFILE, ...). On Windows there is no
63
  corresponding api so the requested value is returned. The assumption
64
  being that the request will never be larger than OS_FILE_LIMIT, @see
65
  my_set_max_open_files.
66
67
  @param max_file_limit Files to open
68
69
  @note The request may not fulfilled because of system limitations
70
71
  @return Files available to open. May be more or less than max_file_limit!
72
*/
73
74
0
uint SetOsLimitMaxOpenFiles(uint max_file_limit) {
75
0
  DBUG_TRACE;
76
77
0
#ifndef _WIN32
78
0
  rlimit existing;
79
0
  if (getrlimit(RLIMIT_NOFILE, &existing) == -1) {
80
0
    DBUG_PRINT("warning", ("getrlimit(RLIMIT_NOFILE) failed: %s (%d)",
81
0
                           strerror(errno), errno));
82
0
    return max_file_limit;
83
0
  }
84
85
  // If rlim_cur is larger than what is requested, we use that
86
  // instead, but capped to the largest value an uint can hold,
87
  // (rlim_t can be 64 bit).
88
0
  if (existing.rlim_cur >= max_file_limit) {
89
0
    constexpr const rlim_t uim = std::numeric_limits<uint>::max();
90
0
    return std::min(existing.rlim_cur, uim);
91
0
  }
92
93
  // Attempt to modify OS setting
94
0
  rlimit request;
95
0
  request.rlim_cur = max_file_limit;
96
0
  request.rlim_max = max_file_limit;
97
0
  if (setrlimit(RLIMIT_NOFILE, &request) == -1) {
98
0
    DBUG_PRINT("warning", ("setrlimit(RLIMIT_NOFILE)=%u failed: %s (%d)",
99
0
                           max_file_limit, strerror(errno), errno));
100
0
    return existing.rlim_cur; /* Use original value */
101
0
  }
102
103
#ifndef NDEBUG
104
  // Read back new value to check "what we got". Seems overly
105
  // pessimistic to assume that a successful setrlimit did not
106
  // actually set the requested values.
107
  rlimit readback;
108
  if (getrlimit(RLIMIT_NOFILE, &readback) == -1) {
109
    DBUG_PRINT("warning",
110
               ("getrlimit(RLIMIT_NOFILE) (after set)  failed: %s (%d)",
111
                strerror(errno), errno));
112
    return max_file_limit;
113
  }
114
  assert(readback.rlim_cur == request.rlim_cur &&
115
         readback.rlim_max == request.rlim_max);
116
#endif /* NDEBUG */
117
0
  return request.rlim_cur;
118
#else  /* not defined(_WIN32) */
119
  // We don't know the limit.
120
  assert(max_file_limit <= OS_FILE_LIMIT);
121
  return max_file_limit;
122
#endif /* not defined _WIN32 */
123
0
}
124
125
/**
126
   Rule of 5 class.
127
   @see https://en.cppreference.com/w/cpp/language/rule_of_three
128
   @see https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-five
129
*/
130
class FileInfo {
131
  const char *m_name = nullptr;
132
  file_info::OpenType m_type = file_info::OpenType::UNOPEN;
133
134
 public:
135
0
  FileInfo() = default;
136
137
  FileInfo(const char *n, file_info::OpenType t)
138
0
      : m_name{my_strdup(key_memory_my_file_info, n,
139
0
                         MYF(MY_WME | ME_FATALERROR))},
140
0
        m_type{t} {}
141
142
  // Rule of 5 (2)
143
  FileInfo(const FileInfo &) = delete;
144
145
  // Rule of 5 (4)
146
  FileInfo(FileInfo &&src) noexcept
147
0
      : m_name{std::exchange(src.m_name, nullptr)},
148
0
        m_type{std::exchange(src.m_type, file_info::OpenType::UNOPEN)} {}
149
150
  // Rule of 5 (1)
151
0
  ~FileInfo() { my_free(const_cast<char *>(m_name)); }
152
153
  // Rule of 5 (3)
154
  FileInfo &operator=(const FileInfo &) = delete;
155
156
  // Rule of 5 (5)
157
0
  FileInfo &operator=(FileInfo &&src) {
158
0
    FileInfo tmp{std::move(src)};
159
0
    Swap(&tmp);
160
0
    return *this;
161
0
  }
162
163
  // Member swap for move assignment.
164
0
  void Swap(FileInfo *src) noexcept {
165
0
    std::swap(m_type, src->m_type);
166
0
    std::swap(m_name, src->m_name);
167
0
  }
168
169
0
  const char *name() const { return m_name; }
170
0
  file_info::OpenType type() const { return m_type; }
171
};
172
173
using FileInfoAllocator = Malloc_allocator<FileInfo>;
174
using FileInfoVector = std::vector<FileInfo, FileInfoAllocator>;
175
FileInfoVector *fivp = nullptr;
176
}  // namespace
177
178
namespace file_info {
179
180
/**
181
   Add FileInfo entry for file descriptor. Increments status variable
182
   for open files/streams.
183
   @relates file_info::RegisterFilename
184
185
   @param fd file descriptor
186
   @param file_name name of file
187
   @param type_of_file tag indicating how the fd was created
188
 */
189
0
void RegisterFilename(File fd, const char *file_name, OpenType type_of_file) {
190
0
  assert(fd > -1);
191
0
  FileInfoVector &fiv = *fivp;
192
0
  MUTEX_LOCK(g, &THR_LOCK_open);
193
0
  if (static_cast<size_t>(fd) >= fiv.size()) {
194
0
    fiv.resize(fd + 1);
195
0
  }
196
0
  CountFileOpen(fiv[fd].type(), type_of_file);
197
0
  fiv[fd] = {file_name, type_of_file};
198
199
0
  dbug("fileinfo", [&]() {
200
0
    std::cerr << "Registering (" << fd << ", '" << file_name << ")"
201
0
              << std::endl;
202
0
  });
203
0
}
204
205
/**
206
   Remove FileInfo entry for file descriptor. Decrements status
207
   variables for open files/streams.
208
   @relates file_info::UnregisterFilename
209
210
   @param fd file descriptor
211
 */
212
0
void UnregisterFilename(File fd) {
213
0
  FileInfoVector &fiv = *fivp;
214
0
  MUTEX_LOCK(g, &THR_LOCK_open);
215
216
0
  if (static_cast<size_t>(fd) >= fiv.size()) {
217
0
    dbug("fileinfo", [&]() {
218
0
      std::cerr << "Un-registering unknown fd:" << fd << "!" << std::endl;
219
0
    });
220
0
    return;
221
0
  }
222
0
  if (fiv[fd].type() == OpenType::UNOPEN) {
223
0
    dbug("fileinfo", [&]() {
224
0
      std::cerr << "Un-registering already UNOPEN fd:" << fd << std::endl;
225
0
    });
226
0
    return;
227
0
  }
228
0
  CountFileClose(fiv[fd].type());
229
230
0
  dbug("fileinfo", [&]() {
231
0
    std::cerr << "Un-registering (" << fd << ", '" << fiv[fd].name() << "')"
232
0
              << std::endl;
233
0
  });
234
0
  fiv[fd] = {};
235
0
}
236
}  // namespace file_info
237
238
/**
239
  Get filename of file.
240
241
  @param fd file descriptor
242
  @return file name in file_info object
243
*/
244
0
const char *my_filename(File fd) {
245
0
  DBUG_TRACE;
246
0
  const FileInfoVector &fiv = *fivp;
247
0
  MUTEX_LOCK(g, &THR_LOCK_open);
248
0
  if (fd < 0 || fd >= static_cast<int>(fiv.size())) {
249
0
    return "<fd out of range>";
250
0
  }
251
0
  const FileInfo &fi = fiv[fd];
252
0
  if (fi.type() == file_info::OpenType::UNOPEN) {
253
0
    return "<unopen fd>";
254
0
  }
255
0
  return fi.name();
256
0
}
257
258
/**
259
  Sets the OS limit on the number of open files (if supported).
260
261
  @param files Number of requested files
262
263
  @return  The actual new OS limit which may be both more or less than
264
           what was requested.
265
*/
266
0
uint my_set_max_open_files(uint files) {
267
0
  DBUG_TRACE;
268
0
  return SetOsLimitMaxOpenFiles(std::min(files + MY_FILE_MIN, OS_FILE_LIMIT));
269
0
}
270
271
/**
272
  Constructs static objects.
273
*/
274
2
void MyFileInit() {
275
2
  fivp =
276
2
      new FileInfoVector(Malloc_allocator<FileInfo>{key_memory_my_file_info});
277
2
}
278
279
/**
280
  Destroys static objects.
281
*/
282
0
void MyFileEnd() { delete fivp; }