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