]> git.ipfire.org Git - thirdparty/squid.git/blob - src/disk.cc
LINT
[thirdparty/squid.git] / src / disk.cc
1 /*
2 * $Id: disk.cc,v 1.91 1997/11/05 05:29:22 wessels Exp $
3 *
4 * DEBUG: section 6 Disk I/O Routines
5 * AUTHOR: Harvest Derived
6 *
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * --------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by
13 * the National Science Foundation.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
29 */
30
31 /*
32 * Copyright (c) 1994, 1995. All rights reserved.
33 *
34 * The Harvest software was developed by the Internet Research Task
35 * Force Research Group on Resource Discovery (IRTF-RD):
36 *
37 * Mic Bowman of Transarc Corporation.
38 * Peter Danzig of the University of Southern California.
39 * Darren R. Hardy of the University of Colorado at Boulder.
40 * Udi Manber of the University of Arizona.
41 * Michael F. Schwartz of the University of Colorado at Boulder.
42 * Duane Wessels of the University of Colorado at Boulder.
43 *
44 * This copyright notice applies to software in the Harvest
45 * ``src/'' directory only. Users should consult the individual
46 * copyright notices in the ``components/'' subdirectories for
47 * copyright information about other software bundled with the
48 * Harvest source code distribution.
49 *
50 * TERMS OF USE
51 *
52 * The Harvest software may be used and re-distributed without
53 * charge, provided that the software origin and research team are
54 * cited in any use of the system. Most commonly this is
55 * accomplished by including a link to the Harvest Home Page
56 * (http://harvest.cs.colorado.edu/) from the query page of any
57 * Broker you deploy, as well as in the query result pages. These
58 * links are generated automatically by the standard Broker
59 * software distribution.
60 *
61 * The Harvest software is provided ``as is'', without express or
62 * implied warranty, and with no support nor obligation to assist
63 * in its use, correction, modification or enhancement. We assume
64 * no liability with respect to the infringement of copyrights,
65 * trade secrets, or any patents, and are not responsible for
66 * consequential damages. Proper use of the Harvest software is
67 * entirely the responsibility of the user.
68 *
69 * DERIVATIVE WORKS
70 *
71 * Users may make derivative works from the Harvest software, subject
72 * to the following constraints:
73 *
74 * - You must include the above copyright notice and these
75 * accompanying paragraphs in all forms of derivative works,
76 * and any documentation and other materials related to such
77 * distribution and use acknowledge that the software was
78 * developed at the above institutions.
79 *
80 * - You must notify IRTF-RD regarding your distribution of
81 * the derivative work.
82 *
83 * - You must clearly notify users that your are distributing
84 * a modified version and not the original Harvest software.
85 *
86 * - Any derivative product is also subject to these copyright
87 * and use restrictions.
88 *
89 * Note that the Harvest software is NOT in the public domain. We
90 * retain copyright, as specified above.
91 *
92 * HISTORY OF FREE SOFTWARE STATUS
93 *
94 * Originally we required sites to license the software in cases
95 * where they were going to build commercial products/services
96 * around Harvest. In June 1995 we changed this policy. We now
97 * allow people to use the core Harvest software (the code found in
98 * the Harvest ``src/'' directory) for free. We made this change
99 * in the interest of encouraging the widest possible deployment of
100 * the technology. The Harvest software is really a reference
101 * implementation of a set of protocols and formats, some of which
102 * we intend to standardize. We encourage commercial
103 * re-implementations of code complying to this set of standards.
104 */
105
106 #include "squid.h"
107
108 #define DISK_LINE_LEN 1024
109
110 typedef struct disk_ctrl_t {
111 int fd;
112 void *data;
113 } disk_ctrl_t;
114
115
116 typedef struct open_ctrl_t {
117 FOCB *callback;
118 void *callback_data;
119 char *path;
120 } open_ctrl_t;
121
122 static AIOCB diskHandleWriteComplete;
123 static AIOCB diskHandleReadComplete;
124 static PF diskHandleRead;
125 static PF diskHandleWrite;
126 static void file_open_complete(void *, int, int);
127
128 /* initialize table */
129 int
130 disk_init(void)
131 {
132 return 0;
133 }
134
135 /* Open a disk file. Return a file descriptor */
136 int
137 file_open(const char *path, int mode, FOCB * callback, void *callback_data)
138 {
139 int fd;
140 open_ctrl_t *ctrlp;
141
142 ctrlp = xmalloc(sizeof(open_ctrl_t));
143 ctrlp->path = xstrdup(path);
144 ctrlp->callback = callback;
145 ctrlp->callback_data = callback_data;
146
147 if (mode & O_WRONLY)
148 mode |= O_APPEND;
149 mode |= SQUID_NONBLOCK;
150
151 /* Open file */
152 #if USE_ASYNC_IO
153 if (callback != NULL) {
154 aioOpen(path, mode, 0644, file_open_complete, ctrlp);
155 return DISK_OK;
156 }
157 #endif
158 fd = open(path, mode, 0644);
159 file_open_complete(ctrlp, fd, errno);
160 if (fd < 0)
161 return DISK_ERROR;
162 return fd;
163 }
164
165
166 static void
167 file_open_complete(void *data, int fd, int errcode)
168 {
169 open_ctrl_t *ctrlp = (open_ctrl_t *) data;
170 if (fd < 0) {
171 errno = errcode;
172 debug(50, 0) ("file_open: error opening file %s: %s\n", ctrlp->path,
173 xstrerror());
174 if (ctrlp->callback)
175 (ctrlp->callback) (ctrlp->callback_data, DISK_ERROR);
176 xfree(ctrlp->path);
177 xfree(ctrlp);
178 return;
179 }
180 debug(6, 5) ("file_open: FD %d\n", fd);
181 commSetCloseOnExec(fd);
182 fd_open(fd, FD_FILE, ctrlp->path);
183 if (ctrlp->callback)
184 (ctrlp->callback) (ctrlp->callback_data, fd);
185 xfree(ctrlp->path);
186 xfree(ctrlp);
187 }
188
189 /* close a disk file. */
190 void
191 file_close(int fd)
192 {
193 fde *F = &fd_table[fd];
194 assert(fd >= 0);
195 assert(F->open);
196 if (BIT_TEST(F->flags, FD_WRITE_DAEMON)) {
197 BIT_SET(F->flags, FD_CLOSE_REQUEST);
198 return;
199 }
200 if (BIT_TEST(F->flags, FD_WRITE_PENDING)) {
201 BIT_SET(F->flags, FD_CLOSE_REQUEST);
202 return;
203 }
204 fd_close(fd);
205 debug(6, 5) ("file_close: FD %d\n", fd);
206 #if USE_ASYNC_IO
207 aioClose(fd);
208 #else
209 close(fd);
210 #endif
211 }
212
213
214 /* write handler */
215 static void
216 diskHandleWrite(int fd, void *notused)
217 {
218 int len = 0;
219 disk_ctrl_t *ctrlp;
220 dwrite_q *q = NULL;
221 dwrite_q *wq = NULL;
222 fde *F = &fd_table[fd];
223 struct _fde_disk *fdd = &F->disk;
224 if (!fdd->write_q)
225 return;
226 /* We need to combine subsequent write requests after the first */
227 if (fdd->write_q->next != NULL && fdd->write_q->next->next != NULL) {
228 len = 0;
229 for (q = fdd->write_q->next; q != NULL; q = q->next)
230 len += q->len - q->cur_offset;
231 wq = xcalloc(1, sizeof(dwrite_q));
232 wq->buf = xmalloc(len);
233 wq->len = 0;
234 wq->cur_offset = 0;
235 wq->next = NULL;
236 wq->free = xfree;
237 do {
238 q = fdd->write_q->next;
239 len = q->len - q->cur_offset;
240 xmemcpy(wq->buf + wq->len, q->buf + q->cur_offset, len);
241 wq->len += len;
242 fdd->write_q->next = q->next;
243 if (q->free)
244 (q->free) (q->buf);
245 safe_free(q);
246 } while (fdd->write_q->next != NULL);
247 fdd->write_q_tail = wq;
248 fdd->write_q->next = wq;
249 }
250 ctrlp = xcalloc(1, sizeof(disk_ctrl_t));
251 ctrlp->fd = fd;
252 assert(fdd->write_q != NULL);
253 assert(fdd->write_q->len > fdd->write_q->cur_offset);
254 #if USE_ASYNC_IO
255 aioWrite(fd,
256 fdd->write_q->buf + fdd->write_q->cur_offset,
257 fdd->write_q->len - fdd->write_q->cur_offset,
258 diskHandleWriteComplete,
259 ctrlp);
260 #else
261 len = write(fd,
262 fdd->write_q->buf + fdd->write_q->cur_offset,
263 fdd->write_q->len - fdd->write_q->cur_offset);
264 diskHandleWriteComplete(ctrlp, len, errno);
265 #endif
266 }
267
268 static void
269 diskHandleWriteComplete(void *data, int len, int errcode)
270 {
271 disk_ctrl_t *ctrlp = data;
272 int fd = ctrlp->fd;
273 fde *F = &fd_table[fd];
274 struct _fde_disk *fdd = &F->disk;
275 dwrite_q *q = fdd->write_q;
276 int status = DISK_OK;
277 errno = errcode;
278 safe_free(data);
279 fd_bytes(fd, len, FD_WRITE);
280 if (q == NULL) /* Someone aborted then write completed */
281 return;
282 if (len < 0) {
283 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
284 (void) 0;
285 } else {
286 status = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR;
287 debug(50, 1) ("diskHandleWrite: FD %d: disk write error: %s\n",
288 fd, xstrerror());
289 if (fdd->wrt_handle == NULL) {
290 /* FLUSH PENDING BUFFERS */
291 do {
292 fdd->write_q = q->next;
293 if (q->free)
294 (q->free) (q->buf);
295 safe_free(q);
296 } while ((q = fdd->write_q));
297 }
298 }
299 len = 0;
300 }
301 if (q != NULL) {
302 /* q might become NULL from write failure above */
303 q->cur_offset += len;
304 assert(q->cur_offset <= q->len);
305 if (q->cur_offset == q->len) {
306 /* complete write */
307 fdd->write_q = q->next;
308 if (q->free)
309 (q->free) (q->buf);
310 safe_free(q);
311 }
312 }
313 if (fdd->write_q == NULL) {
314 /* no more data */
315 fdd->write_q_tail = NULL;
316 BIT_CLR(F->flags, FD_WRITE_PENDING);
317 BIT_CLR(F->flags, FD_WRITE_DAEMON);
318 } else {
319 /* another block is queued */
320 commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
321 BIT_SET(F->flags, FD_WRITE_DAEMON);
322 }
323 if (fdd->wrt_handle)
324 fdd->wrt_handle(fd, status, len, fdd->wrt_handle_data);
325 if (BIT_TEST(F->flags, FD_CLOSE_REQUEST))
326 file_close(fd);
327 }
328
329
330 /* write block to a file */
331 /* write back queue. Only one writer at a time. */
332 /* call a handle when writing is complete. */
333 int
334 file_write(int fd,
335 char *ptr_to_buf,
336 int len,
337 DWCB handle,
338 void *handle_data,
339 FREE * free_func)
340 {
341 dwrite_q *wq = NULL;
342 fde *F = &fd_table[fd];
343 assert(fd >= 0);
344 assert(F->open);
345 /* if we got here. Caller is eligible to write. */
346 wq = xcalloc(1, sizeof(dwrite_q));
347 wq->buf = ptr_to_buf;
348 wq->len = len;
349 wq->cur_offset = 0;
350 wq->next = NULL;
351 wq->free = free_func;
352 F->disk.wrt_handle = handle;
353 F->disk.wrt_handle_data = handle_data;
354 /* add to queue */
355 BIT_SET(F->flags, FD_WRITE_PENDING);
356 if (F->disk.write_q == NULL) {
357 /* empty queue */
358 F->disk.write_q = F->disk.write_q_tail = wq;
359 } else {
360 F->disk.write_q_tail->next = wq;
361 F->disk.write_q_tail = wq;
362 }
363 if (!BIT_TEST(F->flags, FD_WRITE_DAEMON)) {
364 #if USE_ASYNC_IO
365 diskHandleWrite(fd, NULL);
366 #else
367 commSetSelect(fd, COMM_SELECT_WRITE, diskHandleWrite, NULL, 0);
368 #endif
369 BIT_SET(F->flags, FD_WRITE_DAEMON);
370 }
371 return DISK_OK;
372 }
373
374
375
376 /* Read from FD */
377 static void
378 diskHandleRead(int fd, void *data)
379 {
380 dread_ctrl *ctrl_dat = data;
381 fde *F = &fd_table[fd];
382 #if !USE_ASYNC_IO
383 int len;
384 #endif
385 disk_ctrl_t *ctrlp = xcalloc(1, sizeof(disk_ctrl_t));
386 ctrlp->fd = fd;
387 ctrlp->data = ctrl_dat;
388 #if USE_ASYNC_IO
389 aioRead(fd,
390 ctrl_dat->buf,
391 ctrl_dat->req_len,
392 diskHandleReadComplete,
393 ctrlp);
394 #else
395 if (F->disk.offset != ctrl_dat->offset) {
396 debug(6, 1) ("diskHandleRead: FD %d seeking to offset %d\n",
397 fd, (int) ctrl_dat->offset);
398 lseek(fd, ctrl_dat->offset, SEEK_SET); /* XXX ignore return? */
399 F->disk.offset = ctrl_dat->offset;
400 }
401 len = read(fd, ctrl_dat->buf, ctrl_dat->req_len);
402 F->disk.offset += len;
403 diskHandleReadComplete(ctrlp, len, errno);
404 #endif
405 }
406
407 static void
408 diskHandleReadComplete(void *data, int len, int errcode)
409 {
410 disk_ctrl_t *ctrlp = data;
411 dread_ctrl *ctrl_dat = ctrlp->data;
412 int fd = ctrlp->fd;
413 int rc = DISK_OK;
414 errno = errcode;
415 xfree(data);
416 fd_bytes(fd, len, FD_READ);
417 if (len < 0) {
418 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
419 commSetSelect(fd, COMM_SELECT_READ, diskHandleRead, ctrl_dat, 0);
420 return;
421 }
422 debug(50, 1) ("diskHandleRead: FD %d: %s\n", fd, xstrerror());
423 len = 0;
424 rc = DISK_ERROR;
425 } else if (len == 0) {
426 rc = DISK_EOF;
427 }
428 if (cbdataValid(ctrl_dat->client_data))
429 ctrl_dat->handler(fd, ctrl_dat->buf, len, rc, ctrl_dat->client_data);
430 cbdataUnlock(ctrl_dat->client_data);
431 safe_free(ctrl_dat);
432 }
433
434
435 /* start read operation */
436 /* buffer must be allocated from the caller.
437 * It must have at least req_len space in there.
438 * call handler when a reading is complete. */
439 int
440 file_read(int fd, char *buf, int req_len, int offset, DRCB * handler, void *client_data)
441 {
442 dread_ctrl *ctrl_dat;
443 assert(fd >= 0);
444 ctrl_dat = xcalloc(1, sizeof(dread_ctrl));
445 ctrl_dat->fd = fd;
446 ctrl_dat->offset = offset;
447 ctrl_dat->req_len = req_len;
448 ctrl_dat->buf = buf;
449 ctrl_dat->end_of_file = 0;
450 ctrl_dat->handler = handler;
451 ctrl_dat->client_data = client_data;
452 cbdataLock(client_data);
453 #if USE_ASYNC_IO
454 diskHandleRead(fd, ctrl_dat);
455 #else
456 commSetSelect(fd,
457 COMM_SELECT_READ,
458 diskHandleRead,
459 ctrl_dat,
460 0);
461 #endif
462 return DISK_OK;
463 }
464
465 int
466 diskWriteIsComplete(int fd)
467 {
468 return fd_table[fd].disk.write_q ? 0 : 1;
469 }