]> git.ipfire.org Git - thirdparty/squid.git/blame - src/disk.cc
ANSIFY
[thirdparty/squid.git] / src / disk.cc
CommitLineData
30a4f2a8 1/*
3ca60c86 2 * $Id: disk.cc,v 1.22 1996/08/30 22:36:28 wessels Exp $
30a4f2a8 3 *
4 * DEBUG: section 6 Disk I/O Routines
5 * AUTHOR: Harvest Derived
6 *
7 * SQUID Internet Object Cache http://www.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 */
ed43818f 105
44a47c6e 106#include "squid.h"
090089c4 107
108#define DISK_LINE_LEN 1024
090089c4 109
110typedef struct _dwalk_ctrl {
111 int fd;
112 off_t offset;
113 char *buf; /* line buffer */
114 int cur_len; /* line len */
2318883b 115 int (*handler) _PARAMS((int fd, int errflag, void *data));
116 void *client_data;
117 int (*line_handler) _PARAMS((int fd, char *buf, int size, void *line_data));
118 void *line_data;
090089c4 119} dwalk_ctrl;
120
090089c4 121/* table for FILE variable, write lock and queue. Indexed by fd. */
122FileEntry *file_table;
090089c4 123
090089c4 124/* initialize table */
125int disk_init()
126{
30a4f2a8 127 int fd;
090089c4 128
30a4f2a8 129 file_table = xcalloc(FD_SETSIZE, sizeof(FileEntry));
130 meta_data.misc += FD_SETSIZE * sizeof(FileEntry);
131 for (fd = 0; fd < FD_SETSIZE; fd++) {
090089c4 132 file_table[fd].filename[0] = '\0';
133 file_table[fd].at_eof = NO;
c6ac7aae 134 file_table[fd].open_stat = FILE_NOT_OPEN;
090089c4 135 file_table[fd].close_request = NOT_REQUEST;
136 file_table[fd].write_daemon = NOT_PRESENT;
137 file_table[fd].write_lock = UNLOCK;
138 file_table[fd].access_code = 0;
139 file_table[fd].write_pending = NO_WRT_PENDING;
140 file_table[fd].write_q = file_table[fd].write_q_tail = NULL;
141 }
090089c4 142 return 0;
143}
144
145/* Open a disk file. Return a file descriptor */
146int file_open(path, handler, mode)
147 char *path; /* path to file */
148 int (*handler) (); /* Interrupt handler. */
149 int mode;
150{
151 FD_ENTRY *conn;
152 int fd;
153
b59c7120 154 if (mode & O_RDWR)
155 fatal_dump("file_open: O_RDWR not allowed");
156 if (mode & O_WRONLY)
157 mode |= O_APPEND;
158 mode |= O_NDELAY;
159
090089c4 160 /* Open file */
b59c7120 161 if ((fd = open(path, mode, 0644)) < 0) {
1d30b295 162 debug(6, 0, "file_open: error opening file %s: %s\n",
090089c4 163 path, xstrerror());
164 return (DISK_ERROR);
165 }
166 /* update fdstat */
30a4f2a8 167 fdstat_open(fd, FD_FILE);
3ca60c86 168 commSetCloseOnExec(fd);
090089c4 169
170 /* init table */
171 strncpy(file_table[fd].filename, path, MAX_FILE_NAME_LEN);
172 file_table[fd].at_eof = NO;
c6ac7aae 173 file_table[fd].open_stat = FILE_OPEN;
090089c4 174 file_table[fd].close_request = NOT_REQUEST;
175 file_table[fd].write_lock = UNLOCK;
176 file_table[fd].write_pending = NO_WRT_PENDING;
177 file_table[fd].write_daemon = NOT_PRESENT;
178 file_table[fd].access_code = 0;
179 file_table[fd].write_q = NULL;
180
181 conn = &fd_table[fd];
30a4f2a8 182 memset(conn, '\0', sizeof(FD_ENTRY));
183 if (commSetNonBlocking(fd) == COMM_ERROR)
090089c4 184 return DISK_ERROR;
090089c4 185 conn->comm_type = COMM_NONBLOCKING;
090089c4 186 return fd;
187}
188
b59c7120 189#ifdef UNUSED_CODE
090089c4 190int file_update_open(fd, path)
191 int fd;
192 char *path; /* path to file */
193{
194 FD_ENTRY *conn;
195
090089c4 196 /* update fdstat */
30a4f2a8 197 fdstat_open(fd, FD_FILE);
090089c4 198
199 /* init table */
200 strncpy(file_table[fd].filename, path, MAX_FILE_NAME_LEN);
201 file_table[fd].at_eof = NO;
c6ac7aae 202 file_table[fd].open_stat = FILE_OPEN;
090089c4 203 file_table[fd].close_request = NOT_REQUEST;
204 file_table[fd].write_lock = UNLOCK;
205 file_table[fd].write_pending = NO_WRT_PENDING;
206 file_table[fd].write_daemon = NOT_PRESENT;
207 file_table[fd].access_code = 0;
208 file_table[fd].write_q = NULL;
209
210 conn = &fd_table[fd];
30a4f2a8 211 memset(conn, '\0', sizeof(FD_ENTRY));
090089c4 212 conn->comm_type = COMM_NONBLOCKING;
090089c4 213 return fd;
214}
b59c7120 215#endif
090089c4 216
217
218/* close a disk file. */
219int file_close(fd)
220 int fd; /* file descriptor */
221{
222 FD_ENTRY *conn = NULL;
223
224 /* we might have to flush all the write back queue before we can
225 * close it */
226 /* save it for later */
227
c6ac7aae 228 if (file_table[fd].open_stat == FILE_NOT_OPEN) {
eaf73e50 229 debug(6, 3, "file_close: FD %d is not OPEN\n", fd);
230 } else if (file_table[fd].write_daemon == PRESENT) {
231 debug(6, 3, "file_close: FD %d has a write daemon PRESENT\n", fd);
232 } else if (file_table[fd].write_pending == WRT_PENDING) {
233 debug(6, 3, "file_close: FD %d has a write PENDING\n", fd);
234 } else {
c6ac7aae 235 file_table[fd].open_stat = FILE_NOT_OPEN;
090089c4 236 file_table[fd].write_lock = UNLOCK;
237 file_table[fd].write_daemon = NOT_PRESENT;
238 file_table[fd].filename[0] = '\0';
239
555198ac 240 if (fdstatGetType(fd) == FD_SOCKET) {
1d30b295 241 debug(6, 0, "FD %d: Someone called file_close() on a socket\n", fd);
090089c4 242 fatal_dump(NULL);
243 }
244 /* update fdstat */
245 fdstat_close(fd);
246 conn = &fd_table[fd];
247 memset(conn, '\0', sizeof(FD_ENTRY));
248 comm_set_fd_lifetime(fd, -1); /* invalidate the lifetime */
249 close(fd);
250 return DISK_OK;
090089c4 251 }
eaf73e50 252
253 /* refused to close file if there is a daemon running */
254 /* have pending flag set */
255 file_table[fd].close_request = REQUEST;
256 return DISK_ERROR;
090089c4 257}
258
090089c4 259/* grab a writing lock for file */
260int file_write_lock(fd)
261 int fd;
262{
263 if (file_table[fd].write_lock == LOCK) {
1d30b295 264 debug(6, 0, "trying to lock a locked file\n");
090089c4 265 return DISK_WRT_LOCK_FAIL;
266 } else {
267 file_table[fd].write_lock = LOCK;
268 file_table[fd].access_code += 1;
269 file_table[fd].access_code %= 65536;
270 return file_table[fd].access_code;
271 }
272}
273
274
275/* release a writing lock for file */
276int file_write_unlock(fd, access_code)
277 int fd;
278 int access_code;
279{
280 if (file_table[fd].access_code == access_code) {
281 file_table[fd].write_lock = UNLOCK;
282 return DISK_OK;
283 } else {
1d30b295 284 debug(6, 0, "trying to unlock the file with the wrong access code\n");
090089c4 285 return DISK_WRT_WRONG_CODE;
286 }
287}
288
289
290/* write handler */
291int diskHandleWrite(fd, entry)
292 int fd;
293 FileEntry *entry;
294{
295 int len;
b59c7120 296 dwrite_q *q = NULL;
090089c4 297
298 if (file_table[fd].at_eof == NO)
299 lseek(fd, 0, SEEK_END);
300
b59c7120 301 while (entry->write_q) {
302 len = write(fd,
303 entry->write_q->buf + entry->write_q->cur_offset,
090089c4 304 entry->write_q->len - entry->write_q->cur_offset);
090089c4 305 file_table[fd].at_eof = YES;
090089c4 306 if (len < 0) {
b59c7120 307 if (errno == EAGAIN || errno == EWOULDBLOCK) {
308 /* just reschedule us, try again */
090089c4 309 comm_set_select_handler(fd,
310 COMM_SELECT_WRITE,
311 (PF) diskHandleWrite,
51496678 312 (void *) entry);
090089c4 313 entry->write_daemon = PRESENT;
314 return DISK_OK;
b59c7120 315 } else {
090089c4 316 /* disk i/o failure--flushing all outstanding writes */
30a4f2a8 317 debug(6, 1, "diskHandleWrite: FD %d: disk write error: %s\n",
318 fd, xstrerror());
090089c4 319 entry->write_daemon = NOT_PRESENT;
320 entry->write_pending = NO_WRT_PENDING;
321 /* call finish handler */
322 do {
323 q = entry->write_q;
324 entry->write_q = q->next;
b59c7120 325 if (q->free)
326 (q->free) (q->buf);
090089c4 327 safe_free(q);
328 } while (entry->write_q);
b59c7120 329 if (entry->wrt_handle) {
330 entry->wrt_handle(fd,
331 errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR,
332 entry->wrt_handle_data);
333 }
090089c4 334 return DISK_ERROR;
335 }
336 }
337 entry->write_q->cur_offset += len;
b59c7120 338 if (entry->write_q->cur_offset < entry->write_q->len) {
339 continue; /* partial write? */
340 } else {
341 /* complete write */
090089c4 342 q = entry->write_q;
b59c7120 343 entry->write_q = q->next;
344 if (q->free)
345 (q->free) (q->buf);
090089c4 346 safe_free(q);
090089c4 347 }
348 }
b59c7120 349 /* no more data */
350 entry->write_q = entry->write_q_tail = NULL;
351 entry->write_pending = NO_WRT_PENDING;
352 entry->write_daemon = NOT_PRESENT;
353 if (entry->wrt_handle)
354 entry->wrt_handle(fd, DISK_OK, entry->wrt_handle_data);
355 if (file_table[fd].close_request == REQUEST)
356 file_close(fd);
357 return DISK_OK;
090089c4 358}
359
360
361
362/* write block to a file */
363/* write back queue. Only one writer at a time. */
364/* call a handle when writing is complete. */
b59c7120 365int file_write(fd, ptr_to_buf, len, access_code, handle, handle_data, free)
090089c4 366 int fd;
2318883b 367 char *ptr_to_buf;
090089c4 368 int len;
369 int access_code;
370 void (*handle) ();
371 void *handle_data;
b59c7120 372 void (*free) _PARAMS((void *));
090089c4 373{
c6ac7aae 374 dwrite_q *wq = NULL;
090089c4 375
c6ac7aae 376 if (file_table[fd].open_stat == FILE_NOT_OPEN)
090089c4 377 return DISK_ERROR;
090089c4 378 if ((file_table[fd].write_lock == LOCK) &&
379 (file_table[fd].access_code != access_code)) {
9215c1da 380 debug(6, 0, "file write: FD %d access code checked failed.\n", fd);
090089c4 381 return DISK_WRT_WRONG_CODE;
382 }
383 /* if we got here. Caller is eligible to write. */
30a4f2a8 384 wq = xcalloc(1, sizeof(dwrite_q));
090089c4 385 wq->buf = ptr_to_buf;
090089c4 386 wq->len = len;
387 wq->cur_offset = 0;
388 wq->next = NULL;
b59c7120 389 wq->free = free;
090089c4 390 file_table[fd].wrt_handle = handle;
391 file_table[fd].wrt_handle_data = handle_data;
392
393 /* add to queue */
394 file_table[fd].write_pending = WRT_PENDING;
395 if (!(file_table[fd].write_q)) {
396 /* empty queue */
397 file_table[fd].write_q = file_table[fd].write_q_tail = wq;
090089c4 398 } else {
399 file_table[fd].write_q_tail->next = wq;
400 file_table[fd].write_q_tail = wq;
401 }
402
898f5d1d 403 if (file_table[fd].write_daemon == PRESENT)
404 return DISK_OK;
898f5d1d 405#if USE_ASYNC_IO
406 return aioFileQueueWrite(fd,
94e891ea 407 aioFileWriteComplete,
898f5d1d 408 &file_table[fd]);
409#else
410 comm_set_select_handler(fd,
411 COMM_SELECT_WRITE,
412 (PF) diskHandleWrite,
b560dd20 413 (void *) &file_table[fd]);
090089c4 414 return DISK_OK;
898f5d1d 415#endif
090089c4 416}
417
418
419
420/* Read from FD */
421int diskHandleRead(fd, ctrl_dat)
422 int fd;
423 dread_ctrl *ctrl_dat;
424{
425 int len;
426
427 /* go to requested position. */
428 lseek(fd, ctrl_dat->offset, SEEK_SET);
429 file_table[fd].at_eof = NO;
5409636c 430 len = read(fd,
431 ctrl_dat->buf + ctrl_dat->cur_len,
090089c4 432 ctrl_dat->req_len - ctrl_dat->cur_len);
433
434 if (len < 0)
435 switch (errno) {
436#if EAGAIN != EWOULDBLOCK
437 case EAGAIN:
438#endif
439 case EWOULDBLOCK:
440 break;
441 default:
1d30b295 442 debug(6, 1, "diskHandleRead: FD %d: error reading: %s\n",
090089c4 443 fd, xstrerror());
444 ctrl_dat->handler(fd, ctrl_dat->buf,
445 ctrl_dat->cur_len, DISK_ERROR,
446 ctrl_dat->client_data, ctrl_dat->offset);
447 safe_free(ctrl_dat);
448 return DISK_ERROR;
449 } else if (len == 0) {
450 /* EOF */
451 ctrl_dat->end_of_file = 1;
452 /* call handler */
453 ctrl_dat->handler(fd, ctrl_dat->buf, ctrl_dat->cur_len, DISK_EOF,
454 ctrl_dat->client_data, ctrl_dat->offset);
455 safe_free(ctrl_dat);
456 return DISK_OK;
457 }
458 ctrl_dat->cur_len += len;
459 ctrl_dat->offset = lseek(fd, 0L, SEEK_CUR);
460
461 /* reschedule if need more data. */
462 if (ctrl_dat->cur_len < ctrl_dat->req_len) {
234967c9 463 comm_set_select_handler(fd,
464 COMM_SELECT_READ,
465 (PF) diskHandleRead,
51496678 466 (void *) ctrl_dat);
090089c4 467 return DISK_OK;
468 } else {
469 /* all data we need is here. */
5409636c 470 /* call handler */
471 ctrl_dat->handler(fd,
472 ctrl_dat->buf,
473 ctrl_dat->cur_len,
474 DISK_OK,
475 ctrl_dat->client_data,
476 ctrl_dat->offset);
090089c4 477 safe_free(ctrl_dat);
478 return DISK_OK;
479 }
480}
481
482
483/* start read operation */
484/* buffer must be allocated from the caller.
485 * It must have at least req_len space in there.
486 * call handler when a reading is complete. */
487int file_read(fd, buf, req_len, offset, handler, client_data)
488 int fd;
51496678 489 char *buf;
090089c4 490 int req_len;
491 int offset;
492 FILE_READ_HD handler;
2318883b 493 void *client_data;
090089c4 494{
495 dread_ctrl *ctrl_dat;
496
30a4f2a8 497 ctrl_dat = xcalloc(1, sizeof(dread_ctrl));
090089c4 498 ctrl_dat->fd = fd;
499 ctrl_dat->offset = offset;
500 ctrl_dat->req_len = req_len;
501 ctrl_dat->buf = buf;
502 ctrl_dat->cur_len = 0;
503 ctrl_dat->end_of_file = 0;
504 ctrl_dat->handler = handler;
505 ctrl_dat->client_data = client_data;
506
898f5d1d 507#if USE_ASYNC_IO
94e891ea 508 return aioFileQueueRead(fd, aioFileReadComplete, ctrl_dat);
898f5d1d 509#else
234967c9 510 comm_set_select_handler(fd,
511 COMM_SELECT_READ,
512 (PF) diskHandleRead,
51496678 513 (void *) ctrl_dat);
090089c4 514 return DISK_OK;
898f5d1d 515#endif
090089c4 516}
517
518
519/* Read from FD and pass a line to routine. Walk to EOF. */
520int diskHandleWalk(fd, walk_dat)
521 int fd;
522 dwalk_ctrl *walk_dat;
523{
524 int len;
525 int end_pos;
526 int st_pos;
527 int used_bytes;
95d659f0 528 LOCAL_ARRAY(char, temp_line, DISK_LINE_LEN);
090089c4 529
530 lseek(fd, walk_dat->offset, SEEK_SET);
531 file_table[fd].at_eof = NO;
532 len = read(fd, walk_dat->buf, DISK_LINE_LEN - 1);
533
534 if (len < 0)
535 switch (errno) {
536#if EAGAIN != EWOULDBLOCK
537 case EAGAIN:
538#endif
539 case EWOULDBLOCK:
540 break;
541 default:
1d30b295 542 debug(6, 1, "diskHandleWalk: FD %d: error readingd: %s\n",
090089c4 543 fd, xstrerror());
544 walk_dat->handler(fd, DISK_ERROR, walk_dat->client_data);
545 safe_free(walk_dat->buf);
546 safe_free(walk_dat);
547 return DISK_ERROR;
548 } else if (len == 0) {
549 /* EOF */
550 walk_dat->handler(fd, DISK_EOF, walk_dat->client_data);
551 safe_free(walk_dat->buf);
552 safe_free(walk_dat);
553 return DISK_OK;
554 }
555 /* emulate fgets here. Cut the into separate line. newline is excluded */
556 /* it throws last partial line, if exist, away. */
557 used_bytes = st_pos = end_pos = 0;
558 while (end_pos < len) {
559 if (walk_dat->buf[end_pos] == '\n') {
560 /* new line found */
561 strncpy(temp_line, walk_dat->buf + st_pos, end_pos - st_pos);
562 temp_line[end_pos - st_pos] = '\0';
563 used_bytes += end_pos - st_pos + 1;
564
565 /* invoke line handler */
566 walk_dat->line_handler(fd, temp_line, strlen(temp_line),
567 walk_dat->line_data);
568
569 /* skip to next line */
570 st_pos = end_pos + 1;
571 }
572 end_pos++;
573 }
574
575 /* update file pointer to the next to be read character */
576 walk_dat->offset += used_bytes;
577
578 /* reschedule it for next line. */
579 comm_set_select_handler(fd, COMM_SELECT_READ, (PF) diskHandleWalk,
51496678 580 (void *) walk_dat);
090089c4 581 return DISK_OK;
582}
583
584
585/* start walk through whole file operation
586 * read one block and chop it to a line and pass it to provided
587 * handler one line at a time.
588 * call a completion handler when done. */
589int file_walk(fd, handler, client_data, line_handler, line_data)
590 int fd;
591 FILE_WALK_HD handler;
2318883b 592 void *client_data;
090089c4 593 FILE_WALK_LHD line_handler;
2318883b 594 void *line_data;
090089c4 595
596{
597 dwalk_ctrl *walk_dat;
598
30a4f2a8 599 walk_dat = xcalloc(1, sizeof(dwalk_ctrl));
090089c4 600 walk_dat->fd = fd;
601 walk_dat->offset = 0;
30a4f2a8 602 walk_dat->buf = xcalloc(1, DISK_LINE_LEN);
090089c4 603 walk_dat->cur_len = 0;
604 walk_dat->handler = handler;
605 walk_dat->client_data = client_data;
606 walk_dat->line_handler = line_handler;
607 walk_dat->line_data = line_data;
608
609 comm_set_select_handler(fd, COMM_SELECT_READ, (PF) diskHandleWalk,
51496678 610 (void *) walk_dat);
090089c4 611 return DISK_OK;
612}
613
614char *diskFileName(fd)
615 int fd;
616{
617 if (file_table[fd].filename[0])
618 return (file_table[fd].filename);
619 else
620 return (0);
621}
b59c7120 622
623int diskWriteIsComplete(fd)
624{
625 return file_table[fd].write_q ? 0 : 1;
626}