]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs_io.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / fs_io.cc
CommitLineData
30a4f2a8 1/*
bf95c10a 2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
30a4f2a8 7 */
ed43818f 8
bbc27441
AJ
9/* DEBUG: section 06 Disk I/O Routines */
10
582c2af2 11#include "squid.h"
d841c88d 12#include "comm/Loops.h"
c4ad1349 13#include "fd.h"
528b2c61 14#include "fde.h"
b3f7fd88 15#include "fs_io.h"
67679543 16#include "globals.h"
0eb49b6d 17#include "MemBuf.h"
e4f1fdae 18#include "StatCounters.h"
090089c4 19
1a30fdf5 20#include <cerrno>
21d845b1 21
95d15928 22static PF diskHandleRead;
23static PF diskHandleWrite;
24382924 24
be266cb2 25#if _SQUID_WINDOWS_ || _SQUID_OS2_
1a94f598 26static int
27diskWriteIsComplete(int fd)
28{
29 return fd_table[fd].disk.write_q ? 0 : 1;
30}
62e76326 31
1a94f598 32#endif
33
59a09b98
FC
34/* hack needed on SunStudio to avoid linkage convention mismatch */
35static void cxx_xfree(void *ptr)
36{
f673997d 37 xfree(ptr);
59a09b98
FC
38}
39
1a94f598 40/*
41 * opens a disk file specified by 'path'. This function always
42 * blocks! There is no callback.
43 */
684c2720 44int
1a94f598 45file_open(const char *path, int mode)
090089c4 46{
090089c4 47 int fd;
62e76326 48
5d1a7121 49 if (FILE_MODE(mode) == O_WRONLY)
62e76326 50 mode |= O_APPEND;
51
b870e0b4 52 errno = 0;
62e76326 53
0a0bf5db 54 fd = open(path, mode, 0644);
62e76326 55
95dc7ff4 56 ++ statCounter.syscalls.disk.opens;
62e76326 57
0a0bf5db 58 if (fd < 0) {
b69e9ffa
AJ
59 int xerrno = errno;
60 debugs(50, 3, "error opening file " << path << ": " << xstrerr(xerrno));
62e76326 61 fd = DISK_ERROR;
2391a162 62 } else {
b69e9ffa 63 debugs(6, 5, "FD " << fd);
62e76326 64 commSetCloseOnExec(fd);
65 fd_open(fd, FD_FILE, path);
090089c4 66 }
62e76326 67
2391a162 68 return fd;
0a0bf5db 69}
70
090089c4 71/* close a disk file. */
95d15928 72void
b8d8561b 73file_close(int fd)
090089c4 74{
76f87348 75 fde *F = &fd_table[fd];
2391a162 76 PF *read_callback;
25354045 77 assert(fd >= 0);
60c0b5a2 78 assert(F->flags.open);
62e76326 79
2391a162 80 if ((read_callback = F->read_handler)) {
aee3523a 81 F->read_handler = nullptr;
62e76326 82 read_callback(-1, F->read_data);
65d548bf 83 }
62e76326 84
0cd30ba5 85 if (F->flags.write_daemon) {
be266cb2 86#if _SQUID_WINDOWS_ || _SQUID_OS2_
62e76326 87 /*
88 * on some operating systems, you can not delete or rename
89 * open files, so we won't allow delayed close.
90 */
62e76326 91 while (!diskWriteIsComplete(fd))
92 diskHandleWrite(fd, NULL);
cd377065 93#else
be4d35dc 94 F->flags.close_request = true;
bf8fe701 95 debugs(6, 2, "file_close: FD " << fd << ", delaying close");
62e76326 96 return;
cd377065 97#endif
62e76326 98
fb247d78 99 }
62e76326 100
65d548bf 101 /*
102 * Assert there is no write callback. Otherwise we might be
103 * leaking write state data by closing the descriptor
104 */
aee3523a 105 assert(F->write_handler == nullptr);
62e76326 106
95d15928 107 close(fd);
62e76326 108
34492237 109 debugs(6, F->flags.close_request ? 2 : 5, "file_close: FD " << fd << " really closing");
62e76326 110
6cf028ab 111 fd_close(fd);
62e76326 112
95dc7ff4 113 ++ statCounter.syscalls.disk.closes;
090089c4 114}
115
f02b8498 116/*
117 * This function has the purpose of combining multiple writes. This is
118 * to facilitate the ASYNC_IO option since it can only guarantee 1
119 * write to a file per trip around the comm.c select() loop. That's bad
120 * because more than 1 write can be made to the access.log file per
121 * trip, and so this code is purely designed to help batch multiple
122 * sequential writes to the access.log file. Squid will never issue
123 * multiple writes for any other file type during 1 trip around the
124 * select() loop. --SLF
125 */
582b6456 126static void
5fed1735 127diskCombineWrites(_fde_disk *fdd)
090089c4 128{
f02b8498 129 /*
130 * We need to combine multiple write requests on an FD's write
131 * queue But only if we don't need to seek() in between them, ugh!
132 * XXX This currently ignores any seeks (file_offset)
133 */
62e76326 134
aee3523a 135 if (fdd->write_q != nullptr && fdd->write_q->next != nullptr) {
b115733c 136 int len = 0;
62e76326 137
aee3523a 138 for (dwrite_q *q = fdd->write_q; q != nullptr; q = q->next)
62e76326 139 len += q->len - q->buf_offset;
140
b115733c 141 dwrite_q *wq = (dwrite_q *)memAllocate(MEM_DWRITE_Q);
62e76326 142
143 wq->buf = (char *)xmalloc(len);
144
145 wq->len = 0;
146
147 wq->buf_offset = 0;
148
aee3523a 149 wq->next = nullptr;
62e76326 150
59a09b98 151 wq->free_func = cxx_xfree;
62e76326 152
aee3523a 153 while (fdd->write_q != nullptr) {
b115733c
AJ
154 dwrite_q *q = fdd->write_q;
155
62e76326 156 len = q->len - q->buf_offset;
41d00cd3 157 memcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
62e76326 158 wq->len += len;
159 fdd->write_q = q->next;
160
161 if (q->free_func)
6ca34f6f 162 q->free_func(q->buf);
62e76326 163
b115733c
AJ
164 memFree(q, MEM_DWRITE_Q);
165 };
62e76326 166
167 fdd->write_q_tail = wq;
168
169 fdd->write_q = wq;
0a0bf5db 170 }
f02b8498 171}
172
173/* write handler */
174static void
ced8def3 175diskHandleWrite(int fd, void *)
f02b8498 176{
177 int len = 0;
f02b8498 178 fde *F = &fd_table[fd];
62e76326 179
5fed1735 180 _fde_disk *fdd = &F->disk;
2391a162 181 dwrite_q *q = fdd->write_q;
182 int status = DISK_OK;
be4d35dc 183 bool do_close;
62e76326 184
aee3523a 185 if (nullptr == q)
62e76326 186 return;
187
bf8fe701 188 debugs(6, 3, "diskHandleWrite: FD " << fd);
62e76326 189
be4d35dc 190 F->flags.write_daemon = false;
62e76326 191
aee3523a 192 assert(fdd->write_q != nullptr);
62e76326 193
d377699f 194 assert(fdd->write_q->len > fdd->write_q->buf_offset);
62e76326 195
e2851fe7
AR
196 debugs(6, 3, "diskHandleWrite: FD " << fd << " writing " <<
197 (fdd->write_q->len - fdd->write_q->buf_offset) << " bytes at " <<
198 fdd->write_q->file_offset);
62e76326 199
b870e0b4 200 errno = 0;
62e76326 201
914fbbba
FC
202 if (fdd->write_q->file_offset != -1) {
203 errno = 0;
204 if (lseek(fd, fdd->write_q->file_offset, SEEK_SET) == -1) {
b69e9ffa 205 int xerrno = errno;
d816f28d 206 debugs(50, DBG_IMPORTANT, "ERROR: in seek for FD " << fd << ": " << xstrerr(xerrno));
914fbbba
FC
207 // XXX: handle error?
208 }
209 }
62e76326 210
1f7c9178 211 len = FD_WRITE_METHOD(fd,
62e76326 212 fdd->write_q->buf + fdd->write_q->buf_offset,
213 fdd->write_q->len - fdd->write_q->buf_offset);
214
bf8fe701 215 debugs(6, 3, "diskHandleWrite: FD " << fd << " len = " << len);
62e76326 216
95dc7ff4 217 ++ statCounter.syscalls.disk.writes;
62e76326 218
6cf028ab 219 fd_bytes(fd, len, FD_WRITE);
62e76326 220
0a0bf5db 221 if (len < 0) {
62e76326 222 if (!ignoreErrno(errno)) {
223 status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
b69e9ffa 224 int xerrno = errno;
d816f28d 225 debugs(50, DBG_IMPORTANT, "ERROR: diskHandleWrite: FD " << fd << ": disk write failure: " << xstrerr(xerrno));
bf8fe701 226
62e76326 227 /*
228 * If there is no write callback, then this file is
229 * most likely something important like a log file, or
230 * an interprocess pipe. Its not a swapfile. We feel
231 * that a write failure on a log file is rather important,
232 * and Squid doesn't otherwise deal with this condition.
233 * So to get the administrators attention, we exit with
234 * a fatal message.
235 */
236
aee3523a 237 if (fdd->wrt_handle == nullptr)
62e76326 238 fatal("Write failure -- check your disk space and cache.log");
239
240 /*
241 * If there is a write failure, then we notify the
242 * upper layer via the callback, at the end of this
243 * function. Meanwhile, flush all pending buffers
244 * here. Let the upper layer decide how to handle the
245 * failure. This will prevent experiencing multiple,
246 * repeated write failures for the same FD because of
247 * the queued data.
248 */
249 do {
250 fdd->write_q = q->next;
251
252 if (q->free_func)
6ca34f6f 253 q->free_func(q->buf);
62e76326 254
255 if (q) {
256 memFree(q, MEM_DWRITE_Q);
aee3523a 257 q = nullptr;
62e76326 258 }
259 } while ((q = fdd->write_q));
260 }
261
262 len = 0;
0a0bf5db 263 }
62e76326 264
aee3523a 265 if (q != nullptr) {
62e76326 266 /* q might become NULL from write failure above */
267 q->buf_offset += len;
268
269 if (q->buf_offset > q->len)
e0236918 270 debugs(50, DBG_IMPORTANT, "diskHandleWriteComplete: q->buf_offset > q->len (" <<
bf8fe701 271 q << "," << (int) q->buf_offset << ", " << q->len << ", " <<
272 len << " FD " << fd << ")");
273
62e76326 274 assert(q->buf_offset <= q->len);
275
276 if (q->buf_offset == q->len) {
277 /* complete write */
278 fdd->write_q = q->next;
279
280 if (q->free_func)
6ca34f6f 281 q->free_func(q->buf);
62e76326 282
283 if (q) {
284 memFree(q, MEM_DWRITE_Q);
aee3523a 285 q = nullptr;
62e76326 286 }
287 }
090089c4 288 }
62e76326 289
aee3523a 290 if (fdd->write_q == nullptr) {
62e76326 291 /* no more data */
aee3523a 292 fdd->write_q_tail = nullptr;
de866d20 293 } else {
62e76326 294 /* another block is queued */
295 diskCombineWrites(fdd);
aee3523a 296 Comm::SetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, nullptr, 0);
be4d35dc 297 F->flags.write_daemon = true;
4a86108c 298 }
62e76326 299
0cd30ba5 300 do_close = F->flags.close_request;
62e76326 301
25354045 302 if (fdd->wrt_handle) {
62e76326 303 DWCB *callback = fdd->wrt_handle;
304 void *cbdata;
aee3523a 305 fdd->wrt_handle = nullptr;
62e76326 306
307 if (cbdataReferenceValidDone(fdd->wrt_handle_data, &cbdata)) {
308 callback(fd, status, len, cbdata);
309 /*
310 * NOTE, this callback can close the FD, so we must
311 * not touch 'F', 'fdd', etc. after this.
312 */
62e76326 313 return;
314 /* XXX But what about close_request??? */
315 }
25354045 316 }
62e76326 317
68c21f71 318 if (do_close)
62e76326 319 file_close(fd);
090089c4 320}
321
090089c4 322/* write block to a file */
323/* write back queue. Only one writer at a time. */
324/* call a handle when writing is complete. */
e3ef2b09 325void
3ebcfaa1 326file_write(int fd,
62e76326 327 off_t file_offset,
328 void const *ptr_to_buf,
329 int len,
330 DWCB * handle,
331 void *handle_data,
332 FREE * free_func)
090089c4 333{
aee3523a 334 dwrite_q *wq = nullptr;
48cc3fcf 335 fde *F = &fd_table[fd];
336 assert(fd >= 0);
60c0b5a2 337 assert(F->flags.open);
090089c4 338 /* if we got here. Caller is eligible to write. */
e6ccf245 339 wq = (dwrite_q *)memAllocate(MEM_DWRITE_Q);
d377699f 340 wq->file_offset = file_offset;
e6ccf245 341 wq->buf = (char *)ptr_to_buf;
090089c4 342 wq->len = len;
d377699f 343 wq->buf_offset = 0;
aee3523a 344 wq->next = nullptr;
ed7f0b6a 345 wq->free_func = free_func;
62e76326 346
fa80a8ef 347 if (!F->disk.wrt_handle_data) {
62e76326 348 F->disk.wrt_handle = handle;
349 F->disk.wrt_handle_data = cbdataReference(handle_data);
fa80a8ef 350 } else {
62e76326 351 /* Detect if there is multiple concurrent users of this fd.. we only support one callback */
352 assert(F->disk.wrt_handle_data == handle_data && F->disk.wrt_handle == handle);
fa80a8ef 353 }
62e76326 354
090089c4 355 /* add to queue */
aee3523a 356 if (F->disk.write_q == nullptr) {
62e76326 357 /* empty queue */
358 F->disk.write_q = F->disk.write_q_tail = wq;
090089c4 359 } else {
62e76326 360 F->disk.write_q_tail->next = wq;
361 F->disk.write_q_tail = wq;
090089c4 362 }
62e76326 363
0cd30ba5 364 if (!F->flags.write_daemon) {
aee3523a 365 diskHandleWrite(fd, nullptr);
429fdbec 366 }
090089c4 367}
368
23b2b404 369/*
370 * a wrapper around file_write to allow for MemBuf to be file_written
371 * in a snap
372 */
137ee196 373void
374file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
375{
2fe7eff9 376 file_write(fd, off, mb.buf, mb.size, handler, handler_data, mb.freeFunc());
137ee196 377}
090089c4 378
379/* Read from FD */
582b6456 380static void
381diskHandleRead(int fd, void *data)
090089c4 382{
e6ccf245 383 dread_ctrl *ctrl_dat = (dread_ctrl *)data;
edd2eb63 384 fde *F = &fd_table[fd];
090089c4 385 int len;
2391a162 386 int rc = DISK_OK;
b69e9ffa
AJ
387 int xerrno;
388
65d548bf 389 /*
390 * FD < 0 indicates premature close; we just have to free
391 * the state data.
392 */
62e76326 393
65d548bf 394 if (fd < 0) {
62e76326 395 memFree(ctrl_dat, MEM_DREAD_CTRL);
396 return;
65d548bf 397 }
62e76326 398
034b5ea4 399#if WRITES_MAINTAIN_DISK_OFFSET
711982d8 400 if (F->disk.offset != ctrl_dat->offset) {
034b5ea4
AR
401#else
402 {
403#endif
4a7a3d56 404 debugs(6, 3, "diskHandleRead: FD " << fd << " seeking to offset " << ctrl_dat->offset);
4ab1af1f
FC
405 errno = 0;
406 if (lseek(fd, ctrl_dat->offset, SEEK_SET) == -1) {
b69e9ffa 407 xerrno = errno;
4ab1af1f 408 // shouldn't happen, let's detect that
d816f28d 409 debugs(50, DBG_IMPORTANT, "ERROR: in seek for FD " << fd << ": " << xstrerr(xerrno));
4ab1af1f
FC
410 // XXX handle failures?
411 }
95dc7ff4 412 ++ statCounter.syscalls.disk.seeks;
62e76326 413 F->disk.offset = ctrl_dat->offset;
711982d8 414 }
62e76326 415
b870e0b4 416 errno = 0;
1f7c9178 417 len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
b69e9ffa 418 xerrno = errno;
62e76326 419
015b507a 420 if (len > 0)
62e76326 421 F->disk.offset += len;
422
95dc7ff4 423 ++ statCounter.syscalls.disk.reads;
62e76326 424
4f92c80c 425 fd_bytes(fd, len, FD_READ);
62e76326 426
0a0bf5db 427 if (len < 0) {
b69e9ffa 428 if (ignoreErrno(xerrno)) {
d841c88d 429 Comm::SetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
62e76326 430 return;
431 }
432
b69e9ffa 433 debugs(50, DBG_IMPORTANT, "diskHandleRead: FD " << fd << ": " << xstrerr(xerrno));
62e76326 434 len = 0;
435 rc = DISK_ERROR;
090089c4 436 } else if (len == 0) {
62e76326 437 rc = DISK_EOF;
090089c4 438 }
62e76326 439
fa80a8ef 440 if (cbdataReferenceValid(ctrl_dat->client_data))
62e76326 441 ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
442
fa80a8ef 443 cbdataReferenceDone(ctrl_dat->client_data);
62e76326 444
db1cd23c 445 memFree(ctrl_dat, MEM_DREAD_CTRL);
090089c4 446}
447
090089c4 448/* start read operation */
62e76326 449/* buffer must be allocated from the caller.
26ac0430 450 * It must have at least req_len space in there.
090089c4 451 * call handler when a reading is complete. */
2391a162 452void
d377699f 453file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
090089c4 454{
455 dread_ctrl *ctrl_dat;
711982d8 456 assert(fd >= 0);
e6ccf245 457 ctrl_dat = (dread_ctrl *)memAllocate(MEM_DREAD_CTRL);
090089c4 458 ctrl_dat->fd = fd;
459 ctrl_dat->offset = offset;
460 ctrl_dat->req_len = req_len;
461 ctrl_dat->buf = buf;
090089c4 462 ctrl_dat->end_of_file = 0;
463 ctrl_dat->handler = handler;
fa80a8ef 464 ctrl_dat->client_data = cbdataReference(client_data);
0a0bf5db 465 diskHandleRead(fd, ctrl_dat);
090089c4 466}
c8f4eac4 467
468void
469safeunlink(const char *s, int quiet)
470{
95dc7ff4 471 ++ statCounter.syscalls.disk.unlinks;
c8f4eac4 472
b69e9ffa
AJ
473 if (unlink(s) < 0 && !quiet) {
474 int xerrno = errno;
d816f28d 475 debugs(50, DBG_IMPORTANT, "ERROR: safeunlink: Could not delete " << s << ": " << xstrerr(xerrno));
b69e9ffa 476 }
c8f4eac4 477}
478
b56b37cf
AJ
479bool
480FileRename(const SBuf &from, const SBuf &to)
c8f4eac4 481{
b56b37cf
AJ
482 debugs(21, 2, "renaming " << from << " to " << to);
483
484 // non-const copy for c_str()
485 SBuf from2(from);
486 // ensure c_str() lifetimes even if `to` and `from` share memory
487 SBuf to2(to.rawContent(), to.length());
488
be266cb2 489#if _SQUID_OS2_ || _SQUID_WINDOWS_
b56b37cf 490 remove(to2.c_str());
c8f4eac4 491#endif
492
b56b37cf
AJ
493 if (rename(from2.c_str(), to2.c_str()) == 0)
494 return true;
c8f4eac4 495
b69e9ffa 496 int xerrno = errno;
d816f28d 497 debugs(21, (errno == ENOENT ? 2 : DBG_IMPORTANT), "ERROR: Cannot rename " << from << " to " << to << ": " << xstrerr(xerrno));
c8f4eac4 498
b56b37cf 499 return false;
c8f4eac4 500}
501
2745fea5
AR
502int
503fsBlockSize(const char *path, int *blksize)
504{
505 struct statvfs sfs;
506
507 if (xstatvfs(path, &sfs)) {
b69e9ffa
AJ
508 int xerrno = errno;
509 debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
2745fea5
AR
510 *blksize = 2048;
511 return 1;
512 }
513
514 *blksize = (int) sfs.f_frsize;
515
516 // Sanity check; make sure we have a meaningful value.
517 if (*blksize < 512)
518 *blksize = 2048;
519
520 return 0;
521}
522
523#define fsbtoblk(num, fsbs, bs) \
524 (((fsbs) != 0 && (fsbs) < (bs)) ? \
525 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
526int
527fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
528{
529 struct statvfs sfs;
530
531 if (xstatvfs(path, &sfs)) {
b69e9ffa
AJ
532 int xerrno = errno;
533 debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
2745fea5
AR
534 return 1;
535 }
536
537 *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024);
538 *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_frsize, 1024);
539 *totl_in = (int) sfs.f_files;
540 *free_in = (int) sfs.f_ffree;
541 return 0;
542}
7d84d4ca 543