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