]> git.ipfire.org Git - thirdparty/squid.git/blame - src/disk.cc
Release notes updates
[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
44a47c6e 35#include "squid.h"
528b2c61 36#include "fde.h"
0eb49b6d 37#include "MemBuf.h"
090089c4 38
95d15928 39static PF diskHandleRead;
40static PF diskHandleWrite;
24382924 41
ec4daaa5 42#if defined(_SQUID_WIN32_) || defined(_SQUID_OS2_)
1a94f598 43static int
44diskWriteIsComplete(int fd)
45{
46 return fd_table[fd].disk.write_q ? 0 : 1;
47}
62e76326 48
1a94f598 49#endif
50
04cece06 51void
0673c0ba 52disk_init(void)
090089c4 53{
60df005c 54 (void) 0;
090089c4 55}
56
59a09b98
FC
57/* hack needed on SunStudio to avoid linkage convention mismatch */
58static void cxx_xfree(void *ptr)
59{
f673997d 60 xfree(ptr);
59a09b98
FC
61}
62
1a94f598 63/*
64 * opens a disk file specified by 'path'. This function always
65 * blocks! There is no callback.
66 */
684c2720 67int
1a94f598 68file_open(const char *path, int mode)
090089c4 69{
090089c4 70 int fd;
88bfe092 71 PROF_start(file_open);
62e76326 72
5d1a7121 73 if (FILE_MODE(mode) == O_WRONLY)
62e76326 74 mode |= O_APPEND;
75
b870e0b4 76 errno = 0;
62e76326 77
0a0bf5db 78 fd = open(path, mode, 0644);
62e76326 79
83704487 80 statCounter.syscalls.disk.opens++;
62e76326 81
0a0bf5db 82 if (fd < 0) {
bf8fe701 83 debugs(50, 3, "file_open: error opening file " << path << ": " << xstrerror());
62e76326 84 fd = DISK_ERROR;
2391a162 85 } else {
bf8fe701 86 debugs(6, 5, "file_open: FD " << fd);
62e76326 87 commSetCloseOnExec(fd);
88 fd_open(fd, FD_FILE, path);
090089c4 89 }
62e76326 90
88bfe092 91 PROF_stop(file_open);
2391a162 92 return fd;
0a0bf5db 93}
94
2391a162 95
090089c4 96/* close a disk file. */
95d15928 97void
b8d8561b 98file_close(int fd)
090089c4 99{
76f87348 100 fde *F = &fd_table[fd];
2391a162 101 PF *read_callback;
88bfe092 102 PROF_start(file_close);
25354045 103 assert(fd >= 0);
60c0b5a2 104 assert(F->flags.open);
62e76326 105
2391a162 106 if ((read_callback = F->read_handler)) {
62e76326 107 F->read_handler = NULL;
108 read_callback(-1, F->read_data);
65d548bf 109 }
62e76326 110
0cd30ba5 111 if (F->flags.write_daemon) {
ec4daaa5 112#if defined(_SQUID_WIN32_) || defined(_SQUID_OS2_)
62e76326 113 /*
114 * on some operating systems, you can not delete or rename
115 * open files, so we won't allow delayed close.
116 */
117
118 while (!diskWriteIsComplete(fd))
119 diskHandleWrite(fd, NULL);
120
cd377065 121#else
62e76326 122
123 F->flags.close_request = 1;
124
bf8fe701 125 debugs(6, 2, "file_close: FD " << fd << ", delaying close");
62e76326 126
127 PROF_stop(file_close);
128
129 return;
130
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
83704487 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
62e76326 169
f02b8498 170diskCombineWrites(struct _fde_disk *fdd)
090089c4 171{
4a86108c 172 int len = 0;
0a0bf5db 173 dwrite_q *q = NULL;
174 dwrite_q *wq = NULL;
f02b8498 175 /*
176 * We need to combine multiple write requests on an FD's write
177 * queue But only if we don't need to seek() in between them, ugh!
178 * XXX This currently ignores any seeks (file_offset)
179 */
62e76326 180
26ac0430 181 if (fdd->write_q != NULL && fdd->write_q->next != NULL) {
62e76326 182 len = 0;
183
184 for (q = fdd->write_q; q != NULL; q = q->next)
185 len += q->len - q->buf_offset;
186
187 wq = (dwrite_q *)memAllocate(MEM_DWRITE_Q);
188
189 wq->buf = (char *)xmalloc(len);
190
191 wq->len = 0;
192
193 wq->buf_offset = 0;
194
195 wq->next = NULL;
196
59a09b98 197 wq->free_func = cxx_xfree;
62e76326 198
199 do {
200 q = fdd->write_q;
201 len = q->len - q->buf_offset;
202 xmemcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
203 wq->len += len;
204 fdd->write_q = q->next;
205
206 if (q->free_func)
207 (q->free_func) (q->buf);
208
209 if (q) {
210 memFree(q, MEM_DWRITE_Q);
211 q = NULL;
212 }
213 } while (fdd->write_q != NULL);
214
215 fdd->write_q_tail = wq;
216
217 fdd->write_q = wq;
0a0bf5db 218 }
f02b8498 219}
220
221/* write handler */
222static void
223diskHandleWrite(int fd, void *notused)
224{
225 int len = 0;
f02b8498 226 fde *F = &fd_table[fd];
62e76326 227
f02b8498 228 struct _fde_disk *fdd = &F->disk;
2391a162 229 dwrite_q *q = fdd->write_q;
230 int status = DISK_OK;
2391a162 231 int do_close;
62e76326 232
2391a162 233 if (NULL == q)
62e76326 234 return;
235
88bfe092 236 PROF_start(diskHandleWrite);
62e76326 237
bf8fe701 238 debugs(6, 3, "diskHandleWrite: FD " << fd);
62e76326 239
2391a162 240 F->flags.write_daemon = 0;
62e76326 241
8350fe9b 242 assert(fdd->write_q != NULL);
62e76326 243
d377699f 244 assert(fdd->write_q->len > fdd->write_q->buf_offset);
62e76326 245
4a7a3d56 246 debugs(6, 3, "diskHandleWrite: FD " << fd << " writing " << (fdd->write_q->len - fdd->write_q->buf_offset) << " bytes");
62e76326 247
b870e0b4 248 errno = 0;
62e76326 249
cd748f27 250 if (fdd->write_q->file_offset != -1)
62e76326 251 lseek(fd, fdd->write_q->file_offset, SEEK_SET);
252
1f7c9178 253 len = FD_WRITE_METHOD(fd,
62e76326 254 fdd->write_q->buf + fdd->write_q->buf_offset,
255 fdd->write_q->len - fdd->write_q->buf_offset);
256
bf8fe701 257 debugs(6, 3, "diskHandleWrite: FD " << fd << " len = " << len);
62e76326 258
83704487 259 statCounter.syscalls.disk.writes++;
62e76326 260
6cf028ab 261 fd_bytes(fd, len, FD_WRITE);
62e76326 262
0a0bf5db 263 if (len < 0) {
62e76326 264 if (!ignoreErrno(errno)) {
265 status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
bf8fe701 266 debugs(50, 1, "diskHandleWrite: FD " << fd << ": disk write error: " << xstrerror());
267
62e76326 268 /*
269 * If there is no write callback, then this file is
270 * most likely something important like a log file, or
271 * an interprocess pipe. Its not a swapfile. We feel
272 * that a write failure on a log file is rather important,
273 * and Squid doesn't otherwise deal with this condition.
274 * So to get the administrators attention, we exit with
275 * a fatal message.
276 */
277
278 if (fdd->wrt_handle == NULL)
279 fatal("Write failure -- check your disk space and cache.log");
280
281 /*
282 * If there is a write failure, then we notify the
283 * upper layer via the callback, at the end of this
284 * function. Meanwhile, flush all pending buffers
285 * here. Let the upper layer decide how to handle the
286 * failure. This will prevent experiencing multiple,
287 * repeated write failures for the same FD because of
288 * the queued data.
289 */
290 do {
291 fdd->write_q = q->next;
292
293 if (q->free_func)
294 (q->free_func) (q->buf);
295
296 if (q) {
297 memFree(q, MEM_DWRITE_Q);
298 q = NULL;
299 }
300 } while ((q = fdd->write_q));
301 }
302
303 len = 0;
0a0bf5db 304 }
62e76326 305
8350fe9b 306 if (q != NULL) {
62e76326 307 /* q might become NULL from write failure above */
308 q->buf_offset += len;
309
310 if (q->buf_offset > q->len)
bf8fe701 311 debugs(50, 1, "diskHandleWriteComplete: q->buf_offset > q->len (" <<
312 q << "," << (int) q->buf_offset << ", " << q->len << ", " <<
313 len << " FD " << fd << ")");
314
62e76326 315
316 assert(q->buf_offset <= q->len);
317
318 if (q->buf_offset == q->len) {
319 /* complete write */
320 fdd->write_q = q->next;
321
322 if (q->free_func)
323 (q->free_func) (q->buf);
324
325 if (q) {
326 memFree(q, MEM_DWRITE_Q);
327 q = NULL;
328 }
329 }
090089c4 330 }
62e76326 331
de866d20 332 if (fdd->write_q == NULL) {
62e76326 333 /* no more data */
334 fdd->write_q_tail = NULL;
de866d20 335 } else {
62e76326 336 /* another block is queued */
337 diskCombineWrites(fdd);
338 commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
339 F->flags.write_daemon = 1;
4a86108c 340 }
62e76326 341
0cd30ba5 342 do_close = F->flags.close_request;
62e76326 343
25354045 344 if (fdd->wrt_handle) {
62e76326 345 DWCB *callback = fdd->wrt_handle;
346 void *cbdata;
347 fdd->wrt_handle = NULL;
348
349 if (cbdataReferenceValidDone(fdd->wrt_handle_data, &cbdata)) {
350 callback(fd, status, len, cbdata);
351 /*
352 * NOTE, this callback can close the FD, so we must
353 * not touch 'F', 'fdd', etc. after this.
354 */
355 PROF_stop(diskHandleWrite);
356 return;
357 /* XXX But what about close_request??? */
358 }
25354045 359 }
62e76326 360
68c21f71 361 if (do_close)
62e76326 362 file_close(fd);
363
88bfe092 364 PROF_stop(diskHandleWrite);
090089c4 365}
366
367
090089c4 368/* write block to a file */
369/* write back queue. Only one writer at a time. */
370/* call a handle when writing is complete. */
e3ef2b09 371void
3ebcfaa1 372file_write(int fd,
62e76326 373 off_t file_offset,
374 void const *ptr_to_buf,
375 int len,
376 DWCB * handle,
377 void *handle_data,
378 FREE * free_func)
090089c4 379{
c6ac7aae 380 dwrite_q *wq = NULL;
48cc3fcf 381 fde *F = &fd_table[fd];
88bfe092 382 PROF_start(file_write);
48cc3fcf 383 assert(fd >= 0);
60c0b5a2 384 assert(F->flags.open);
090089c4 385 /* if we got here. Caller is eligible to write. */
e6ccf245 386 wq = (dwrite_q *)memAllocate(MEM_DWRITE_Q);
d377699f 387 wq->file_offset = file_offset;
e6ccf245 388 wq->buf = (char *)ptr_to_buf;
090089c4 389 wq->len = len;
d377699f 390 wq->buf_offset = 0;
090089c4 391 wq->next = NULL;
ed7f0b6a 392 wq->free_func = free_func;
62e76326 393
fa80a8ef 394 if (!F->disk.wrt_handle_data) {
62e76326 395 F->disk.wrt_handle = handle;
396 F->disk.wrt_handle_data = cbdataReference(handle_data);
fa80a8ef 397 } else {
62e76326 398 /* Detect if there is multiple concurrent users of this fd.. we only support one callback */
399 assert(F->disk.wrt_handle_data == handle_data && F->disk.wrt_handle == handle);
fa80a8ef 400 }
62e76326 401
090089c4 402 /* add to queue */
48cc3fcf 403 if (F->disk.write_q == NULL) {
62e76326 404 /* empty queue */
405 F->disk.write_q = F->disk.write_q_tail = wq;
090089c4 406 } else {
62e76326 407 F->disk.write_q_tail->next = wq;
408 F->disk.write_q_tail = wq;
090089c4 409 }
62e76326 410
0cd30ba5 411 if (!F->flags.write_daemon) {
62e76326 412 diskHandleWrite(fd, NULL);
429fdbec 413 }
62e76326 414
88bfe092 415 PROF_stop(file_write);
090089c4 416}
417
23b2b404 418/*
419 * a wrapper around file_write to allow for MemBuf to be file_written
420 * in a snap
421 */
137ee196 422void
423file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
424{
2fe7eff9 425 file_write(fd, off, mb.buf, mb.size, handler, handler_data, mb.freeFunc());
137ee196 426}
090089c4 427
428/* Read from FD */
582b6456 429static void
430diskHandleRead(int fd, void *data)
090089c4 431{
e6ccf245 432 dread_ctrl *ctrl_dat = (dread_ctrl *)data;
edd2eb63 433 fde *F = &fd_table[fd];
090089c4 434 int len;
2391a162 435 int rc = DISK_OK;
65d548bf 436 /*
437 * FD < 0 indicates premature close; we just have to free
438 * the state data.
439 */
62e76326 440
65d548bf 441 if (fd < 0) {
62e76326 442 memFree(ctrl_dat, MEM_DREAD_CTRL);
443 return;
65d548bf 444 }
62e76326 445
88bfe092 446 PROF_start(diskHandleRead);
62e76326 447
711982d8 448 if (F->disk.offset != ctrl_dat->offset) {
4a7a3d56 449 debugs(6, 3, "diskHandleRead: FD " << fd << " seeking to offset " << ctrl_dat->offset);
62e76326 450 lseek(fd, ctrl_dat->offset, SEEK_SET); /* XXX ignore return? */
451 statCounter.syscalls.disk.seeks++;
452 F->disk.offset = ctrl_dat->offset;
711982d8 453 }
62e76326 454
b870e0b4 455 errno = 0;
1f7c9178 456 len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
62e76326 457
015b507a 458 if (len > 0)
62e76326 459 F->disk.offset += len;
460
83704487 461 statCounter.syscalls.disk.reads++;
62e76326 462
4f92c80c 463 fd_bytes(fd, len, FD_READ);
62e76326 464
0a0bf5db 465 if (len < 0) {
62e76326 466 if (ignoreErrno(errno)) {
467 commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
468 PROF_stop(diskHandleRead);
469 return;
470 }
471
bf8fe701 472 debugs(50, 1, "diskHandleRead: FD " << fd << ": " << xstrerror());
62e76326 473 len = 0;
474 rc = DISK_ERROR;
090089c4 475 } else if (len == 0) {
62e76326 476 rc = DISK_EOF;
090089c4 477 }
62e76326 478
fa80a8ef 479 if (cbdataReferenceValid(ctrl_dat->client_data))
62e76326 480 ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
481
fa80a8ef 482 cbdataReferenceDone(ctrl_dat->client_data);
62e76326 483
db1cd23c 484 memFree(ctrl_dat, MEM_DREAD_CTRL);
62e76326 485
88bfe092 486 PROF_stop(diskHandleRead);
090089c4 487}
488
489
490/* start read operation */
62e76326 491/* buffer must be allocated from the caller.
26ac0430 492 * It must have at least req_len space in there.
090089c4 493 * call handler when a reading is complete. */
2391a162 494void
d377699f 495file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
090089c4 496{
497 dread_ctrl *ctrl_dat;
88bfe092 498 PROF_start(file_read);
711982d8 499 assert(fd >= 0);
e6ccf245 500 ctrl_dat = (dread_ctrl *)memAllocate(MEM_DREAD_CTRL);
090089c4 501 ctrl_dat->fd = fd;
502 ctrl_dat->offset = offset;
503 ctrl_dat->req_len = req_len;
504 ctrl_dat->buf = buf;
090089c4 505 ctrl_dat->end_of_file = 0;
506 ctrl_dat->handler = handler;
fa80a8ef 507 ctrl_dat->client_data = cbdataReference(client_data);
0a0bf5db 508 diskHandleRead(fd, ctrl_dat);
88bfe092 509 PROF_stop(file_read);
090089c4 510}
c8f4eac4 511
512void
513safeunlink(const char *s, int quiet)
514{
515 statCounter.syscalls.disk.unlinks++;
516
517 if (unlink(s) < 0 && !quiet)
bf8fe701 518 debugs(50, 1, "safeunlink: Couldn't delete " << s << ": " << xstrerror());
c8f4eac4 519}
520
521/*
522 * Same as rename(2) but complains if something goes wrong;
26ac0430 523 * the caller is responsible for handing and explaining the
c8f4eac4 524 * consequences of errors.
525 */
526int
527xrename(const char *from, const char *to)
528{
bf8fe701 529 debugs(21, 2, "xrename: renaming " << from << " to " << to);
c8f4eac4 530#if defined (_SQUID_OS2_) || defined (_SQUID_WIN32_)
531
532 remove
26ac0430 533 (to);
c8f4eac4 534
535#endif
536
537 if (0 == rename(from, to))
538 return 0;
539
bf8fe701 540 debugs(21, errno == ENOENT ? 2 : 1, "xrename: Cannot rename " << from << " to " << to << ": " << xstrerror());
c8f4eac4 541
542 return -1;
543}
544