2 * $Id: disk.cc,v 1.91 1997/11/05 05:29:22 wessels Exp $
4 * DEBUG: section 6 Disk I/O Routines
5 * AUTHOR: Harvest Derived
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * --------------------------------------------------------
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.
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.
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.
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.
32 * Copyright (c) 1994, 1995. All rights reserved.
34 * The Harvest software was developed by the Internet Research Task
35 * Force Research Group on Resource Discovery (IRTF-RD):
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.
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.
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.
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.
71 * Users may make derivative works from the Harvest software, subject
72 * to the following constraints:
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.
80 * - You must notify IRTF-RD regarding your distribution of
81 * the derivative work.
83 * - You must clearly notify users that your are distributing
84 * a modified version and not the original Harvest software.
86 * - Any derivative product is also subject to these copyright
87 * and use restrictions.
89 * Note that the Harvest software is NOT in the public domain. We
90 * retain copyright, as specified above.
92 * HISTORY OF FREE SOFTWARE STATUS
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.
108 #define DISK_LINE_LEN 1024
110 typedef struct disk_ctrl_t
{
116 typedef struct open_ctrl_t
{
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);
128 /* initialize table */
135 /* Open a disk file. Return a file descriptor */
137 file_open(const char *path
, int mode
, FOCB
* callback
, void *callback_data
)
142 ctrlp
= xmalloc(sizeof(open_ctrl_t
));
143 ctrlp
->path
= xstrdup(path
);
144 ctrlp
->callback
= callback
;
145 ctrlp
->callback_data
= callback_data
;
149 mode
|= SQUID_NONBLOCK
;
153 if (callback
!= NULL
) {
154 aioOpen(path
, mode
, 0644, file_open_complete
, ctrlp
);
158 fd
= open(path
, mode
, 0644);
159 file_open_complete(ctrlp
, fd
, errno
);
167 file_open_complete(void *data
, int fd
, int errcode
)
169 open_ctrl_t
*ctrlp
= (open_ctrl_t
*) data
;
172 debug(50, 0) ("file_open: error opening file %s: %s\n", ctrlp
->path
,
175 (ctrlp
->callback
) (ctrlp
->callback_data
, DISK_ERROR
);
180 debug(6, 5) ("file_open: FD %d\n", fd
);
181 commSetCloseOnExec(fd
);
182 fd_open(fd
, FD_FILE
, ctrlp
->path
);
184 (ctrlp
->callback
) (ctrlp
->callback_data
, fd
);
189 /* close a disk file. */
193 fde
*F
= &fd_table
[fd
];
196 if (BIT_TEST(F
->flags
, FD_WRITE_DAEMON
)) {
197 BIT_SET(F
->flags
, FD_CLOSE_REQUEST
);
200 if (BIT_TEST(F
->flags
, FD_WRITE_PENDING
)) {
201 BIT_SET(F
->flags
, FD_CLOSE_REQUEST
);
205 debug(6, 5) ("file_close: FD %d\n", fd
);
216 diskHandleWrite(int fd
, void *notused
)
222 fde
*F
= &fd_table
[fd
];
223 struct _fde_disk
*fdd
= &F
->disk
;
226 /* We need to combine subsequent write requests after the first */
227 if (fdd
->write_q
->next
!= NULL
&& fdd
->write_q
->next
->next
!= NULL
) {
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
);
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
);
242 fdd
->write_q
->next
= q
->next
;
246 } while (fdd
->write_q
->next
!= NULL
);
247 fdd
->write_q_tail
= wq
;
248 fdd
->write_q
->next
= wq
;
250 ctrlp
= xcalloc(1, sizeof(disk_ctrl_t
));
252 assert(fdd
->write_q
!= NULL
);
253 assert(fdd
->write_q
->len
> fdd
->write_q
->cur_offset
);
256 fdd
->write_q
->buf
+ fdd
->write_q
->cur_offset
,
257 fdd
->write_q
->len
- fdd
->write_q
->cur_offset
,
258 diskHandleWriteComplete
,
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
);
269 diskHandleWriteComplete(void *data
, int len
, int errcode
)
271 disk_ctrl_t
*ctrlp
= data
;
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
;
279 fd_bytes(fd
, len
, FD_WRITE
);
280 if (q
== NULL
) /* Someone aborted then write completed */
283 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
) {
286 status
= errno
== ENOSPC
? DISK_NO_SPACE_LEFT
: DISK_ERROR
;
287 debug(50, 1) ("diskHandleWrite: FD %d: disk write error: %s\n",
289 if (fdd
->wrt_handle
== NULL
) {
290 /* FLUSH PENDING BUFFERS */
292 fdd
->write_q
= q
->next
;
296 } while ((q
= fdd
->write_q
));
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
) {
307 fdd
->write_q
= q
->next
;
313 if (fdd
->write_q
== NULL
) {
315 fdd
->write_q_tail
= NULL
;
316 BIT_CLR(F
->flags
, FD_WRITE_PENDING
);
317 BIT_CLR(F
->flags
, FD_WRITE_DAEMON
);
319 /* another block is queued */
320 commSetSelect(fd
, COMM_SELECT_WRITE
, diskHandleWrite
, NULL
, 0);
321 BIT_SET(F
->flags
, FD_WRITE_DAEMON
);
324 fdd
->wrt_handle(fd
, status
, len
, fdd
->wrt_handle_data
);
325 if (BIT_TEST(F
->flags
, FD_CLOSE_REQUEST
))
330 /* write block to a file */
331 /* write back queue. Only one writer at a time. */
332 /* call a handle when writing is complete. */
342 fde
*F
= &fd_table
[fd
];
345 /* if we got here. Caller is eligible to write. */
346 wq
= xcalloc(1, sizeof(dwrite_q
));
347 wq
->buf
= ptr_to_buf
;
351 wq
->free
= free_func
;
352 F
->disk
.wrt_handle
= handle
;
353 F
->disk
.wrt_handle_data
= handle_data
;
355 BIT_SET(F
->flags
, FD_WRITE_PENDING
);
356 if (F
->disk
.write_q
== NULL
) {
358 F
->disk
.write_q
= F
->disk
.write_q_tail
= wq
;
360 F
->disk
.write_q_tail
->next
= wq
;
361 F
->disk
.write_q_tail
= wq
;
363 if (!BIT_TEST(F
->flags
, FD_WRITE_DAEMON
)) {
365 diskHandleWrite(fd
, NULL
);
367 commSetSelect(fd
, COMM_SELECT_WRITE
, diskHandleWrite
, NULL
, 0);
369 BIT_SET(F
->flags
, FD_WRITE_DAEMON
);
378 diskHandleRead(int fd
, void *data
)
380 dread_ctrl
*ctrl_dat
= data
;
381 fde
*F
= &fd_table
[fd
];
385 disk_ctrl_t
*ctrlp
= xcalloc(1, sizeof(disk_ctrl_t
));
387 ctrlp
->data
= ctrl_dat
;
392 diskHandleReadComplete
,
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
;
401 len
= read(fd
, ctrl_dat
->buf
, ctrl_dat
->req_len
);
402 F
->disk
.offset
+= len
;
403 diskHandleReadComplete(ctrlp
, len
, errno
);
408 diskHandleReadComplete(void *data
, int len
, int errcode
)
410 disk_ctrl_t
*ctrlp
= data
;
411 dread_ctrl
*ctrl_dat
= ctrlp
->data
;
416 fd_bytes(fd
, len
, FD_READ
);
418 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
) {
419 commSetSelect(fd
, COMM_SELECT_READ
, diskHandleRead
, ctrl_dat
, 0);
422 debug(50, 1) ("diskHandleRead: FD %d: %s\n", fd
, xstrerror());
425 } else if (len
== 0) {
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
);
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. */
440 file_read(int fd
, char *buf
, int req_len
, int offset
, DRCB
* handler
, void *client_data
)
442 dread_ctrl
*ctrl_dat
;
444 ctrl_dat
= xcalloc(1, sizeof(dread_ctrl
));
446 ctrl_dat
->offset
= offset
;
447 ctrl_dat
->req_len
= req_len
;
449 ctrl_dat
->end_of_file
= 0;
450 ctrl_dat
->handler
= handler
;
451 ctrl_dat
->client_data
= client_data
;
452 cbdataLock(client_data
);
454 diskHandleRead(fd
, ctrl_dat
);
466 diskWriteIsComplete(int fd
)
468 return fd_table
[fd
].disk
.write_q
? 0 : 1;