]> git.ipfire.org Git - thirdparty/squid.git/blob - src/store/Disk.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / store / Disk.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 20 Swap Dir base object */
10
11 #include "squid.h"
12 #include "cache_cf.h"
13 #include "compat/strtoll.h"
14 #include "ConfigOption.h"
15 #include "ConfigParser.h"
16 #include "globals.h"
17 #include "Parsing.h"
18 #include "SquidConfig.h"
19 #include "Store.h"
20 #include "store/Disk.h"
21 #include "StoreFileSystem.h"
22 #include "tools.h"
23
24 Store::Disk::Disk(char const *aType): theType(aType),
25 max_size(0), min_objsize(-1), max_objsize (-1),
26 path(nullptr), index(-1), disker(-1),
27 repl(nullptr), removals(0), scanned(0),
28 cleanLog(nullptr)
29 {
30 fs.blksize = 1024;
31 }
32
33 Store::Disk::~Disk()
34 {
35 // TODO: should we delete repl?
36 xfree(path);
37 }
38
39 void
40 Store::Disk::create() {}
41
42 void
43 Store::Disk::dump(StoreEntry &)const {}
44
45 bool
46 Store::Disk::doubleCheck(StoreEntry &)
47 {
48 return false;
49 }
50
51 void
52 Store::Disk::getStats(StoreInfoStats &stats) const
53 {
54 if (!doReportStat())
55 return;
56
57 stats.swap.size = currentSize();
58 stats.swap.capacity = maxSize();
59 stats.swap.count = currentCount();
60 }
61
62 void
63 Store::Disk::stat(StoreEntry &output) const
64 {
65 if (!doReportStat())
66 return;
67
68 storeAppendPrintf(&output, "Store Directory #%d (%s): %s\n", index, type(),
69 path);
70 storeAppendPrintf(&output, "FS Block Size %d Bytes\n",
71 fs.blksize);
72 statfs(output);
73
74 if (repl) {
75 storeAppendPrintf(&output, "Removal policy: %s\n", repl->_type);
76
77 if (repl->Stats)
78 repl->Stats(repl, &output);
79 }
80 }
81
82 void
83 Store::Disk::statfs(StoreEntry &)const {}
84
85 void
86 Store::Disk::maintain() {}
87
88 uint64_t
89 Store::Disk::minSize() const
90 {
91 // XXX: Not all disk stores use Config.Swap.lowWaterMark
92 return ((maxSize() * Config.Swap.lowWaterMark) / 100);
93 }
94
95 int64_t
96 Store::Disk::minObjectSize() const
97 {
98 // per-store min-size=N value is authoritative
99 return min_objsize > -1 ? min_objsize : Config.Store.minObjectSize;
100 }
101
102 int64_t
103 Store::Disk::maxObjectSize() const
104 {
105 // per-store max-size=N value is authoritative
106 if (max_objsize > -1)
107 return max_objsize;
108
109 // store with no individual max limit is limited by configured maximum_object_size
110 // or the total store size, whichever is smaller
111 return min(static_cast<int64_t>(maxSize()), Config.Store.maxObjectSize);
112 }
113
114 void
115 Store::Disk::maxObjectSize(int64_t newMax)
116 {
117 // negative values mean no limit (-1)
118 if (newMax < 0) {
119 max_objsize = -1; // set explicitly in case it had a non-default value previously
120 return;
121 }
122
123 // prohibit values greater than total storage area size
124 // but set max_objsize to the maximum allowed to override maximum_object_size global config
125 if (static_cast<uint64_t>(newMax) > maxSize()) {
126 debugs(47, DBG_PARSE_NOTE(2), "WARNING: Ignoring 'max-size' option for " << path <<
127 " which is larger than total cache_dir size of " << maxSize() << " bytes.");
128 max_objsize = maxSize();
129 return;
130 }
131
132 max_objsize = newMax;
133 }
134
135 void
136 Store::Disk::reference(StoreEntry &) {}
137
138 bool
139 Store::Disk::dereference(StoreEntry &)
140 {
141 return true; // keep in global store_table
142 }
143
144 void
145 Store::Disk::diskFull()
146 {
147 if (currentSize() >= maxSize())
148 return;
149
150 max_size = currentSize();
151
152 debugs(20, DBG_IMPORTANT, "WARNING: Shrinking cache_dir #" << index << " to " << currentSize() / 1024.0 << " KB");
153 }
154
155 bool
156 Store::Disk::objectSizeIsAcceptable(int64_t objsize) const
157 {
158 // need either the expected or the already accumulated object size
159 assert(objsize >= 0);
160 return minObjectSize() <= objsize && objsize <= maxObjectSize();
161 }
162
163 bool
164 Store::Disk::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const
165 {
166 debugs(47,8, "cache_dir[" << index << "]: needs " <<
167 diskSpaceNeeded << " <? " << max_objsize);
168
169 if (EBIT_TEST(e.flags, ENTRY_SPECIAL))
170 return false; // we do not store Squid-generated entries
171
172 if (!objectSizeIsAcceptable(diskSpaceNeeded))
173 return false; // does not satisfy size limits
174
175 if (flags.read_only)
176 return false; // cannot write at all
177
178 if (currentSize() > maxSize())
179 return false; // already overflowing
180
181 /* Return 999 (99.9%) constant load; TODO: add a named constant for this */
182 load = 999;
183 return true; // kids may provide more tests and should report true load
184 }
185
186 /* Move to StoreEntry ? */
187 bool
188 Store::Disk::canLog(StoreEntry const &e)const
189 {
190 if (!e.hasDisk())
191 return false;
192
193 if (!e.swappedOut())
194 return false;
195
196 if (e.swap_file_sz <= 0)
197 return false;
198
199 if (EBIT_TEST(e.flags, RELEASE_REQUEST))
200 return false;
201
202 if (EBIT_TEST(e.flags, KEY_PRIVATE))
203 return false;
204
205 if (EBIT_TEST(e.flags, ENTRY_SPECIAL))
206 return false;
207
208 return true;
209 }
210
211 void
212 Store::Disk::openLog() {}
213
214 void
215 Store::Disk::closeLog() {}
216
217 int
218 Store::Disk::writeCleanStart()
219 {
220 return 0;
221 }
222
223 void
224 Store::Disk::writeCleanDone() {}
225
226 void
227 Store::Disk::logEntry(const StoreEntry &, int) const {}
228
229 char const *
230 Store::Disk::type() const
231 {
232 return theType;
233 }
234
235 bool
236 Store::Disk::active() const
237 {
238 if (IamWorkerProcess())
239 return true;
240
241 // we are inside a disker dedicated to this disk
242 if (KidIdentifier == disker)
243 return true;
244
245 return false; // Coordinator, wrong disker, etc.
246 }
247
248 bool
249 Store::Disk::needsDiskStrand() const
250 {
251 return false;
252 }
253
254 /* NOT performance critical. Really. Don't bother optimising for speed
255 * - RBC 20030718
256 */
257 ConfigOption *
258 Store::Disk::getOptionTree() const
259 {
260 ConfigOptionVector *result = new ConfigOptionVector;
261 result->options.push_back(new ConfigOptionAdapter<Disk>(*const_cast<Disk*>(this), &Store::Disk::optionReadOnlyParse, &Store::Disk::optionReadOnlyDump));
262 result->options.push_back(new ConfigOptionAdapter<Disk>(*const_cast<Disk*>(this), &Store::Disk::optionObjectSizeParse, &Store::Disk::optionObjectSizeDump));
263 return result;
264 }
265
266 void
267 Store::Disk::parseOptions(int isaReconfig)
268 {
269 const bool old_read_only = flags.read_only;
270 char *name, *value;
271
272 ConfigOption *newOption = getOptionTree();
273
274 while ((name = ConfigParser::NextToken()) != nullptr) {
275 value = strchr(name, '=');
276
277 if (value) {
278 *value = '\0'; /* cut on = */
279 ++value;
280 }
281
282 debugs(3,2, "cache_dir " << name << '=' << (value ? value : ""));
283
284 if (newOption)
285 if (!newOption->parse(name, value, isaReconfig)) {
286 self_destruct();
287 return;
288 }
289 }
290
291 delete newOption;
292
293 /*
294 * Handle notifications about reconfigured single-options with no value
295 * where the removal of the option cannot be easily detected in the
296 * parsing...
297 */
298
299 if (isaReconfig) {
300 if (old_read_only != flags.read_only) {
301 debugs(3, DBG_IMPORTANT, "Cache dir '" << path << "' now " << (flags.read_only ? "No-Store" : "Read-Write"));
302 }
303 }
304 }
305
306 void
307 Store::Disk::dumpOptions(StoreEntry * entry) const
308 {
309 ConfigOption *newOption = getOptionTree();
310
311 if (newOption)
312 newOption->dump(entry);
313
314 delete newOption;
315 }
316
317 bool
318 Store::Disk::optionReadOnlyParse(char const *option, const char *value, int)
319 {
320 if (strcmp(option, "no-store") != 0 && strcmp(option, "read-only") != 0)
321 return false;
322
323 if (strcmp(option, "read-only") == 0) {
324 debugs(3, DBG_PARSE_NOTE(3), "UPGRADE WARNING: Replace cache_dir option 'read-only' with 'no-store'.");
325 }
326
327 bool read_only = 0;
328
329 if (value)
330 read_only = (xatoi(value) != 0);
331 else
332 read_only = true;
333
334 flags.read_only = read_only;
335
336 return true;
337 }
338
339 void
340 Store::Disk::optionReadOnlyDump(StoreEntry * e) const
341 {
342 if (flags.read_only)
343 storeAppendPrintf(e, " no-store");
344 }
345
346 bool
347 Store::Disk::optionObjectSizeParse(char const *option, const char *value, int isaReconfig)
348 {
349 int64_t *val;
350 if (strcmp(option, "max-size") == 0) {
351 val = &max_objsize;
352 } else if (strcmp(option, "min-size") == 0) {
353 val = &min_objsize;
354 } else
355 return false;
356
357 if (!value) {
358 self_destruct();
359 return false;
360 }
361
362 int64_t size = strtoll(value, nullptr, 10);
363
364 if (isaReconfig && *val != size) {
365 if (allowOptionReconfigure(option)) {
366 debugs(3, DBG_IMPORTANT, "cache_dir '" << path << "' object " <<
367 option << " now " << size << " Bytes");
368 } else {
369 debugs(3, DBG_IMPORTANT, "WARNING: cache_dir '" << path << "' "
370 "object " << option << " cannot be changed dynamically, " <<
371 "value left unchanged (" << *val << " Bytes)");
372 return true;
373 }
374 }
375
376 *val = size;
377
378 return true;
379 }
380
381 void
382 Store::Disk::optionObjectSizeDump(StoreEntry * e) const
383 {
384 if (min_objsize != -1)
385 storeAppendPrintf(e, " min-size=%" PRId64, min_objsize);
386
387 if (max_objsize != -1)
388 storeAppendPrintf(e, " max-size=%" PRId64, max_objsize);
389 }
390
391 // some SwapDirs may maintain their indexes and be able to lookup an entry key
392 StoreEntry *
393 Store::Disk::get(const cache_key *)
394 {
395 return nullptr;
396 }
397