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