]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/disk.cc
3 * $Id: disk.cc,v 1.155 2001/04/14 00:03:22 hno Exp $
5 * DEBUG: section 6 Disk I/O Routines
6 * AUTHOR: Harvest Derived
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
38 static PF diskHandleRead
;
39 static PF diskHandleWrite
;
41 #if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined(_SQUID_CYGWIN_)
43 diskWriteIsComplete(int fd
)
45 return fd_table
[fd
].disk
.write_q
? 0 : 1;
56 * opens a disk file specified by 'path'. This function always
57 * blocks! There is no callback.
60 file_open(const char *path
, int mode
)
65 mode
|= SQUID_NONBLOCK
;
67 fd
= open(path
, mode
, 0644);
68 statCounter
.syscalls
.disk
.opens
++;
70 debug(50, 3) ("file_open: error opening file %s: %s\n", path
,
74 debug(6, 5) ("file_open: FD %d\n", fd
);
75 commSetCloseOnExec(fd
);
76 fd_open(fd
, FD_FILE
, path
);
82 /* close a disk file. */
86 fde
*F
= &fd_table
[fd
];
89 assert(F
->flags
.open
);
90 if ((read_callback
= F
->read_handler
)) {
91 F
->read_handler
= NULL
;
92 read_callback(-1, F
->read_data
);
94 if (F
->flags
.write_daemon
) {
95 #if defined(_SQUID_MSWIN_) || defined(_SQUID_OS2_) || defined (_SQUID_CYGWIN_)
97 * on some operating systems, you can not delete or rename
98 * open files, so we won't allow delayed close.
100 while (!diskWriteIsComplete(fd
))
101 diskHandleWrite(fd
, NULL
);
103 F
->flags
.close_request
= 1;
104 debug(6, 2) ("file_close: FD %d, delaying close\n", fd
);
109 * Assert there is no write callback. Otherwise we might be
110 * leaking write state data by closing the descriptor
112 assert(F
->write_handler
== NULL
);
113 F
->flags
.closing
= 1;
114 #if CALL_FSYNC_BEFORE_CLOSE
118 debug(6, F
->flags
.close_request
? 2 : 5)
119 ("file_close: FD %d, really closing\n", fd
);
121 statCounter
.syscalls
.disk
.closes
++;
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
135 diskCombineWrites(struct _fde_disk
*fdd
)
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)
145 if (fdd
->write_q
!= NULL
&& fdd
->write_q
->next
!= NULL
) {
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
);
154 wq
->free_func
= xfree
;
157 len
= q
->len
- q
->buf_offset
;
158 xmemcpy(wq
->buf
+ wq
->len
, q
->buf
+ q
->buf_offset
, len
);
160 fdd
->write_q
= q
->next
;
162 (q
->free_func
) (q
->buf
);
164 memFree(q
, MEM_DWRITE_Q
);
167 } while (fdd
->write_q
!= NULL
);
168 fdd
->write_q_tail
= wq
;
175 diskHandleWrite(int fd
, void *notused
)
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
;
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
));
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
);
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",
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
215 if (fdd
->wrt_handle
== NULL
)
216 fatal("Write failure -- check your disk space and cache.log");
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
227 fdd
->write_q
= q
->next
;
229 (q
->free_func
) (q
->buf
);
231 memFree(q
, MEM_DWRITE_Q
);
234 } while ((q
= fdd
->write_q
));
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
) {
247 fdd
->write_q
= q
->next
;
249 (q
->free_func
) (q
->buf
);
251 memFree(q
, MEM_DWRITE_Q
);
256 if (fdd
->write_q
== NULL
) {
258 fdd
->write_q_tail
= NULL
;
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;
266 do_close
= F
->flags
.close_request
;
267 if (fdd
->wrt_handle
) {
268 if (fdd
->wrt_handle_data
== NULL
)
270 else if (cbdataValid(fdd
->wrt_handle_data
))
274 if (fdd
->wrt_handle_data
!= NULL
)
275 cbdataUnlock(fdd
->wrt_handle_data
);
277 fdd
->wrt_handle(fd
, status
, len
, fdd
->wrt_handle_data
);
279 * NOTE, this callback can close the FD, so we must
280 * not touch 'F', 'fdd', etc. after this.
290 /* write block to a file */
291 /* write back queue. Only one writer at a time. */
292 /* call a handle when writing is complete. */
303 fde
*F
= &fd_table
[fd
];
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
;
313 wq
->free_func
= free_func
;
314 F
->disk
.wrt_handle
= handle
;
315 F
->disk
.wrt_handle_data
= handle_data
;
317 if (F
->disk
.write_q
== NULL
) {
319 F
->disk
.write_q
= F
->disk
.write_q_tail
= wq
;
321 F
->disk
.write_q_tail
->next
= wq
;
322 F
->disk
.write_q_tail
= wq
;
324 if (!F
->flags
.write_daemon
) {
325 cbdataLock(F
->disk
.wrt_handle_data
);
326 diskHandleWrite(fd
, NULL
);
331 * a wrapper around file_write to allow for MemBuf to be file_written
335 file_write_mbuf(int fd
, off_t off
, MemBuf mb
, DWCB
* handler
, void *handler_data
)
337 file_write(fd
, off
, mb
.buf
, mb
.size
, handler
, handler_data
, memBufFreeFunc(&mb
));
342 diskHandleRead(int fd
, void *data
)
344 dread_ctrl
*ctrl_dat
= data
;
345 fde
*F
= &fd_table
[fd
];
349 * FD < 0 indicates premature close; we just have to free
353 memFree(ctrl_dat
, MEM_DREAD_CTRL
);
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
;
364 len
= FD_READ_METHOD(fd
, ctrl_dat
->buf
, ctrl_dat
->req_len
);
366 F
->disk
.offset
+= len
;
367 statCounter
.syscalls
.disk
.reads
++;
368 fd_bytes(fd
, len
, FD_READ
);
370 if (ignoreErrno(errno
)) {
371 commSetSelect(fd
, COMM_SELECT_READ
, diskHandleRead
, ctrl_dat
, 0);
374 debug(50, 1) ("diskHandleRead: FD %d: %s\n", fd
, xstrerror());
377 } else if (len
== 0) {
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
);
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. */
392 file_read(int fd
, char *buf
, int req_len
, off_t offset
, DRCB
* handler
, void *client_data
)
394 dread_ctrl
*ctrl_dat
;
396 ctrl_dat
= memAllocate(MEM_DREAD_CTRL
);
398 ctrl_dat
->offset
= offset
;
399 ctrl_dat
->req_len
= req_len
;
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
);