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_
26 static int
27 diskWriteIsComplete(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 */
35 static void cxx_xfree(void *ptr)
36 {
37  xfree(ptr);
38 }
39 
40 dwrite_q::dwrite_q(const size_t aSize, char * const aBuffer, FREE * const aFree):
41  buf(aBuffer),
42  capacity(aSize),
43  free_func(aFree)
44 {
45  assert(buf || !free_func);
46  if (!buf) {
47  buf = static_cast<char *>(xmalloc(aSize));
48  free_func = cxx_xfree; // dwrite_q buffer xfree()
49  } else {
50  len = aSize;
51  }
52 }
53 
55 {
56  if (free_func)
57  free_func(buf);
58 }
59 
60 /*
61  * opens a disk file specified by 'path'. This function always
62  * blocks! There is no callback.
63  */
64 int
65 file_open(const char *path, int mode)
66 {
67  int fd;
68 
69  if (FILE_MODE(mode) == O_WRONLY)
70  mode |= O_APPEND;
71 
72  errno = 0;
73 
74  fd = open(path, mode, 0644);
75 
76  ++ statCounter.syscalls.disk.opens;
77 
78  if (fd < 0) {
79  int xerrno = errno;
80  debugs(50, 3, "error opening file " << path << ": " << xstrerr(xerrno));
81  fd = DISK_ERROR;
82  } else {
83  debugs(6, 5, "FD " << fd);
85  fd_open(fd, FD_FILE, path);
86  }
87 
88  return fd;
89 }
90 
91 /* close a disk file. */
92 void
93 file_close(int fd)
94 {
95  fde *F = &fd_table[fd];
96  PF *read_callback;
97  assert(fd >= 0);
98  assert(F->flags.open);
99 
100  if ((read_callback = F->read_handler)) {
101  F->read_handler = nullptr;
102  read_callback(-1, F->read_data);
103  }
104 
105  if (F->flags.write_daemon) {
106 #if _SQUID_WINDOWS_ || _SQUID_OS2_
107  /*
108  * on some operating systems, you can not delete or rename
109  * open files, so we won't allow delayed close.
110  */
111  while (!diskWriteIsComplete(fd))
112  diskHandleWrite(fd, nullptr);
113 #else
114  F->flags.close_request = true;
115  debugs(6, 2, "file_close: FD " << fd << ", delaying close");
116  return;
117 #endif
118 
119  }
120 
121  /*
122  * Assert there is no write callback. Otherwise we might be
123  * leaking write state data by closing the descriptor
124  */
125  assert(F->write_handler == nullptr);
126 
127  close(fd);
128 
129  debugs(6, F->flags.close_request ? 2 : 5, "file_close: FD " << fd << " really closing");
130 
131  fd_close(fd);
132 
133  ++ statCounter.syscalls.disk.closes;
134 }
135 
136 /*
137  * This function has the purpose of combining multiple writes. This is
138  * to facilitate the ASYNC_IO option since it can only guarantee 1
139  * write to a file per trip around the comm.c select() loop. That's bad
140  * because more than 1 write can be made to the access.log file per
141  * trip, and so this code is purely designed to help batch multiple
142  * sequential writes to the access.log file. Squid will never issue
143  * multiple writes for any other file type during 1 trip around the
144  * select() loop. --SLF
145  */
146 static void
148 {
149  /*
150  * We need to combine multiple write requests on an FD's write
151  * queue But only if we don't need to seek() in between them, ugh!
152  * XXX This currently ignores any seeks (file_offset)
153  */
154 
155  if (fdd->write_q != nullptr && fdd->write_q->next != nullptr) {
156  size_t wantCapacity = 0;
157 
158  for (dwrite_q *q = fdd->write_q; q != nullptr; q = q->next)
159  wantCapacity += q->len - q->buf_offset; // XXX: might overflow
160 
161  const auto wq = new dwrite_q(wantCapacity);
162  while (const auto q = fdd->write_q) {
163  const auto len = q->len - q->buf_offset;
164  memcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
165  wq->len += len;
166  fdd->write_q = q->next;
167  delete q;
168  };
169 
170  fdd->write_q_tail = wq;
171 
172  fdd->write_q = wq;
173  }
174 }
175 
176 /* write handler */
177 static void
178 diskHandleWrite(int fd, void *)
179 {
180  int len = 0;
181  fde *F = &fd_table[fd];
182 
183  _fde_disk *fdd = &F->disk;
184  int status = DISK_OK;
185  bool do_close;
186 
187  if (!fdd->write_q)
188  return;
189 
190  debugs(6, 3, "diskHandleWrite: FD " << fd);
191 
192  F->flags.write_daemon = false;
193 
194  assert(fdd->write_q != nullptr);
195 
196  assert(fdd->write_q->len > fdd->write_q->buf_offset);
197 
198  debugs(6, 3, "diskHandleWrite: FD " << fd << " writing " <<
199  (fdd->write_q->len - fdd->write_q->buf_offset) << " bytes at " <<
200  fdd->write_q->file_offset);
201 
202  errno = 0;
203 
204  if (fdd->write_q->file_offset != -1) {
205  errno = 0;
206  if (lseek(fd, fdd->write_q->file_offset, SEEK_SET) == -1) {
207  int xerrno = errno;
208  debugs(50, DBG_IMPORTANT, "ERROR: in seek for FD " << fd << ": " << xstrerr(xerrno));
209  // XXX: handle error?
210  }
211  }
212 
213  len = FD_WRITE_METHOD(fd,
214  fdd->write_q->buf + fdd->write_q->buf_offset,
215  fdd->write_q->len - fdd->write_q->buf_offset);
216  const auto xerrno = errno;
217 
218  debugs(6, 3, "diskHandleWrite: FD " << fd << " len = " << len);
219 
220  ++ statCounter.syscalls.disk.writes;
221 
222  fd_bytes(fd, len, IoDirection::Write);
223 
224  if (len < 0) {
225  if (!ignoreErrno(xerrno)) {
226  status = xerrno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
227  debugs(50, DBG_IMPORTANT, "ERROR: diskHandleWrite: FD " << fd << ": disk write failure: " << xstrerr(xerrno));
228 
229  /*
230  * If there is no write callback, then this file is
231  * most likely something important like a log file, or
232  * an interprocess pipe. Its not a swapfile. We feel
233  * that a write failure on a log file is rather important,
234  * and Squid doesn't otherwise deal with this condition.
235  * So to get the administrators attention, we exit with
236  * a fatal message.
237  */
238 
239  if (fdd->wrt_handle == nullptr)
240  fatal("Write failure -- check your disk space and cache.log");
241 
242  /*
243  * If there is a write failure, then we notify the
244  * upper layer via the callback, at the end of this
245  * function. Meanwhile, flush all pending buffers
246  * here. Let the upper layer decide how to handle the
247  * failure. This will prevent experiencing multiple,
248  * repeated write failures for the same FD because of
249  * the queued data.
250  */
251  while (const auto q = fdd->write_q) {
252  fdd->write_q = q->next;
253  delete q;
254  }
255  }
256 
257  len = 0;
258  }
259 
260  if (const auto q = fdd->write_q) {
261  /* q might become NULL from write failure above */
262  q->buf_offset += len;
263 
264  if (q->buf_offset > q->len)
265  debugs(50, DBG_IMPORTANT, "diskHandleWriteComplete: q->buf_offset > q->len (" <<
266  q << "," << (int) q->buf_offset << ", " << q->len << ", " <<
267  len << " FD " << fd << ")");
268 
269  assert(q->buf_offset <= q->len);
270 
271  if (q->buf_offset == q->len) {
272  /* complete write */
273  fdd->write_q = q->next;
274  delete q;
275  }
276  }
277 
278  if (fdd->write_q == nullptr) {
279  /* no more data */
280  fdd->write_q_tail = nullptr;
281  } else {
282  /* another block is queued */
283  diskCombineWrites(fdd);
285  F->flags.write_daemon = true;
286  }
287 
288  do_close = F->flags.close_request;
289 
290  if (fdd->wrt_handle) {
291  DWCB *callback = fdd->wrt_handle;
292  void *cbdata;
293  fdd->wrt_handle = nullptr;
294 
296  callback(fd, status, len, cbdata);
297  /*
298  * NOTE, this callback can close the FD, so we must
299  * not touch 'F', 'fdd', etc. after this.
300  */
301  return;
302  /* XXX But what about close_request??? */
303  }
304  }
305 
306  if (do_close)
307  file_close(fd);
308 }
309 
310 /* write block to a file */
311 /* write back queue. Only one writer at a time. */
312 /* call a handle when writing is complete. */
313 void
314 file_write(int fd,
315  off_t file_offset,
316  void const *ptr_to_buf,
317  int len,
318  DWCB * handle,
319  void *handle_data,
320  FREE * free_func)
321 {
322  fde *F = &fd_table[fd];
323  assert(fd >= 0);
324  assert(F->flags.open);
325  /* if we got here. Caller is eligible to write. */
326  const auto wq = new dwrite_q(len, static_cast<char *>(const_cast<void *>(ptr_to_buf)), free_func);
327  wq->file_offset = file_offset;
328 
329  if (!F->disk.wrt_handle_data) {
330  F->disk.wrt_handle = handle;
331  F->disk.wrt_handle_data = cbdataReference(handle_data);
332  } else {
333  /* Detect if there is multiple concurrent users of this fd.. we only support one callback */
334  assert(F->disk.wrt_handle_data == handle_data && F->disk.wrt_handle == handle);
335  }
336 
337  /* add to queue */
338  if (F->disk.write_q == nullptr) {
339  /* empty queue */
340  F->disk.write_q = F->disk.write_q_tail = wq;
341  } else {
342  F->disk.write_q_tail->next = wq;
343  F->disk.write_q_tail = wq;
344  }
345 
346  if (!F->flags.write_daemon) {
347  diskHandleWrite(fd, nullptr);
348  }
349 }
350 
351 /* Read from FD */
352 static void
353 diskHandleRead(int fd, void *data)
354 {
355  dread_ctrl *ctrl_dat = (dread_ctrl *)data;
356  fde *F = &fd_table[fd];
357  int len;
358  int rc = DISK_OK;
359  int xerrno;
360 
361  /*
362  * FD < 0 indicates premature close; we just have to free
363  * the state data.
364  */
365 
366  if (fd < 0) {
367  delete ctrl_dat;
368  return;
369  }
370 
371 #if WRITES_MAINTAIN_DISK_OFFSET
372  if (F->disk.offset != ctrl_dat->offset) {
373 #else
374  {
375 #endif
376  debugs(6, 3, "diskHandleRead: FD " << fd << " seeking to offset " << ctrl_dat->offset);
377  errno = 0;
378  if (lseek(fd, ctrl_dat->offset, SEEK_SET) == -1) {
379  xerrno = errno;
380  // shouldn't happen, let's detect that
381  debugs(50, DBG_IMPORTANT, "ERROR: in seek for FD " << fd << ": " << xstrerr(xerrno));
382  // XXX handle failures?
383  }
384  ++ statCounter.syscalls.disk.seeks;
385  F->disk.offset = ctrl_dat->offset;
386  }
387 
388  errno = 0;
389  len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
390  xerrno = errno;
391 
392  if (len > 0)
393  F->disk.offset += len;
394 
395  ++ statCounter.syscalls.disk.reads;
396 
397  fd_bytes(fd, len, IoDirection::Read);
398 
399  if (len < 0) {
400  if (ignoreErrno(xerrno)) {
402  return;
403  }
404 
405  debugs(50, DBG_IMPORTANT, "diskHandleRead: FD " << fd << ": " << xstrerr(xerrno));
406  len = 0;
407  rc = DISK_ERROR;
408  } else if (len == 0) {
409  rc = DISK_EOF;
410  }
411 
412  if (cbdataReferenceValid(ctrl_dat->client_data))
413  ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
414 
415  cbdataReferenceDone(ctrl_dat->client_data);
416 
417  delete ctrl_dat;
418 }
419 
420 /* start read operation */
421 /* buffer must be allocated from the caller.
422  * It must have at least req_len space in there.
423  * call handler when a reading is complete. */
424 void
425 file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
426 {
427  assert(fd >= 0);
428  const auto ctrl_dat = new dread_ctrl(fd, offset, buf, req_len, handler, cbdataReference(client_data));
429  diskHandleRead(fd, ctrl_dat);
430 }
431 
432 void
433 safeunlink(const char *s, int quiet)
434 {
435  ++ statCounter.syscalls.disk.unlinks;
436 
437  if (unlink(s) < 0 && !quiet) {
438  int xerrno = errno;
439  debugs(50, DBG_IMPORTANT, "ERROR: safeunlink: Could not delete " << s << ": " << xstrerr(xerrno));
440  }
441 }
442 
443 bool
444 FileRename(const SBuf &from, const SBuf &to)
445 {
446  debugs(21, 2, "renaming " << from << " to " << to);
447 
448  // non-const copy for c_str()
449  SBuf from2(from);
450  // ensure c_str() lifetimes even if `to` and `from` share memory
451  SBuf to2(to.rawContent(), to.length());
452 
453 #if _SQUID_OS2_ || _SQUID_WINDOWS_
454  remove(to2.c_str());
455 #endif
456 
457  if (rename(from2.c_str(), to2.c_str()) == 0)
458  return true;
459 
460  int xerrno = errno;
461  debugs(21, (xerrno == ENOENT ? 2 : DBG_IMPORTANT), "ERROR: Cannot rename " << from << " to " << to << ": " << xstrerr(xerrno));
462 
463  return false;
464 }
465 
466 int
467 fsBlockSize(const char *path, int *blksize)
468 {
469  struct statvfs sfs;
470 
471  if (xstatvfs(path, &sfs)) {
472  int xerrno = errno;
473  debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
474  *blksize = 2048;
475  return 1;
476  }
477 
478  *blksize = (int) sfs.f_frsize;
479 
480  // Sanity check; make sure we have a meaningful value.
481  if (*blksize < 512)
482  *blksize = 2048;
483 
484  return 0;
485 }
486 
487 #define fsbtoblk(num, fsbs, bs) \
488  (((fsbs) != 0 && (fsbs) < (bs)) ? \
489  (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
490 int
491 fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
492 {
493  struct statvfs sfs;
494 
495  if (xstatvfs(path, &sfs)) {
496  int xerrno = errno;
497  debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
498  return 1;
499  }
500 
501  *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024);
502  *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_frsize, 1024);
503  *totl_in = (int) sfs.f_files;
504  *free_in = (int) sfs.f_ffree;
505  return 0;
506 }
507 
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
static PF diskHandleWrite
Definition: fs_io.cc:23
fsblkcnt_t f_bfree
Definition: statvfs.h:47
#define xmalloc
void fd_bytes(const int fd, const int len, const IoDirection direction)
Definition: fd.cc:226
static void cxx_xfree(void *ptr)
Definition: fs_io.cc:35
dwrite_q * write_q
Definition: fde.h:46
#define DISK_EOF
Definition: defines.h:29
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
void FREE(void *)
Definition: forward.h:37
void commSetCloseOnExec(int)
Definition: minimal.cc:27
Definition: fde.h:39
int fsBlockSize(const char *path, int *blksize)
Definition: fs_io.cc:467
fsblkcnt_t f_blocks
Definition: statvfs.h:46
int req_len
Definition: fs_io.h:37
Definition: SBuf.h:93
void * client_data
Definition: fs_io.h:41
dwrite_q(const size_t wantCapacity)
Definition: fs_io.h:48
static uint32 F(uint32 X, uint32 Y, uint32 Z)
Definition: md4.c:46
struct StatCounters::@119::@123 disk
Definition: cbdata.cc:37
void file_read(int fd, char *buf, int req_len, off_t offset, DRCB *handler, void *client_data)
Definition: fs_io.cc:425
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:270
int xstatvfs(const char *path, struct statvfs *sfs)
Definition: statvfs.cc:22
off_t file_offset
Definition: fs_io.h:53
void file_close(int fd)
Definition: fs_io.cc:93
void fd_open(const int fd, unsigned int, const char *description)
Definition: minimal.cc:15
fsfilcnt_t f_files
Definition: statvfs.h:49
#define cbdataReference(var)
Definition: cbdata.h:348
Definition: fde.h:51
#define DISK_OK
Definition: defines.h:27
static int do_close(diomsg *r, int)
Definition: diskd.cc:88
DRCB * handler
Definition: fs_io.h:40
void DRCB(int, const char *buf, int size, int errflag, void *data)
Definition: typedefs.h:16
struct StatCounters::@119 syscalls
char * buf
Definition: fs_io.h:54
DWCB * wrt_handle
Definition: fde.h:44
const char * rawContent() const
Definition: SBuf.cc:509
int fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
Definition: fs_io.cc:491
#define DISK_NO_SPACE_LEFT
Definition: defines.h:30
dwrite_q * write_q_tail
Definition: fde.h:47
SMB_Handle_Type handle
size_t len
length of content in buf
Definition: fs_io.h:55
fsfilcnt_t f_ffree
Definition: statvfs.h:50
FREE * free_func
when set, gets called upon object destruction to free buf
Definition: fs_io.h:62
#define assert(EX)
Definition: assert.h:17
#define COMM_SELECT_READ
Definition: defines.h:24
#define cbdataReferenceDone(var)
Definition: cbdata.h:357
int FD_WRITE_METHOD(int fd, const char *buf, int len)
Definition: fde.h:200
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
off_t offset
Definition: fs_io.h:36
#define xfree
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
int ignoreErrno(int ierrno)
Definition: comm.cc:1422
#define fd_table
Definition: fde.h:189
~dwrite_q()
Definition: fs_io.cc:54
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:220
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:314
#define FILE_MODE(x)
Definition: defines.h:143
unsigned long f_frsize
Definition: statvfs.h:45
#define DBG_IMPORTANT
Definition: Stream.h:38
Definition: fs_io.h:44
void safeunlink(const char *s, int quiet)
Definition: fs_io.cc:433
dwrite_q * next
Definition: fs_io.h:57
void fd_close(const int fd)
Definition: minimal.cc:21
#define DISK_ERROR
Definition: defines.h:28
static PF diskHandleRead
Definition: fs_io.cc:22
bool FileRename(const SBuf &from, const SBuf &to)
Definition: fs_io.cc:444
int file_open(const char *path, int mode)
Definition: fs_io.cc:65
#define fsbtoblk(num, fsbs, bs)
Definition: fs_io.cc:487
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
#define COMM_SELECT_WRITE
Definition: defines.h:25
char * buf
Definition: fs_io.h:38
@ FD_FILE
Definition: enums.h:15
static void diskCombineWrites(_fde_disk *fdd)
Definition: fs_io.cc:147
void * wrt_handle_data
Definition: fde.h:45
void DWCB(int, int, size_t, void *)
Definition: typedefs.h:18
void PF(int, void *)
Definition: forward.h:18
int unsigned int
Definition: stub_fd.cc:19
StatCounters statCounter
Definition: StatCounters.cc:12
size_t buf_offset
Definition: fs_io.h:56

 

Introduction

Documentation

Support

Miscellaneous