]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/fs_io.cc
51876cb40ee88c61bfa069fd6cdd4887ee397ae1
2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 06 Disk I/O Routines */
12 #include "comm/Loops.h"
18 #include "profiler/Profiler.h"
19 #include "StatCounters.h"
23 static PF diskHandleRead
;
24 static PF diskHandleWrite
;
26 #if _SQUID_WINDOWS_ || _SQUID_OS2_
28 diskWriteIsComplete(int fd
)
30 return fd_table
[fd
].disk
.write_q
? 0 : 1;
35 /* hack needed on SunStudio to avoid linkage convention mismatch */
36 static void cxx_xfree(void *ptr
)
42 * opens a disk file specified by 'path'. This function always
43 * blocks! There is no callback.
46 file_open(const char *path
, int mode
)
49 PROF_start(file_open
);
51 if (FILE_MODE(mode
) == O_WRONLY
)
56 fd
= open(path
, mode
, 0644);
58 ++ statCounter
.syscalls
.disk
.opens
;
62 debugs(50, 3, "error opening file " << path
<< ": " << xstrerr(xerrno
));
65 debugs(6, 5, "FD " << fd
);
66 commSetCloseOnExec(fd
);
67 fd_open(fd
, FD_FILE
, path
);
74 /* close a disk file. */
78 fde
*F
= &fd_table
[fd
];
80 PROF_start(file_close
);
82 assert(F
->flags
.open
);
84 if ((read_callback
= F
->read_handler
)) {
85 F
->read_handler
= NULL
;
86 read_callback(-1, F
->read_data
);
89 if (F
->flags
.write_daemon
) {
90 #if _SQUID_WINDOWS_ || _SQUID_OS2_
92 * on some operating systems, you can not delete or rename
93 * open files, so we won't allow delayed close.
95 while (!diskWriteIsComplete(fd
))
96 diskHandleWrite(fd
, NULL
);
98 F
->flags
.close_request
= true;
99 debugs(6, 2, "file_close: FD " << fd
<< ", delaying close");
100 PROF_stop(file_close
);
107 * Assert there is no write callback. Otherwise we might be
108 * leaking write state data by closing the descriptor
110 assert(F
->write_handler
== NULL
);
112 #if CALL_FSYNC_BEFORE_CLOSE
120 debugs(6, F
->flags
.close_request
? 2 : 5, "file_close: FD " << fd
<< " really closing");
124 ++ statCounter
.syscalls
.disk
.closes
;
126 PROF_stop(file_close
);
130 * This function has the purpose of combining multiple writes. This is
131 * to facilitate the ASYNC_IO option since it can only guarantee 1
132 * write to a file per trip around the comm.c select() loop. That's bad
133 * because more than 1 write can be made to the access.log file per
134 * trip, and so this code is purely designed to help batch multiple
135 * sequential writes to the access.log file. Squid will never issue
136 * multiple writes for any other file type during 1 trip around the
137 * select() loop. --SLF
140 diskCombineWrites(_fde_disk
*fdd
)
143 * We need to combine multiple write requests on an FD's write
144 * queue But only if we don't need to seek() in between them, ugh!
145 * XXX This currently ignores any seeks (file_offset)
148 if (fdd
->write_q
!= NULL
&& fdd
->write_q
->next
!= NULL
) {
151 for (dwrite_q
*q
= fdd
->write_q
; q
!= NULL
; q
= q
->next
)
152 len
+= q
->len
- q
->buf_offset
;
154 dwrite_q
*wq
= (dwrite_q
*)memAllocate(MEM_DWRITE_Q
);
156 wq
->buf
= (char *)xmalloc(len
);
164 wq
->free_func
= cxx_xfree
;
166 while (fdd
->write_q
!= NULL
) {
167 dwrite_q
*q
= fdd
->write_q
;
169 len
= q
->len
- q
->buf_offset
;
170 memcpy(wq
->buf
+ wq
->len
, q
->buf
+ q
->buf_offset
, len
);
172 fdd
->write_q
= q
->next
;
175 q
->free_func(q
->buf
);
177 memFree(q
, MEM_DWRITE_Q
);
180 fdd
->write_q_tail
= wq
;
188 diskHandleWrite(int fd
, void *)
191 fde
*F
= &fd_table
[fd
];
193 _fde_disk
*fdd
= &F
->disk
;
194 dwrite_q
*q
= fdd
->write_q
;
195 int status
= DISK_OK
;
201 PROF_start(diskHandleWrite
);
203 debugs(6, 3, "diskHandleWrite: FD " << fd
);
205 F
->flags
.write_daemon
= false;
207 assert(fdd
->write_q
!= NULL
);
209 assert(fdd
->write_q
->len
> fdd
->write_q
->buf_offset
);
211 debugs(6, 3, "diskHandleWrite: FD " << fd
<< " writing " <<
212 (fdd
->write_q
->len
- fdd
->write_q
->buf_offset
) << " bytes at " <<
213 fdd
->write_q
->file_offset
);
217 if (fdd
->write_q
->file_offset
!= -1) {
219 if (lseek(fd
, fdd
->write_q
->file_offset
, SEEK_SET
) == -1) {
221 debugs(50, DBG_IMPORTANT
, "error in seek for FD " << fd
<< ": " << xstrerr(xerrno
));
222 // XXX: handle error?
226 len
= FD_WRITE_METHOD(fd
,
227 fdd
->write_q
->buf
+ fdd
->write_q
->buf_offset
,
228 fdd
->write_q
->len
- fdd
->write_q
->buf_offset
);
230 debugs(6, 3, "diskHandleWrite: FD " << fd
<< " len = " << len
);
232 ++ statCounter
.syscalls
.disk
.writes
;
234 fd_bytes(fd
, len
, FD_WRITE
);
237 if (!ignoreErrno(errno
)) {
238 status
= errno
== ENOSPC
? DISK_NO_SPACE_LEFT
: DISK_ERROR
;
240 debugs(50, DBG_IMPORTANT
, "diskHandleWrite: FD " << fd
<< ": disk write error: " << xstrerr(xerrno
));
243 * If there is no write callback, then this file is
244 * most likely something important like a log file, or
245 * an interprocess pipe. Its not a swapfile. We feel
246 * that a write failure on a log file is rather important,
247 * and Squid doesn't otherwise deal with this condition.
248 * So to get the administrators attention, we exit with
252 if (fdd
->wrt_handle
== NULL
)
253 fatal("Write failure -- check your disk space and cache.log");
256 * If there is a write failure, then we notify the
257 * upper layer via the callback, at the end of this
258 * function. Meanwhile, flush all pending buffers
259 * here. Let the upper layer decide how to handle the
260 * failure. This will prevent experiencing multiple,
261 * repeated write failures for the same FD because of
265 fdd
->write_q
= q
->next
;
268 q
->free_func(q
->buf
);
271 memFree(q
, MEM_DWRITE_Q
);
274 } while ((q
= fdd
->write_q
));
281 /* q might become NULL from write failure above */
282 q
->buf_offset
+= len
;
284 if (q
->buf_offset
> q
->len
)
285 debugs(50, DBG_IMPORTANT
, "diskHandleWriteComplete: q->buf_offset > q->len (" <<
286 q
<< "," << (int) q
->buf_offset
<< ", " << q
->len
<< ", " <<
287 len
<< " FD " << fd
<< ")");
289 assert(q
->buf_offset
<= q
->len
);
291 if (q
->buf_offset
== q
->len
) {
293 fdd
->write_q
= q
->next
;
296 q
->free_func(q
->buf
);
299 memFree(q
, MEM_DWRITE_Q
);
305 if (fdd
->write_q
== NULL
) {
307 fdd
->write_q_tail
= NULL
;
309 /* another block is queued */
310 diskCombineWrites(fdd
);
311 Comm::SetSelect(fd
, COMM_SELECT_WRITE
, diskHandleWrite
, NULL
, 0);
312 F
->flags
.write_daemon
= true;
315 do_close
= F
->flags
.close_request
;
317 if (fdd
->wrt_handle
) {
318 DWCB
*callback
= fdd
->wrt_handle
;
320 fdd
->wrt_handle
= NULL
;
322 if (cbdataReferenceValidDone(fdd
->wrt_handle_data
, &cbdata
)) {
323 callback(fd
, status
, len
, cbdata
);
325 * NOTE, this callback can close the FD, so we must
326 * not touch 'F', 'fdd', etc. after this.
328 PROF_stop(diskHandleWrite
);
330 /* XXX But what about close_request??? */
337 PROF_stop(diskHandleWrite
);
340 /* write block to a file */
341 /* write back queue. Only one writer at a time. */
342 /* call a handle when writing is complete. */
346 void const *ptr_to_buf
,
353 fde
*F
= &fd_table
[fd
];
354 PROF_start(file_write
);
356 assert(F
->flags
.open
);
357 /* if we got here. Caller is eligible to write. */
358 wq
= (dwrite_q
*)memAllocate(MEM_DWRITE_Q
);
359 wq
->file_offset
= file_offset
;
360 wq
->buf
= (char *)ptr_to_buf
;
364 wq
->free_func
= free_func
;
366 if (!F
->disk
.wrt_handle_data
) {
367 F
->disk
.wrt_handle
= handle
;
368 F
->disk
.wrt_handle_data
= cbdataReference(handle_data
);
370 /* Detect if there is multiple concurrent users of this fd.. we only support one callback */
371 assert(F
->disk
.wrt_handle_data
== handle_data
&& F
->disk
.wrt_handle
== handle
);
375 if (F
->disk
.write_q
== NULL
) {
377 F
->disk
.write_q
= F
->disk
.write_q_tail
= wq
;
379 F
->disk
.write_q_tail
->next
= wq
;
380 F
->disk
.write_q_tail
= wq
;
383 if (!F
->flags
.write_daemon
) {
384 diskHandleWrite(fd
, NULL
);
387 PROF_stop(file_write
);
391 * a wrapper around file_write to allow for MemBuf to be file_written
395 file_write_mbuf(int fd
, off_t off
, MemBuf mb
, DWCB
* handler
, void *handler_data
)
397 file_write(fd
, off
, mb
.buf
, mb
.size
, handler
, handler_data
, mb
.freeFunc());
402 diskHandleRead(int fd
, void *data
)
404 dread_ctrl
*ctrl_dat
= (dread_ctrl
*)data
;
405 fde
*F
= &fd_table
[fd
];
411 * FD < 0 indicates premature close; we just have to free
416 memFree(ctrl_dat
, MEM_DREAD_CTRL
);
420 PROF_start(diskHandleRead
);
422 #if WRITES_MAINTAIN_DISK_OFFSET
423 if (F
->disk
.offset
!= ctrl_dat
->offset
) {
427 debugs(6, 3, "diskHandleRead: FD " << fd
<< " seeking to offset " << ctrl_dat
->offset
);
429 if (lseek(fd
, ctrl_dat
->offset
, SEEK_SET
) == -1) {
431 // shouldn't happen, let's detect that
432 debugs(50, DBG_IMPORTANT
, "error in seek for FD " << fd
<< ": " << xstrerr(xerrno
));
433 // XXX handle failures?
435 ++ statCounter
.syscalls
.disk
.seeks
;
436 F
->disk
.offset
= ctrl_dat
->offset
;
440 len
= FD_READ_METHOD(fd
, ctrl_dat
->buf
, ctrl_dat
->req_len
);
444 F
->disk
.offset
+= len
;
446 ++ statCounter
.syscalls
.disk
.reads
;
448 fd_bytes(fd
, len
, FD_READ
);
451 if (ignoreErrno(xerrno
)) {
452 Comm::SetSelect(fd
, COMM_SELECT_READ
, diskHandleRead
, ctrl_dat
, 0);
453 PROF_stop(diskHandleRead
);
457 debugs(50, DBG_IMPORTANT
, "diskHandleRead: FD " << fd
<< ": " << xstrerr(xerrno
));
460 } else if (len
== 0) {
464 if (cbdataReferenceValid(ctrl_dat
->client_data
))
465 ctrl_dat
->handler(fd
, ctrl_dat
->buf
, len
, rc
, ctrl_dat
->client_data
);
467 cbdataReferenceDone(ctrl_dat
->client_data
);
469 memFree(ctrl_dat
, MEM_DREAD_CTRL
);
471 PROF_stop(diskHandleRead
);
474 /* start read operation */
475 /* buffer must be allocated from the caller.
476 * It must have at least req_len space in there.
477 * call handler when a reading is complete. */
479 file_read(int fd
, char *buf
, int req_len
, off_t offset
, DRCB
* handler
, void *client_data
)
481 dread_ctrl
*ctrl_dat
;
482 PROF_start(file_read
);
484 ctrl_dat
= (dread_ctrl
*)memAllocate(MEM_DREAD_CTRL
);
486 ctrl_dat
->offset
= offset
;
487 ctrl_dat
->req_len
= req_len
;
489 ctrl_dat
->end_of_file
= 0;
490 ctrl_dat
->handler
= handler
;
491 ctrl_dat
->client_data
= cbdataReference(client_data
);
492 diskHandleRead(fd
, ctrl_dat
);
493 PROF_stop(file_read
);
497 safeunlink(const char *s
, int quiet
)
499 ++ statCounter
.syscalls
.disk
.unlinks
;
501 if (unlink(s
) < 0 && !quiet
) {
503 debugs(50, DBG_IMPORTANT
, "safeunlink: Couldn't delete " << s
<< ": " << xstrerr(xerrno
));
508 FileRename(const SBuf
&from
, const SBuf
&to
)
510 debugs(21, 2, "renaming " << from
<< " to " << to
);
512 // non-const copy for c_str()
514 // ensure c_str() lifetimes even if `to` and `from` share memory
515 SBuf
to2(to
.rawContent(), to
.length());
517 #if _SQUID_OS2_ || _SQUID_WINDOWS_
521 if (rename(from2
.c_str(), to2
.c_str()) == 0)
525 debugs(21, (errno
== ENOENT
? 2 : DBG_IMPORTANT
), "Cannot rename " << from
<< " to " << to
<< ": " << xstrerr(xerrno
));
531 fsBlockSize(const char *path
, int *blksize
)
535 if (xstatvfs(path
, &sfs
)) {
537 debugs(50, DBG_IMPORTANT
, "" << path
<< ": " << xstrerr(xerrno
));
542 *blksize
= (int) sfs
.f_frsize
;
544 // Sanity check; make sure we have a meaningful value.
551 #define fsbtoblk(num, fsbs, bs) \
552 (((fsbs) != 0 && (fsbs) < (bs)) ? \
553 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
555 fsStats(const char *path
, int *totl_kb
, int *free_kb
, int *totl_in
, int *free_in
)
559 if (xstatvfs(path
, &sfs
)) {
561 debugs(50, DBG_IMPORTANT
, "" << path
<< ": " << xstrerr(xerrno
));
565 *totl_kb
= (int) fsbtoblk(sfs
.f_blocks
, sfs
.f_frsize
, 1024);
566 *free_kb
= (int) fsbtoblk(sfs
.f_bfree
, sfs
.f_frsize
, 1024);
567 *totl_in
= (int) sfs
.f_files
;
568 *free_in
= (int) sfs
.f_ffree
;