]> git.ipfire.org Git - thirdparty/squid.git/blame - src/disk.cc
updating to squid-1.0.1
[thirdparty/squid.git] / src / disk.cc
CommitLineData
234967c9 1/* $Id: disk.cc,v 1.12 1996/05/01 22:36:27 wessels Exp $ */
1d30b295 2
3/* DEBUG: Section 6 disk: disk I/O routines */
ed43818f 4
44a47c6e 5#include "squid.h"
090089c4 6
7#define DISK_LINE_LEN 1024
8#define MAX_FILE_NAME_LEN 256
9
10typedef struct _dread_ctrl {
11 int fd;
12 off_t offset;
13 int req_len;
14 char *buf;
15 int cur_len;
16 int end_of_file;
2318883b 17 int (*handler) _PARAMS((int fd, char *buf, int size, int errflag, void *data,
090089c4 18 int offset));
2318883b 19 void *client_data;
090089c4 20} dread_ctrl;
21
22typedef struct _dwalk_ctrl {
23 int fd;
24 off_t offset;
25 char *buf; /* line buffer */
26 int cur_len; /* line len */
2318883b 27 int (*handler) _PARAMS((int fd, int errflag, void *data));
28 void *client_data;
29 int (*line_handler) _PARAMS((int fd, char *buf, int size, void *line_data));
30 void *line_data;
090089c4 31} dwalk_ctrl;
32
33typedef struct _dwrite_q {
51496678 34 char *buf;
090089c4 35 int len;
36 int cur_offset;
37 struct _dwrite_q *next;
38} dwrite_q;
39
40typedef struct _FileEntry {
41 char filename[MAX_FILE_NAME_LEN];
42 enum {
43 NO, YES
44 } at_eof;
45 enum {
46 NOT_OPEN, OPEN
47 } open_stat;
48 enum {
49 NOT_REQUEST, REQUEST
50 } close_request;
51 enum {
52 NOT_PRESENT, PRESENT
53 } write_daemon;
54 enum {
55 UNLOCK, LOCK
56 } write_lock;
57 int access_code; /* use to verify write lock */
58 enum {
59 NO_WRT_PENDING, WRT_PENDING
60 } write_pending;
61 void (*wrt_handle) ();
62 void *wrt_handle_data;
63 dwrite_q *write_q;
64 dwrite_q *write_q_tail;
65} FileEntry;
66
67
68/* table for FILE variable, write lock and queue. Indexed by fd. */
69FileEntry *file_table;
090089c4 70
71extern int getMaxFD();
72extern void fatal_dump _PARAMS((char *));
73
74/* initialize table */
75int disk_init()
76{
77 int fd, max_fd = getMaxFD();
78
090089c4 79 file_table = (FileEntry *) xmalloc(sizeof(FileEntry) * max_fd);
80 memset(file_table, '\0', sizeof(FileEntry) * max_fd);
81
82 for (fd = 0; fd < max_fd; fd++) {
83 file_table[fd].filename[0] = '\0';
84 file_table[fd].at_eof = NO;
85 file_table[fd].open_stat = NOT_OPEN;
86 file_table[fd].close_request = NOT_REQUEST;
87 file_table[fd].write_daemon = NOT_PRESENT;
88 file_table[fd].write_lock = UNLOCK;
89 file_table[fd].access_code = 0;
90 file_table[fd].write_pending = NO_WRT_PENDING;
91 file_table[fd].write_q = file_table[fd].write_q_tail = NULL;
92 }
090089c4 93 return 0;
94}
95
96/* Open a disk file. Return a file descriptor */
97int file_open(path, handler, mode)
98 char *path; /* path to file */
99 int (*handler) (); /* Interrupt handler. */
100 int mode;
101{
102 FD_ENTRY *conn;
103 int fd;
104
090089c4 105 /* Open file */
106 if ((fd = open(path, mode | O_NDELAY, 0644)) < 0) {
1d30b295 107 debug(6, 0, "file_open: error opening file %s: %s\n",
090089c4 108 path, xstrerror());
109 return (DISK_ERROR);
110 }
111 /* update fdstat */
112 fdstat_open(fd, File);
113
114 /* init table */
115 strncpy(file_table[fd].filename, path, MAX_FILE_NAME_LEN);
116 file_table[fd].at_eof = NO;
117 file_table[fd].open_stat = OPEN;
118 file_table[fd].close_request = NOT_REQUEST;
119 file_table[fd].write_lock = UNLOCK;
120 file_table[fd].write_pending = NO_WRT_PENDING;
121 file_table[fd].write_daemon = NOT_PRESENT;
122 file_table[fd].access_code = 0;
123 file_table[fd].write_q = NULL;
124
125 conn = &fd_table[fd];
126 memset(conn, 0, sizeof(FD_ENTRY));
127
128 conn->port = 0;
129 conn->handler = NULL;
130
131 /* set non-blocking mode */
ed43818f 132#if defined(O_NONBLOCK) && !defined(_SQUID_SUNOS_) && !defined(_SQUID_SOLARIS_)
090089c4 133 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
1d30b295 134 debug(6, 0, "file_open: FD %d: Failure to set O_NONBLOCK: %s\n",
090089c4 135 fd, xstrerror());
136 return DISK_ERROR;
137 }
138#else
d519f482 139 if (fcntl(fd, F_SETFL, O_NDELAY) < 0) {
1d30b295 140 debug(6, 0, "file_open: FD %d: Failure to set O_NDELAY: %s\n",
090089c4 141 fd, xstrerror());
142 return DISK_ERROR;
143 }
144#endif /* O_NONBLOCK */
145 conn->comm_type = COMM_NONBLOCKING;
146
147 return fd;
148}
149
150int file_update_open(fd, path)
151 int fd;
152 char *path; /* path to file */
153{
154 FD_ENTRY *conn;
155
090089c4 156 /* update fdstat */
157 fdstat_open(fd, File);
158
159 /* init table */
160 strncpy(file_table[fd].filename, path, MAX_FILE_NAME_LEN);
161 file_table[fd].at_eof = NO;
162 file_table[fd].open_stat = OPEN;
163 file_table[fd].close_request = NOT_REQUEST;
164 file_table[fd].write_lock = UNLOCK;
165 file_table[fd].write_pending = NO_WRT_PENDING;
166 file_table[fd].write_daemon = NOT_PRESENT;
167 file_table[fd].access_code = 0;
168 file_table[fd].write_q = NULL;
169
170 conn = &fd_table[fd];
171 memset(conn, 0, sizeof(FD_ENTRY));
172
173 conn->port = 0;
174 conn->handler = NULL;
175
176 conn->comm_type = COMM_NONBLOCKING;
177
178 return fd;
179}
180
181
182/* close a disk file. */
183int file_close(fd)
184 int fd; /* file descriptor */
185{
186 FD_ENTRY *conn = NULL;
187
188 /* we might have to flush all the write back queue before we can
189 * close it */
190 /* save it for later */
191
eaf73e50 192 if (file_table[fd].open_stat == NOT_OPEN) {
193 debug(6, 3, "file_close: FD %d is not OPEN\n", fd);
194 } else if (file_table[fd].write_daemon == PRESENT) {
195 debug(6, 3, "file_close: FD %d has a write daemon PRESENT\n", fd);
196 } else if (file_table[fd].write_pending == WRT_PENDING) {
197 debug(6, 3, "file_close: FD %d has a write PENDING\n", fd);
198 } else {
090089c4 199 file_table[fd].open_stat = NOT_OPEN;
200 file_table[fd].write_lock = UNLOCK;
201 file_table[fd].write_daemon = NOT_PRESENT;
202 file_table[fd].filename[0] = '\0';
203
204 if (fdstat_type(fd) == Socket) {
1d30b295 205 debug(6, 0, "FD %d: Someone called file_close() on a socket\n", fd);
090089c4 206 fatal_dump(NULL);
207 }
208 /* update fdstat */
209 fdstat_close(fd);
210 conn = &fd_table[fd];
211 memset(conn, '\0', sizeof(FD_ENTRY));
212 comm_set_fd_lifetime(fd, -1); /* invalidate the lifetime */
213 close(fd);
214 return DISK_OK;
090089c4 215 }
eaf73e50 216
217 /* refused to close file if there is a daemon running */
218 /* have pending flag set */
219 file_table[fd].close_request = REQUEST;
220 return DISK_ERROR;
090089c4 221}
222
223
224/* return a opened fd associate with given path name. */
225/* return DISK_FILE_NOT_FOUND if not found. */
226int file_get_fd(filename)
227 char *filename;
228{
229 int fd, max_fd = getMaxFD();
230 for (fd = 1; fd < max_fd; fd++) {
231 if (file_table[fd].open_stat == OPEN) {
232 if (strncmp(file_table[fd].filename, filename, MAX_FILE_NAME_LEN) == 0) {
233 return fd;
234 }
235 }
236 }
237 return DISK_FILE_NOT_FOUND;
238}
239
240/* grab a writing lock for file */
241int file_write_lock(fd)
242 int fd;
243{
244 if (file_table[fd].write_lock == LOCK) {
1d30b295 245 debug(6, 0, "trying to lock a locked file\n");
090089c4 246 return DISK_WRT_LOCK_FAIL;
247 } else {
248 file_table[fd].write_lock = LOCK;
249 file_table[fd].access_code += 1;
250 file_table[fd].access_code %= 65536;
251 return file_table[fd].access_code;
252 }
253}
254
255
256/* release a writing lock for file */
257int file_write_unlock(fd, access_code)
258 int fd;
259 int access_code;
260{
261 if (file_table[fd].access_code == access_code) {
262 file_table[fd].write_lock = UNLOCK;
263 return DISK_OK;
264 } else {
1d30b295 265 debug(6, 0, "trying to unlock the file with the wrong access code\n");
090089c4 266 return DISK_WRT_WRONG_CODE;
267 }
268}
269
270
271/* write handler */
272int diskHandleWrite(fd, entry)
273 int fd;
274 FileEntry *entry;
275{
276 int len;
277 dwrite_q *q;
278 int block_complete = 0;
279
280 if (file_table[fd].at_eof == NO)
281 lseek(fd, 0, SEEK_END);
282
283 for (;;) {
51496678 284 len = write(fd, (entry->write_q->buf) + entry->write_q->cur_offset,
090089c4 285 entry->write_q->len - entry->write_q->cur_offset);
286
287 file_table[fd].at_eof = YES;
288
289 if (len < 0) {
290 switch (errno) {
291#if EAGAIN != EWOULDBLOCK
292 case EAGAIN:
293#endif
294 case EWOULDBLOCK:
295 /* just reschedule itself, try again */
296 comm_set_select_handler(fd,
297 COMM_SELECT_WRITE,
298 (PF) diskHandleWrite,
51496678 299 (void *) entry);
090089c4 300 entry->write_daemon = PRESENT;
301 return DISK_OK;
302 default:
303 /* disk i/o failure--flushing all outstanding writes */
1d30b295 304 debug(6, 1, "diskHandleWrite: disk write error %s\n",
090089c4 305 xstrerror());
306 entry->write_daemon = NOT_PRESENT;
307 entry->write_pending = NO_WRT_PENDING;
308 /* call finish handler */
309 do {
310 q = entry->write_q;
311 entry->write_q = q->next;
312 if (!entry->wrt_handle) {
313 safe_free(q->buf);
314 } else {
315 /* XXXXXX
316 * Notice we call the handler multiple times but
317 * the write handler (in page mode) doesn't know
318 * the buf ptr so it'll be hard to deallocate
319 * memory.
320 * XXXXXX */
321 entry->wrt_handle(fd,
322 errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR,
323 entry->wrt_handle_data);
324 }
325 safe_free(q);
326 } while (entry->write_q);
327 return DISK_ERROR;
328 }
329 }
330 entry->write_q->cur_offset += len;
331 block_complete = (entry->write_q->cur_offset >= entry->write_q->len);
332
333 if (block_complete && (!entry->write_q->next)) {
334 /* No more data */
335 if (!entry->wrt_handle)
336 safe_free(entry->write_q->buf);
337 safe_free(entry->write_q);
338 entry->write_q = entry->write_q_tail = NULL;
339 entry->write_pending = NO_WRT_PENDING;
340 entry->write_daemon = NOT_PRESENT;
341 /* call finish handle */
342 if (entry->wrt_handle) {
343 entry->wrt_handle(fd, DISK_OK, entry->wrt_handle_data);
344 }
345 /* Close it if requested */
346 if (file_table[fd].close_request == REQUEST) {
347 file_close(fd);
348 }
349 return DISK_OK;
350 } else if ((block_complete) && (entry->write_q->next)) {
351 /* Do next block */
352
353 /* XXXXX THESE PRIMITIVES ARE WEIRD XXXXX
354 * If we have multiple blocks to send, we
355 * only call the completion handler once,
356 * so it becomes our job to free buffer space
357 */
358
359 q = entry->write_q;
360 entry->write_q = entry->write_q->next;
361 if (!entry->wrt_handle)
362 safe_free(q->buf);
363 safe_free(q);
364 /* Schedule next write
365 * comm_set_select_handler(fd, COMM_SELECT_WRITE, (PF) diskHandleWrite,
51496678 366 * (void *) entry);
090089c4 367 */
368 entry->write_daemon = PRESENT;
369 /* Repeat loop */
370 } else { /* !Block_completed; block incomplete */
371 /* reschedule */
372 comm_set_select_handler(fd, COMM_SELECT_WRITE, (PF) diskHandleWrite,
51496678 373 (void *) entry);
090089c4 374 entry->write_daemon = PRESENT;
375 return DISK_OK;
376 }
377 }
378}
379
380
381
382/* write block to a file */
383/* write back queue. Only one writer at a time. */
384/* call a handle when writing is complete. */
385int file_write(fd, ptr_to_buf, len, access_code, handle, handle_data)
386 int fd;
2318883b 387 char *ptr_to_buf;
090089c4 388 int len;
389 int access_code;
390 void (*handle) ();
391 void *handle_data;
392{
393 dwrite_q *wq;
394
395 if (file_table[fd].open_stat != OPEN) {
396 return DISK_ERROR;
397 }
398 if ((file_table[fd].write_lock == LOCK) &&
399 (file_table[fd].access_code != access_code)) {
9215c1da 400 debug(6, 0, "file write: FD %d access code checked failed.\n", fd);
090089c4 401 return DISK_WRT_WRONG_CODE;
402 }
403 /* if we got here. Caller is eligible to write. */
404 wq = (dwrite_q *) xcalloc(1, sizeof(dwrite_q));
405
406 wq->buf = ptr_to_buf;
407
408 wq->len = len;
409 wq->cur_offset = 0;
410 wq->next = NULL;
411 file_table[fd].wrt_handle = handle;
412 file_table[fd].wrt_handle_data = handle_data;
413
414 /* add to queue */
415 file_table[fd].write_pending = WRT_PENDING;
416 if (!(file_table[fd].write_q)) {
417 /* empty queue */
418 file_table[fd].write_q = file_table[fd].write_q_tail = wq;
419
420 } else {
421 file_table[fd].write_q_tail->next = wq;
422 file_table[fd].write_q_tail = wq;
423 }
424
425 if (file_table[fd].write_daemon == NOT_PRESENT) {
426 /* got to start write routine for this fd */
427 comm_set_select_handler(fd, COMM_SELECT_WRITE, (PF) diskHandleWrite,
2318883b 428 (void *) &file_table[fd]);
090089c4 429 }
430 return DISK_OK;
431}
432
433
434
435/* Read from FD */
436int diskHandleRead(fd, ctrl_dat)
437 int fd;
438 dread_ctrl *ctrl_dat;
439{
440 int len;
441
442 /* go to requested position. */
443 lseek(fd, ctrl_dat->offset, SEEK_SET);
444 file_table[fd].at_eof = NO;
445 len = read(fd, ctrl_dat->buf + ctrl_dat->cur_len,
446 ctrl_dat->req_len - ctrl_dat->cur_len);
447
448 if (len < 0)
449 switch (errno) {
450#if EAGAIN != EWOULDBLOCK
451 case EAGAIN:
452#endif
453 case EWOULDBLOCK:
454 break;
455 default:
1d30b295 456 debug(6, 1, "diskHandleRead: FD %d: error reading: %s\n",
090089c4 457 fd, xstrerror());
458 ctrl_dat->handler(fd, ctrl_dat->buf,
459 ctrl_dat->cur_len, DISK_ERROR,
460 ctrl_dat->client_data, ctrl_dat->offset);
461 safe_free(ctrl_dat);
462 return DISK_ERROR;
463 } else if (len == 0) {
464 /* EOF */
465 ctrl_dat->end_of_file = 1;
466 /* call handler */
467 ctrl_dat->handler(fd, ctrl_dat->buf, ctrl_dat->cur_len, DISK_EOF,
468 ctrl_dat->client_data, ctrl_dat->offset);
469 safe_free(ctrl_dat);
470 return DISK_OK;
471 }
472 ctrl_dat->cur_len += len;
473 ctrl_dat->offset = lseek(fd, 0L, SEEK_CUR);
474
475 /* reschedule if need more data. */
476 if (ctrl_dat->cur_len < ctrl_dat->req_len) {
234967c9 477 comm_set_select_handler(fd,
478 COMM_SELECT_READ,
479 (PF) diskHandleRead,
51496678 480 (void *) ctrl_dat);
090089c4 481 return DISK_OK;
482 } else {
483 /* all data we need is here. */
484 /* calll handler */
485 ctrl_dat->handler(fd, ctrl_dat->buf, ctrl_dat->cur_len, DISK_OK,
486 ctrl_dat->client_data, ctrl_dat->offset);
487 safe_free(ctrl_dat);
488 return DISK_OK;
489 }
490}
491
492
493/* start read operation */
494/* buffer must be allocated from the caller.
495 * It must have at least req_len space in there.
496 * call handler when a reading is complete. */
497int file_read(fd, buf, req_len, offset, handler, client_data)
498 int fd;
51496678 499 char *buf;
090089c4 500 int req_len;
501 int offset;
502 FILE_READ_HD handler;
2318883b 503 void *client_data;
090089c4 504{
505 dread_ctrl *ctrl_dat;
506
507 ctrl_dat = (dread_ctrl *) xmalloc(sizeof(dread_ctrl));
508 memset(ctrl_dat, '\0', sizeof(dread_ctrl));
509 ctrl_dat->fd = fd;
510 ctrl_dat->offset = offset;
511 ctrl_dat->req_len = req_len;
512 ctrl_dat->buf = buf;
513 ctrl_dat->cur_len = 0;
514 ctrl_dat->end_of_file = 0;
515 ctrl_dat->handler = handler;
516 ctrl_dat->client_data = client_data;
517
234967c9 518 comm_set_select_handler(fd,
519 COMM_SELECT_READ,
520 (PF) diskHandleRead,
51496678 521 (void *) ctrl_dat);
090089c4 522
523 return DISK_OK;
524}
525
526
527/* Read from FD and pass a line to routine. Walk to EOF. */
528int diskHandleWalk(fd, walk_dat)
529 int fd;
530 dwalk_ctrl *walk_dat;
531{
532 int len;
533 int end_pos;
534 int st_pos;
535 int used_bytes;
536 char temp_line[DISK_LINE_LEN];
537
538 lseek(fd, walk_dat->offset, SEEK_SET);
539 file_table[fd].at_eof = NO;
540 len = read(fd, walk_dat->buf, DISK_LINE_LEN - 1);
541
542 if (len < 0)
543 switch (errno) {
544#if EAGAIN != EWOULDBLOCK
545 case EAGAIN:
546#endif
547 case EWOULDBLOCK:
548 break;
549 default:
1d30b295 550 debug(6, 1, "diskHandleWalk: FD %d: error readingd: %s\n",
090089c4 551 fd, xstrerror());
552 walk_dat->handler(fd, DISK_ERROR, walk_dat->client_data);
553 safe_free(walk_dat->buf);
554 safe_free(walk_dat);
555 return DISK_ERROR;
556 } else if (len == 0) {
557 /* EOF */
558 walk_dat->handler(fd, DISK_EOF, walk_dat->client_data);
559 safe_free(walk_dat->buf);
560 safe_free(walk_dat);
561 return DISK_OK;
562 }
563 /* emulate fgets here. Cut the into separate line. newline is excluded */
564 /* it throws last partial line, if exist, away. */
565 used_bytes = st_pos = end_pos = 0;
566 while (end_pos < len) {
567 if (walk_dat->buf[end_pos] == '\n') {
568 /* new line found */
569 strncpy(temp_line, walk_dat->buf + st_pos, end_pos - st_pos);
570 temp_line[end_pos - st_pos] = '\0';
571 used_bytes += end_pos - st_pos + 1;
572
573 /* invoke line handler */
574 walk_dat->line_handler(fd, temp_line, strlen(temp_line),
575 walk_dat->line_data);
576
577 /* skip to next line */
578 st_pos = end_pos + 1;
579 }
580 end_pos++;
581 }
582
583 /* update file pointer to the next to be read character */
584 walk_dat->offset += used_bytes;
585
586 /* reschedule it for next line. */
587 comm_set_select_handler(fd, COMM_SELECT_READ, (PF) diskHandleWalk,
51496678 588 (void *) walk_dat);
090089c4 589 return DISK_OK;
590}
591
592
593/* start walk through whole file operation
594 * read one block and chop it to a line and pass it to provided
595 * handler one line at a time.
596 * call a completion handler when done. */
597int file_walk(fd, handler, client_data, line_handler, line_data)
598 int fd;
599 FILE_WALK_HD handler;
2318883b 600 void *client_data;
090089c4 601 FILE_WALK_LHD line_handler;
2318883b 602 void *line_data;
090089c4 603
604{
605 dwalk_ctrl *walk_dat;
606
607 walk_dat = (dwalk_ctrl *) xmalloc(sizeof(dwalk_ctrl));
608 memset(walk_dat, '\0', sizeof(dwalk_ctrl));
609 walk_dat->fd = fd;
610 walk_dat->offset = 0;
51496678 611 walk_dat->buf = (void *) xcalloc(1, DISK_LINE_LEN);
090089c4 612 walk_dat->cur_len = 0;
613 walk_dat->handler = handler;
614 walk_dat->client_data = client_data;
615 walk_dat->line_handler = line_handler;
616 walk_dat->line_data = line_data;
617
618 comm_set_select_handler(fd, COMM_SELECT_READ, (PF) diskHandleWalk,
51496678 619 (void *) walk_dat);
090089c4 620 return DISK_OK;
621}
622
623char *diskFileName(fd)
624 int fd;
625{
626 if (file_table[fd].filename[0])
627 return (file_table[fd].filename);
628 else
629 return (0);
630}