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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors