fs_io.cc
Go to the documentation of this file.
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 06 Disk I/O Routines */
10
11#include "squid.h"
12#include "comm/Loops.h"
13#include "fd.h"
14#include "fde.h"
15#include "fs_io.h"
16#include "globals.h"
17#include "MemBuf.h"
18#include "StatCounters.h"
19
20#include <cerrno>
21
24
25#if _SQUID_WINDOWS_ || _SQUID_OS2_
26static int
27diskWriteIsComplete(int fd)
28{
29 return fd_table[fd].disk.write_q ? 0 : 1;
30}
31
32#endif
33
34/* hack needed on SunStudio to avoid linkage convention mismatch */
35static void cxx_xfree(void *ptr)
36{
37 xfree(ptr);
38}
39
40/*
41 * opens a disk file specified by 'path'. This function always
42 * blocks! There is no callback.
43 */
44int
45file_open(const char *path, int mode)
46{
47 int fd;
48
49 if (FILE_MODE(mode) == O_WRONLY)
50 mode |= O_APPEND;
51
52 errno = 0;
53
54 fd = open(path, mode, 0644);
55
57
58 if (fd < 0) {
59 int xerrno = errno;
60 debugs(50, 3, "error opening file " << path << ": " << xstrerr(xerrno));
61 fd = DISK_ERROR;
62 } else {
63 debugs(6, 5, "FD " << fd);
65 fd_open(fd, FD_FILE, path);
66 }
67
68 return fd;
69}
70
71/* close a disk file. */
72void
74{
75 fde *F = &fd_table[fd];
76 PF *read_callback;
77 assert(fd >= 0);
78 assert(F->flags.open);
79
80 if ((read_callback = F->read_handler)) {
81 F->read_handler = nullptr;
82 read_callback(-1, F->read_data);
83 }
84
85 if (F->flags.write_daemon) {
86#if _SQUID_WINDOWS_ || _SQUID_OS2_
87 /*
88 * on some operating systems, you can not delete or rename
89 * open files, so we won't allow delayed close.
90 */
91 while (!diskWriteIsComplete(fd))
92 diskHandleWrite(fd, nullptr);
93#else
94 F->flags.close_request = true;
95 debugs(6, 2, "file_close: FD " << fd << ", delaying close");
96 return;
97#endif
98
99 }
100
101 /*
102 * Assert there is no write callback. Otherwise we might be
103 * leaking write state data by closing the descriptor
104 */
105 assert(F->write_handler == nullptr);
106
107 close(fd);
108
109 debugs(6, F->flags.close_request ? 2 : 5, "file_close: FD " << fd << " really closing");
110
111 fd_close(fd);
112
114}
115
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 */
126static void
128{
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 */
134
135 if (fdd->write_q != nullptr && fdd->write_q->next != nullptr) {
136 int len = 0;
137
138 for (dwrite_q *q = fdd->write_q; q != nullptr; q = q->next)
139 len += q->len - q->buf_offset;
140
142
143 wq->buf = (char *)xmalloc(len);
144
145 wq->len = 0;
146
147 wq->buf_offset = 0;
148
149 wq->next = nullptr;
150
151 wq->free_func = cxx_xfree;
152
153 while (fdd->write_q != nullptr) {
154 dwrite_q *q = fdd->write_q;
155
156 len = q->len - q->buf_offset;
157 memcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
158 wq->len += len;
159 fdd->write_q = q->next;
160
161 if (q->free_func)
162 q->free_func(q->buf);
163
165 };
166
167 fdd->write_q_tail = wq;
168
169 fdd->write_q = wq;
170 }
171}
172
173/* write handler */
174static void
175diskHandleWrite(int fd, void *)
176{
177 int len = 0;
178 fde *F = &fd_table[fd];
179
180 _fde_disk *fdd = &F->disk;
181 dwrite_q *q = fdd->write_q;
182 int status = DISK_OK;
183 bool do_close;
184
185 if (nullptr == q)
186 return;
187
188 debugs(6, 3, "diskHandleWrite: FD " << fd);
189
190 F->flags.write_daemon = false;
191
192 assert(fdd->write_q != nullptr);
193
194 assert(fdd->write_q->len > fdd->write_q->buf_offset);
195
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);
199
200 errno = 0;
201
202 if (fdd->write_q->file_offset != -1) {
203 errno = 0;
204 if (lseek(fd, fdd->write_q->file_offset, SEEK_SET) == -1) {
205 int xerrno = errno;
206 debugs(50, DBG_IMPORTANT, "ERROR: in seek for FD " << fd << ": " << xstrerr(xerrno));
207 // XXX: handle error?
208 }
209 }
210
211 len = FD_WRITE_METHOD(fd,
212 fdd->write_q->buf + fdd->write_q->buf_offset,
213 fdd->write_q->len - fdd->write_q->buf_offset);
214 const auto xerrno = errno;
215
216 debugs(6, 3, "diskHandleWrite: FD " << fd << " len = " << len);
217
219
220 fd_bytes(fd, len, FD_WRITE);
221
222 if (len < 0) {
223 if (!ignoreErrno(xerrno)) {
224 status = xerrno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
225 debugs(50, DBG_IMPORTANT, "ERROR: diskHandleWrite: FD " << fd << ": disk write failure: " << xstrerr(xerrno));
226
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
237 if (fdd->wrt_handle == nullptr)
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)
253 q->free_func(q->buf);
254
255 if (q) {
257 q = nullptr;
258 }
259 } while ((q = fdd->write_q));
260 }
261
262 len = 0;
263 }
264
265 if (q != nullptr) {
266 /* q might become NULL from write failure above */
267 q->buf_offset += len;
268
269 if (q->buf_offset > q->len)
270 debugs(50, DBG_IMPORTANT, "diskHandleWriteComplete: q->buf_offset > q->len (" <<
271 q << "," << (int) q->buf_offset << ", " << q->len << ", " <<
272 len << " FD " << fd << ")");
273
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)
281 q->free_func(q->buf);
282
283 if (q) {
285 q = nullptr;
286 }
287 }
288 }
289
290 if (fdd->write_q == nullptr) {
291 /* no more data */
292 fdd->write_q_tail = nullptr;
293 } else {
294 /* another block is queued */
297 F->flags.write_daemon = true;
298 }
299
300 do_close = F->flags.close_request;
301
302 if (fdd->wrt_handle) {
303 DWCB *callback = fdd->wrt_handle;
304 void *cbdata;
305 fdd->wrt_handle = nullptr;
306
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 */
313 return;
314 /* XXX But what about close_request??? */
315 }
316 }
317
318 if (do_close)
319 file_close(fd);
320}
321
322/* write block to a file */
323/* write back queue. Only one writer at a time. */
324/* call a handle when writing is complete. */
325void
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)
333{
334 dwrite_q *wq = nullptr;
335 fde *F = &fd_table[fd];
336 assert(fd >= 0);
337 assert(F->flags.open);
338 /* if we got here. Caller is eligible to write. */
340 wq->file_offset = file_offset;
341 wq->buf = (char *)ptr_to_buf;
342 wq->len = len;
343 wq->buf_offset = 0;
344 wq->next = nullptr;
345 wq->free_func = free_func;
346
347 if (!F->disk.wrt_handle_data) {
348 F->disk.wrt_handle = handle;
349 F->disk.wrt_handle_data = cbdataReference(handle_data);
350 } else {
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);
353 }
354
355 /* add to queue */
356 if (F->disk.write_q == nullptr) {
357 /* empty queue */
358 F->disk.write_q = F->disk.write_q_tail = wq;
359 } else {
360 F->disk.write_q_tail->next = wq;
361 F->disk.write_q_tail = wq;
362 }
363
364 if (!F->flags.write_daemon) {
365 diskHandleWrite(fd, nullptr);
366 }
367}
368
369/*
370 * a wrapper around file_write to allow for MemBuf to be file_written
371 * in a snap
372 */
373void
374file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
375{
376 file_write(fd, off, mb.buf, mb.size, handler, handler_data, mb.freeFunc());
377}
378
379/* Read from FD */
380static void
381diskHandleRead(int fd, void *data)
382{
383 dread_ctrl *ctrl_dat = (dread_ctrl *)data;
384 fde *F = &fd_table[fd];
385 int len;
386 int rc = DISK_OK;
387 int xerrno;
388
389 /*
390 * FD < 0 indicates premature close; we just have to free
391 * the state data.
392 */
393
394 if (fd < 0) {
395 memFree(ctrl_dat, MEM_DREAD_CTRL);
396 return;
397 }
398
399#if WRITES_MAINTAIN_DISK_OFFSET
400 if (F->disk.offset != ctrl_dat->offset) {
401#else
402 {
403#endif
404 debugs(6, 3, "diskHandleRead: FD " << fd << " seeking to offset " << ctrl_dat->offset);
405 errno = 0;
406 if (lseek(fd, ctrl_dat->offset, SEEK_SET) == -1) {
407 xerrno = errno;
408 // shouldn't happen, let's detect that
409 debugs(50, DBG_IMPORTANT, "ERROR: in seek for FD " << fd << ": " << xstrerr(xerrno));
410 // XXX handle failures?
411 }
413 F->disk.offset = ctrl_dat->offset;
414 }
415
416 errno = 0;
417 len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
418 xerrno = errno;
419
420 if (len > 0)
421 F->disk.offset += len;
422
424
425 fd_bytes(fd, len, FD_READ);
426
427 if (len < 0) {
428 if (ignoreErrno(xerrno)) {
430 return;
431 }
432
433 debugs(50, DBG_IMPORTANT, "diskHandleRead: FD " << fd << ": " << xstrerr(xerrno));
434 len = 0;
435 rc = DISK_ERROR;
436 } else if (len == 0) {
437 rc = DISK_EOF;
438 }
439
440 if (cbdataReferenceValid(ctrl_dat->client_data))
441 ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
442
444
445 memFree(ctrl_dat, MEM_DREAD_CTRL);
446}
447
448/* start read operation */
449/* buffer must be allocated from the caller.
450 * It must have at least req_len space in there.
451 * call handler when a reading is complete. */
452void
453file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
454{
455 dread_ctrl *ctrl_dat;
456 assert(fd >= 0);
458 ctrl_dat->fd = fd;
459 ctrl_dat->offset = offset;
460 ctrl_dat->req_len = req_len;
461 ctrl_dat->buf = buf;
462 ctrl_dat->end_of_file = 0;
463 ctrl_dat->handler = handler;
464 ctrl_dat->client_data = cbdataReference(client_data);
465 diskHandleRead(fd, ctrl_dat);
466}
467
468void
469safeunlink(const char *s, int quiet)
470{
472
473 if (unlink(s) < 0 && !quiet) {
474 int xerrno = errno;
475 debugs(50, DBG_IMPORTANT, "ERROR: safeunlink: Could not delete " << s << ": " << xstrerr(xerrno));
476 }
477}
478
479bool
480FileRename(const SBuf &from, const SBuf &to)
481{
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
489#if _SQUID_OS2_ || _SQUID_WINDOWS_
490 remove(to2.c_str());
491#endif
492
493 if (rename(from2.c_str(), to2.c_str()) == 0)
494 return true;
495
496 int xerrno = errno;
497 debugs(21, (xerrno == ENOENT ? 2 : DBG_IMPORTANT), "ERROR: Cannot rename " << from << " to " << to << ": " << xstrerr(xerrno));
498
499 return false;
500}
501
502int
503fsBlockSize(const char *path, int *blksize)
504{
505 struct statvfs sfs;
506
507 if (xstatvfs(path, &sfs)) {
508 int xerrno = errno;
509 debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
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)) {
532 int xerrno = errno;
533 debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
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}
543
StatCounters statCounter
Definition: StatCounters.cc:12
#define assert(EX)
Definition: assert.h:17
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:265
#define cbdataReferenceDone(var)
Definition: cbdata.h:352
#define cbdataReference(var)
Definition: cbdata.h:343
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
Definition: MemBuf.h:24
FREE * freeFunc()
Definition: MemBuf.cc:303
mb_size_t size
Definition: MemBuf.h:135
char * buf
Definition: MemBuf.h:134
Definition: SBuf.h:94
const char * rawContent() const
Definition: SBuf.cc:509
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
struct StatCounters::@130 syscalls
struct StatCounters::@130::@134 disk
Definition: fde.h:40
DWCB * wrt_handle
Definition: fde.h:44
dwrite_q * write_q
Definition: fde.h:46
void * wrt_handle_data
Definition: fde.h:45
dwrite_q * write_q_tail
Definition: fde.h:47
Definition: cbdata.cc:38
int req_len
Definition: fs_io.h:26
void * client_data
Definition: fs_io.h:30
DRCB * handler
Definition: fs_io.h:29
off_t offset
Definition: fs_io.h:25
int fd
Definition: fs_io.h:24
char * buf
Definition: fs_io.h:27
int end_of_file
Definition: fs_io.h:28
Definition: fs_io.h:35
char * buf
Definition: fs_io.h:38
size_t buf_offset
Definition: fs_io.h:40
size_t len
Definition: fs_io.h:39
dwrite_q * next
Definition: fs_io.h:41
off_t file_offset
Definition: fs_io.h:37
FREE * free_func
Definition: fs_io.h:42
Definition: fde.h:52
void PF(int, void *)
Definition: forward.h:18
void fd_open(const int fd, unsigned int, const char *description)
Definition: minimal.cc:14
void fd_close(const int fd)
Definition: minimal.cc:20
void commSetCloseOnExec(int fd)
Definition: comm.cc:1127
int ignoreErrno(int ierrno)
Definition: comm.cc:1440
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define COMM_SELECT_READ
Definition: defines.h:24
#define COMM_SELECT_WRITE
Definition: defines.h:25
#define FILE_MODE(x)
Definition: defines.h:145
#define DISK_EOF
Definition: defines.h:29
#define DISK_NO_SPACE_LEFT
Definition: defines.h:30
#define DISK_ERROR
Definition: defines.h:28
#define DISK_OK
Definition: defines.h:27
static int do_close(diomsg *r, int)
Definition: diskd.cc:88
@ FD_FILE
Definition: enums.h:15
@ FD_WRITE
Definition: enums.h:24
@ FD_READ
Definition: enums.h:23
void fatal(const char *message)
Definition: fatal.cc:28
void fd_bytes(int fd, int len, unsigned int type)
Definition: fd.cc:226
#define fd_table
Definition: fde.h:189
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
int FD_WRITE_METHOD(int fd, const char *buf, int len)
Definition: fde.h:200
int file_open(const char *path, int mode)
Definition: fs_io.cc:45
#define fsbtoblk(num, fsbs, bs)
Definition: fs_io.cc:523
static void diskCombineWrites(_fde_disk *fdd)
Definition: fs_io.cc:127
bool FileRename(const SBuf &from, const SBuf &to)
Definition: fs_io.cc:480
void safeunlink(const char *s, int quiet)
Definition: fs_io.cc:469
void file_read(int fd, char *buf, int req_len, off_t offset, DRCB *handler, void *client_data)
Definition: fs_io.cc:453
static PF diskHandleRead
Definition: fs_io.cc:22
int fsBlockSize(const char *path, int *blksize)
Definition: fs_io.cc:503
static PF diskHandleWrite
Definition: fs_io.cc:23
static void cxx_xfree(void *ptr)
Definition: fs_io.cc:35
void file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB *handler, void *handler_data)
Definition: fs_io.cc:374
void file_close(int fd)
Definition: fs_io.cc:73
int fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
Definition: fs_io.cc:527
void file_write(int fd, off_t file_offset, void const *ptr_to_buf, int len, DWCB *handle, void *handle_data, FREE *free_func)
Definition: fs_io.cc:326
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
void FREE(void *)
Definition: forward.h:37
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: minimal.cc:60
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition: old_api.cc:213
@ MEM_DWRITE_Q
Definition: forward.h:49
@ MEM_DREAD_CTRL
Definition: forward.h:48
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:223
#define xfree
#define xmalloc
SMB_Handle_Type handle
static void handler(int signo)
Definition: purge.cc:858
int xstatvfs(const char *path, struct statvfs *sfs)
Definition: statvfs.cc:22
fsfilcnt_t f_files
Definition: statvfs.h:49
fsfilcnt_t f_ffree
Definition: statvfs.h:50
fsblkcnt_t f_blocks
Definition: statvfs.h:46
fsblkcnt_t f_bfree
Definition: statvfs.h:47
unsigned long f_frsize
Definition: statvfs.h:45
int unsigned int
Definition: stub_fd.cc:19
void DRCB(int, const char *buf, int size, int errflag, void *data)
Definition: typedefs.h:16
void DWCB(int, int, size_t, void *)
Definition: typedefs.h:18
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors