]> git.ipfire.org Git - thirdparty/squid.git/blob - src/disk.cc
SSL->HTTP gatewaying support by Benno Rice
[thirdparty/squid.git] / src / disk.cc
1
2 /*
3 * $Id: disk.cc,v 1.155 2001/04/14 00:03:22 hno Exp $
4 *
5 * DEBUG: section 6 Disk I/O Routines
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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.
24 *
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.
29 *
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 static PF diskHandleRead;
39 static PF diskHandleWrite;
40
41 #if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_)
42 static int
43 diskWriteIsComplete(int fd)
44 {
45 return fd_table[fd].disk.write_q ? 0 : 1;
46 }
47 #endif
48
49 void
50 disk_init(void)
51 {
52 (void) 0;
53 }
54
55 /*
56 * opens a disk file specified by 'path'. This function always
57 * blocks! There is no callback.
58 */
59 int
60 file_open(const char *path, int mode)
61 {
62 int fd;
63 if (mode & O_WRONLY)
64 mode |= O_APPEND;
65 mode |= SQUID_NONBLOCK;
66 errno = 0;
67 fd = open(path, mode, 0644);
68 statCounter.syscalls.disk.opens++;
69 if (fd < 0) {
70 debug(50, 3) ("file_open: error opening file %s: %s\n", path,
71 xstrerror());
72 fd = DISK_ERROR;
73 } else {
74 debug(6, 5) ("file_open: FD %d\n", fd);
75 commSetCloseOnExec(fd);
76 fd_open(fd, FD_FILE, path);
77 }
78 return fd;
79 }
80
81
82 /* close a disk file. */
83 void
84 file_close(int fd)
85 {
86 fde *F = &fd_table[fd];
87 PF *read_callback;
88 assert(fd >= 0);
89 assert(F->flags.open);
90 if ((read_callback = F->read_handler)) {
91 F->read_handler = NULL;
92 read_callback(-1, F->read_data);
93 }
94 if (F->flags.write_daemon) {
95 #if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
96 /*
97 * on some operating systems, you can not delete or rename
98 * open files, so we won't allow delayed close.
99 */
100 while (!diskWriteIsComplete(fd))
101 diskHandleWrite(fd, NULL);
102 #else
103 F->flags.close_request = 1;
104 debug(6, 2) ("file_close: FD %d, delaying close\n", fd);
105 return;
106 #endif
107 }
108 /*
109 * Assert there is no write callback. Otherwise we might be
110 * leaking write state data by closing the descriptor
111 */
112 assert(F->write_handler == NULL);
113 F->flags.closing = 1;
114 #if CALL_FSYNC_BEFORE_CLOSE
115 fsync(fd);
116 #endif
117 close(fd);
118 debug(6, F->flags.close_request ? 2 : 5)
119 ("file_close: FD %d, really closing\n", fd);
120 fd_close(fd);
121 statCounter.syscalls.disk.closes++;
122 }
123
124 /*
125 * This function has the purpose of combining multiple writes. This is
126 * to facilitate the ASYNC_IO option since it can only guarantee 1
127 * write to a file per trip around the comm.c select() loop. That's bad
128 * because more than 1 write can be made to the access.log file per
129 * trip, and so this code is purely designed to help batch multiple
130 * sequential writes to the access.log file. Squid will never issue
131 * multiple writes for any other file type during 1 trip around the
132 * select() loop. --SLF
133 */
134 static void
135 diskCombineWrites(struct _fde_disk *fdd)
136 {
137 int len = 0;
138 dwrite_q *q = NULL;
139 dwrite_q *wq = NULL;
140 /*
141 * We need to combine multiple write requests on an FD's write
142 * queue But only if we don't need to seek() in between them, ugh!
143 * XXX This currently ignores any seeks (file_offset)
144 */
145 if (fdd->write_q != NULL && fdd->write_q->next != NULL) {
146 len = 0;
147 for (q = fdd->write_q; q != NULL; q = q->next)
148 len += q->len - q->buf_offset;
149 wq = memAllocate(MEM_DWRITE_Q);
150 wq->buf = xmalloc(len);
151 wq->len = 0;
152 wq->buf_offset = 0;
153 wq->next = NULL;
154 wq->free_func = xfree;
155 do {
156 q = fdd->write_q;
157 len = q->len - q->buf_offset;
158 xmemcpy(wq->buf + wq->len, q->buf + q->buf_offset, len);
159 wq->len += len;
160 fdd->write_q = q->next;
161 if (q->free_func)
162 (q->free_func) (q->buf);
163 if (q) {
164 memFree(q, MEM_DWRITE_Q);
165 q = NULL;
166 }
167 } while (fdd->write_q != NULL);
168 fdd->write_q_tail = wq;
169 fdd->write_q = wq;
170 }
171 }
172
173 /* write handler */
174 static void
175 diskHandleWrite(int fd, void *notused)
176 {
177 int len = 0;
178 fde *F = &fd_table[fd];
179 struct _fde_disk *fdd = &F->disk;
180 dwrite_q *q = fdd->write_q;
181 int status = DISK_OK;
182 int do_callback;
183 int do_close;
184 if (NULL == q)
185 return;
186 debug(6, 3) ("diskHandleWrite: FD %d\n", fd);
187 F->flags.write_daemon = 0;
188 assert(fdd->write_q != NULL);
189 assert(fdd->write_q->len > fdd->write_q->buf_offset);
190 debug(6, 3) ("diskHandleWrite: FD %d writing %d bytes\n",
191 fd, (int) (fdd->write_q->len - fdd->write_q->buf_offset));
192 errno = 0;
193 if (fdd->write_q->file_offset != -1)
194 lseek(fd, fdd->write_q->file_offset, SEEK_SET);
195 len = FD_WRITE_METHOD(fd,
196 fdd->write_q->buf + fdd->write_q->buf_offset,
197 fdd->write_q->len - fdd->write_q->buf_offset);
198 debug(6, 3) ("diskHandleWrite: FD %d len = %d\n", fd, len);
199 statCounter.syscalls.disk.writes++;
200 fd_bytes(fd, len, FD_WRITE);
201 if (len < 0) {
202 if (!ignoreErrno(errno)) {
203 status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
204 debug(50, 1) ("diskHandleWrite: FD %d: disk write error: %s\n",
205 fd, xstrerror());
206 /*
207 * If there is no write callback, then this file is
208 * most likely something important like a log file, or
209 * an interprocess pipe. Its not a swapfile. We feel
210 * that a write failure on a log file is rather important,
211 * and Squid doesn't otherwise deal with this condition.
212 * So to get the administrators attention, we exit with
213 * a fatal message.
214 */
215 if (fdd->wrt_handle == NULL)
216 fatal("Write failure -- check your disk space and cache.log");
217 /*
218 * If there is a write failure, then we notify the
219 * upper layer via the callback, at the end of this
220 * function. Meanwhile, flush all pending buffers
221 * here. Let the upper layer decide how to handle the
222 * failure. This will prevent experiencing multiple,
223 * repeated write failures for the same FD because of
224 * the queued data.
225 */
226 do {
227 fdd->write_q = q->next;
228 if (q->free_func)
229 (q->free_func) (q->buf);
230 if (q) {
231 memFree(q, MEM_DWRITE_Q);
232 q = NULL;
233 }
234 } while ((q = fdd->write_q));
235 }
236 len = 0;
237 }
238 if (q != NULL) {
239 /* q might become NULL from write failure above */
240 q->buf_offset += len;
241 if (q->buf_offset > q->len)
242 debug(50, 1) ("diskHandleWriteComplete: q->buf_offset > q->len (%p,%d, %d, %d FD %d)\n",
243 q, (int) q->buf_offset, q->len, len, fd);
244 assert(q->buf_offset <= q->len);
245 if (q->buf_offset == q->len) {
246 /* complete write */
247 fdd->write_q = q->next;
248 if (q->free_func)
249 (q->free_func) (q->buf);
250 if (q) {
251 memFree(q, MEM_DWRITE_Q);
252 q = NULL;
253 }
254 }
255 }
256 if (fdd->write_q == NULL) {
257 /* no more data */
258 fdd->write_q_tail = NULL;
259 } else {
260 /* another block is queued */
261 diskCombineWrites(fdd);
262 cbdataLock(fdd->wrt_handle_data);
263 commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
264 F->flags.write_daemon = 1;
265 }
266 do_close = F->flags.close_request;
267 if (fdd->wrt_handle) {
268 if (fdd->wrt_handle_data == NULL)
269 do_callback = 1;
270 else if (cbdataValid(fdd->wrt_handle_data))
271 do_callback = 1;
272 else
273 do_callback = 0;
274 if (fdd->wrt_handle_data != NULL)
275 cbdataUnlock(fdd->wrt_handle_data);
276 if (do_callback) {
277 fdd->wrt_handle(fd, status, len, fdd->wrt_handle_data);
278 /*
279 * NOTE, this callback can close the FD, so we must
280 * not touch 'F', 'fdd', etc. after this.
281 */
282 return;
283 }
284 }
285 if (do_close)
286 file_close(fd);
287 }
288
289
290 /* write block to a file */
291 /* write back queue. Only one writer at a time. */
292 /* call a handle when writing is complete. */
293 void
294 file_write(int fd,
295 off_t file_offset,
296 void *ptr_to_buf,
297 int len,
298 DWCB handle,
299 void *handle_data,
300 FREE * free_func)
301 {
302 dwrite_q *wq = NULL;
303 fde *F = &fd_table[fd];
304 assert(fd >= 0);
305 assert(F->flags.open);
306 /* if we got here. Caller is eligible to write. */
307 wq = memAllocate(MEM_DWRITE_Q);
308 wq->file_offset = file_offset;
309 wq->buf = ptr_to_buf;
310 wq->len = len;
311 wq->buf_offset = 0;
312 wq->next = NULL;
313 wq->free_func = free_func;
314 F->disk.wrt_handle = handle;
315 F->disk.wrt_handle_data = handle_data;
316 /* add to queue */
317 if (F->disk.write_q == NULL) {
318 /* empty queue */
319 F->disk.write_q = F->disk.write_q_tail = wq;
320 } else {
321 F->disk.write_q_tail->next = wq;
322 F->disk.write_q_tail = wq;
323 }
324 if (!F->flags.write_daemon) {
325 cbdataLock(F->disk.wrt_handle_data);
326 diskHandleWrite(fd, NULL);
327 }
328 }
329
330 /*
331 * a wrapper around file_write to allow for MemBuf to be file_written
332 * in a snap
333 */
334 void
335 file_write_mbuf(int fd, off_t off, MemBuf mb, DWCB * handler, void *handler_data)
336 {
337 file_write(fd, off, mb.buf, mb.size, handler, handler_data, memBufFreeFunc(&mb));
338 }
339
340 /* Read from FD */
341 static void
342 diskHandleRead(int fd, void *data)
343 {
344 dread_ctrl *ctrl_dat = data;
345 fde *F = &fd_table[fd];
346 int len;
347 int rc = DISK_OK;
348 /*
349 * FD < 0 indicates premature close; we just have to free
350 * the state data.
351 */
352 if (fd < 0) {
353 memFree(ctrl_dat, MEM_DREAD_CTRL);
354 return;
355 }
356 if (F->disk.offset != ctrl_dat->offset) {
357 debug(6, 3) ("diskHandleRead: FD %d seeking to offset %d\n",
358 fd, (int) ctrl_dat->offset);
359 lseek(fd, ctrl_dat->offset, SEEK_SET); /* XXX ignore return? */
360 statCounter.syscalls.disk.seeks++;
361 F->disk.offset = ctrl_dat->offset;
362 }
363 errno = 0;
364 len = FD_READ_METHOD(fd, ctrl_dat->buf, ctrl_dat->req_len);
365 if (len > 0)
366 F->disk.offset += len;
367 statCounter.syscalls.disk.reads++;
368 fd_bytes(fd, len, FD_READ);
369 if (len < 0) {
370 if (ignoreErrno(errno)) {
371 commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
372 return;
373 }
374 debug(50, 1) ("diskHandleRead: FD %d: %s\n", fd, xstrerror());
375 len = 0;
376 rc = DISK_ERROR;
377 } else if (len == 0) {
378 rc = DISK_EOF;
379 }
380 if (cbdataValid(ctrl_dat->client_data))
381 ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
382 cbdataUnlock(ctrl_dat->client_data);
383 memFree(ctrl_dat, MEM_DREAD_CTRL);
384 }
385
386
387 /* start read operation */
388 /* buffer must be allocated from the caller.
389 * It must have at least req_len space in there.
390 * call handler when a reading is complete. */
391 void
392 file_read(int fd, char *buf, int req_len, off_t offset, DRCB * handler, void *client_data)
393 {
394 dread_ctrl *ctrl_dat;
395 assert(fd >= 0);
396 ctrl_dat = memAllocate(MEM_DREAD_CTRL);
397 ctrl_dat->fd = fd;
398 ctrl_dat->offset = offset;
399 ctrl_dat->req_len = req_len;
400 ctrl_dat->buf = buf;
401 ctrl_dat->end_of_file = 0;
402 ctrl_dat->handler = handler;
403 ctrl_dat->client_data = client_data;
404 cbdataLock(client_data);
405 diskHandleRead(fd, ctrl_dat);
406 }