fs_io.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 
201  PROF_start(diskHandleWrite);
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);
311  Comm::SetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
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 
322  if (cbdataReferenceValidDone(fdd->wrt_handle_data, &cbdata)) {
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  */
328  PROF_stop(diskHandleWrite);
329  return;
330  /* XXX But what about close_request??? */
331  }
332  }
333 
334  if (do_close)
335  file_close(fd);
336 
337  PROF_stop(diskHandleWrite);
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 
420  PROF_start(diskHandleRead);
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)) {
452  Comm::SetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
453  PROF_stop(diskHandleRead);
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 
471  PROF_stop(diskHandleRead);
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 /*
508  * Same as rename(2) but complains if something goes wrong;
509  * the caller is responsible for handing and explaining the
510  * consequences of errors.
511  */
512 int
513 xrename(const char *from, const char *to)
514 {
515  debugs(21, 2, "xrename: renaming " << from << " to " << to);
516 #if _SQUID_OS2_ || _SQUID_WINDOWS_
517  remove(to);
518 #endif
519 
520  if (0 == rename(from, to))
521  return 0;
522 
523  int xerrno = errno;
524  debugs(21, errno == ENOENT ? 2 : 1, "xrename: Cannot rename " << from << " to " << to << ": " << xstrerr(xerrno));
525 
526  return -1;
527 }
528 
529 int
530 fsBlockSize(const char *path, int *blksize)
531 {
532  struct statvfs sfs;
533 
534  if (xstatvfs(path, &sfs)) {
535  int xerrno = errno;
536  debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
537  *blksize = 2048;
538  return 1;
539  }
540 
541  *blksize = (int) sfs.f_frsize;
542 
543  // Sanity check; make sure we have a meaningful value.
544  if (*blksize < 512)
545  *blksize = 2048;
546 
547  return 0;
548 }
549 
550 #define fsbtoblk(num, fsbs, bs) \
551  (((fsbs) != 0 && (fsbs) < (bs)) ? \
552  (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
553 int
554 fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
555 {
556  struct statvfs sfs;
557 
558  if (xstatvfs(path, &sfs)) {
559  int xerrno = errno;
560  debugs(50, DBG_IMPORTANT, "" << path << ": " << xstrerr(xerrno));
561  return 1;
562  }
563 
564  *totl_kb = (int) fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024);
565  *free_kb = (int) fsbtoblk(sfs.f_bfree, sfs.f_frsize, 1024);
566  *totl_in = (int) sfs.f_files;
567  *free_in = (int) sfs.f_ffree;
568  return 0;
569 }
570 
FREE * freeFunc()
Definition: MemBuf.cc:328
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:256
Definition: fde.h:37
#define fd_table
Definition: fde.h:157
#define COMM_SELECT_READ
Definition: defines.h:36
StatCounters statCounter
Definition: StatCounters.cc:12
#define assert(EX)
Definition: assert.h:17
void const char HLPCB * callback
Definition: stub_helper.cc:16
fsfilcnt_t f_files
Definition: statvfs.h:49
int end_of_file
Definition: fs_io.h:27
_fde_disk disk
Definition: fde.h:125
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
size_t buf_offset
Definition: fs_io.h:39
struct fde::_fde_flags flags
bool close_request
true if file_ or comm_close has been called
Definition: fde.h:97
static int do_close(diomsg *r, int)
Definition: diskd.cc:88
Definition: cbdata.cc:60
void safeunlink(const char *s, int quiet)
Definition: fs_io.cc:497
fsblkcnt_t f_blocks
Definition: statvfs.h:46
off_t file_offset
Definition: fs_io.h:36
off_t offset
Definition: fs_io.h:24
void fd_open(int fd, unsigned int type, const char *desc)
Definition: fd.cc:187
dwrite_q * write_q_tail
Definition: fde.h:45
unsigned long f_frsize
Definition: statvfs.h:45
int file_open(const char *path, int mode)
Definition: fs_io.cc:46
void DWCB(int, int, size_t, void *)
Definition: typedefs.h:18
Definition: enums.h:24
int xrename(const char *from, const char *to)
Definition: fs_io.cc:513
void FREE(void *)
Definition: forward.h:36
Definition: enums.h:23
PF * write_handler
Definition: fde.h:128
#define DISK_ERROR
Definition: defines.h:40
#define FILE_MODE(x)
Definition: defines.h:213
void fd_bytes(int fd, int len, unsigned int type)
Definition: fd.cc:261
Definition: fs_io.h:33
static PF diskHandleRead
Definition: fs_io.cc:23
const char * xstrerr(int error)
Definition: xstrerror.cc:83
char * buf
Definition: fs_io.h:37
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
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
int fsBlockSize(const char *path, int *blksize)
Definition: fs_io.cc:530
#define cbdataReference(var)
Definition: cbdata.h:341
void * wrt_handle_data
Definition: fde.h:43
struct StatCounters::@136 syscalls
PF * read_handler
Definition: fde.h:126
#define DBG_IMPORTANT
Definition: Debug.h:45
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:225
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:219
dwrite_q * write_q
Definition: fde.h:44
struct StatCounters::@136::@139 disk
fsblkcnt_t f_bfree
Definition: statvfs.h:47
mb_size_t size
Definition: MemBuf.h:135
void file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB *handler, void *handler_data)
Definition: fs_io.cc:395
#define COMM_SELECT_WRITE
Definition: defines.h:37
void commSetCloseOnExec(int fd)
Definition: comm.cc:1137
char * buf
Definition: fs_io.h:26
size_t len
Definition: fs_io.h:38
void fatal(const char *message)
Definition: fatal.cc:39
void PF(int, void *)
Definition: forward.h:18
void * read_data
Definition: fde.h:127
static void cxx_xfree(void *ptr)
Definition: fs_io.cc:36
SMB_Handle_Type handle
FREE * free_func
Definition: fs_io.h:41
void fd_close(int fd)
Definition: fd.cc:82
static void handler(int signo)
Definition: purge.cc:860
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
void DRCB(int, const char *buf, int size, int errflag, void *data)
Definition: typedefs.h:16
void const char * buf
Definition: stub_helper.cc:16
#define DISK_EOF
Definition: defines.h:41
int req_len
Definition: fs_io.h:25
static PF diskHandleWrite
Definition: fs_io.cc:24
bool SIGHDLR int STUB void int
Definition: stub_tools.cc:68
int ignoreErrno(int ierrno)
Definition: comm.cc:1477
void * client_data
Definition: fs_io.h:29
DWCB * wrt_handle
Definition: fde.h:42
fsfilcnt_t f_ffree
Definition: statvfs.h:50
char * buf
Definition: MemBuf.h:134
#define fsbtoblk(num, fsbs, bs)
Definition: fs_io.cc:550
#define FD_READ_METHOD(fd, buf, len)
Definition: fde.h:161
#define xmalloc
#define PROF_start(probename)
Definition: Profiler.h:62
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: old_api.cc:227
bool open
Definition: fde.h:96
off_t offset
Definition: fde.h:46
int fsStats(const char *path, int *totl_kb, int *free_kb, int *totl_in, int *free_in)
Definition: fs_io.cc:554
int fd
Definition: fs_io.h:23
bool write_daemon
Definition: fde.h:98
Definition: fde.h:49
Definition: MemBuf.h:23
static void diskCombineWrites(_fde_disk *fdd)
Definition: fs_io.cc:140
DRCB * handler
Definition: fs_io.h:28
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:412
Definition: enums.h:15
#define PROF_stop(probename)
Definition: Profiler.h:63
dwrite_q * next
Definition: fs_io.h:40
#define DISK_OK
Definition: defines.h:39
#define FD_WRITE_METHOD(fd, buf, len)
Definition: fde.h:162
#define DISK_NO_SPACE_LEFT
Definition: defines.h:42
#define xfree
void file_close(int fd)
Definition: fs_io.cc:76
void file_read(int fd, char *buf, int req_len, off_t offset, DRCB *handler, void *client_data)
Definition: fs_io.cc:479
#define NULL
Definition: types.h:166

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors