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