MmappedFile.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 /* DEBUG: section 47 Store Directory Routines */
10 
11 #include "squid.h"
12 #include "debug/Stream.h"
13 #include "DiskIO/IORequestor.h"
15 #include "DiskIO/ReadRequest.h"
16 #include "DiskIO/WriteRequest.h"
17 #include "fs_io.h"
18 #include "globals.h"
19 
20 #include <cerrno>
21 #if HAVE_SYS_MMAN_H
22 #include <sys/mman.h>
23 #endif
24 #if HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27 
28 // Some systems such as Hurd provide mmap() API but do not support MAP_NORESERVE
29 #ifndef MAP_NORESERVE
30 #define MAP_NORESERVE 0
31 #endif
32 
34 
35 // helper class to deal with mmap(2) offset alignment and other low-level specs
36 class Mmapping
37 {
38 public:
39  Mmapping(int fd, size_t length, int prot, int flags, off_t offset);
40  ~Mmapping();
41 
42  void *map();
43  bool unmap();
44 
45 private:
46  const int fd;
47  const size_t length;
48  const int prot;
49  const int flags;
50  const off_t offset;
51 
52  off_t delta;
53  void *buf;
54 };
55 
56 MmappedFile::MmappedFile(char const *aPath): fd(-1),
57  minOffset(0), maxOffset(-1), error_(false)
58 {
59  assert(aPath);
60  path_ = xstrdup(aPath);
61  debugs(79,5, this << ' ' << path_);
62 }
63 
65 {
67  doClose();
68 }
69 
70 // XXX: almost a copy of BlockingFile::open
71 void
73 {
74  assert(fd < 0);
75 
76  /* Simulate async calls */
77  fd = file_open(path_, flags);
78  ioRequestor = callback;
79 
80  if (fd < 0) {
81  int xerrno = errno;
82  debugs(79,3, "open error: " << xstrerr(xerrno));
83  error_ = true;
84  } else {
86  debugs(79,3, "FD " << fd);
87 
88  // setup mapping boundaries
89  struct stat sb;
90  if (fstat(fd, &sb) == 0)
91  maxOffset = sb.st_size; // we do not expect it to change
92  }
93 
94  callback->ioCompletedNotification();
95 }
96 
101 void
103 {
104  /* We use the same logic path for open */
105  open(flags, mode, callback);
106 }
107 
109 {
110  if (fd >= 0) {
111  file_close(fd);
112  fd = -1;
114  }
115 }
116 
117 void
119 {
120  debugs(79, 3, this << " closing for " << ioRequestor);
121  doClose();
122  assert(ioRequestor != nullptr);
124 }
125 
126 bool
128 {
129  return fd >= 0;
130 }
131 
132 bool
134 {
135  return fd >= 0;
136 }
137 
138 bool
140 {
141  return error_;
142 }
143 
144 void
146 {
147  debugs(79,3, "(FD " << fd << ", " << aRequest->len << ", " <<
148  aRequest->offset << ")");
149 
150  assert(fd >= 0);
151  assert(ioRequestor != nullptr);
152  assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len?
153  assert(aRequest->offset >= 0);
154  assert(!error_); // TODO: propagate instead?
155 
156  assert(minOffset < 0 || minOffset <= aRequest->offset);
157  assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset));
158 
159  Mmapping mapping(fd, aRequest->len, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
160  aRequest->offset);
161 
162  bool done = false;
163  if (void *buf = mapping.map()) {
164  memcpy(aRequest->buf, buf, aRequest->len);
165  done = mapping.unmap();
166  }
167  error_ = !done;
168 
169  const ssize_t rlen = error_ ? -1 : (ssize_t)aRequest->len;
170  const int errflag = error_ ? DISK_ERROR :DISK_OK;
171  ioRequestor->readCompleted(aRequest->buf, rlen, errflag, aRequest);
172 }
173 
174 void
176 {
177  debugs(79,3, "(FD " << fd << ", " << aRequest->len << ", " <<
178  aRequest->offset << ")");
179 
180  assert(fd >= 0);
181  assert(ioRequestor != nullptr);
182  assert(aRequest->len > 0); // TODO: work around mmap failures on zero-len?
183  assert(aRequest->offset >= 0);
184  assert(!error_); // TODO: propagate instead?
185 
186  assert(minOffset < 0 || minOffset <= aRequest->offset);
187  assert(maxOffset < 0 || static_cast<uint64_t>(aRequest->offset + aRequest->len) <= static_cast<uint64_t>(maxOffset));
188 
189  const ssize_t written =
190  pwrite(fd, aRequest->buf, aRequest->len, aRequest->offset);
191  if (written < 0) {
192  debugs(79, DBG_IMPORTANT, "ERROR: " << xstrerr(errno));
193  error_ = true;
194  } else if (static_cast<size_t>(written) != aRequest->len) {
195  debugs(79, DBG_IMPORTANT, "problem: " << written << " < " << aRequest->len);
196  error_ = true;
197  }
198 
199  if (aRequest->free_func)
200  (aRequest->free_func)(const_cast<char*>(aRequest->buf)); // broken API?
201 
202  if (!error_) {
203  debugs(79,5, "wrote " << aRequest->len << " to FD " << fd << " at " << aRequest->offset);
204  } else {
205  doClose();
206  }
207 
208  const ssize_t rlen = error_ ? 0 : (ssize_t)aRequest->len;
209  const int errflag = error_ ? DISK_ERROR :DISK_OK;
210  ioRequestor->writeCompleted(errflag, rlen, aRequest);
211 }
212 
214 bool
216 {
217  return false;
218 }
219 
220 Mmapping::Mmapping(int aFd, size_t aLength, int aProt, int aFlags, off_t anOffset):
221  fd(aFd), length(aLength), prot(aProt), flags(aFlags), offset(anOffset),
222  delta(-1), buf(nullptr)
223 {
224 }
225 
227 {
228  if (buf)
229  unmap();
230 }
231 
232 void *
234 {
235  // mmap(2) requires that offset is a multiple of the page size
236  static const int pageSize = getpagesize();
237  delta = offset % pageSize;
238 
239  buf = mmap(nullptr, length + delta, prot, flags, fd, offset - delta);
240 
241  if (buf == MAP_FAILED) {
242  const int errNo = errno;
243  debugs(79,3, "error FD " << fd << "mmap(" << length << '+' <<
244  delta << ", " << offset << '-' << delta << "): " << xstrerr(errNo));
245  buf = nullptr;
246  return nullptr;
247  }
248 
249  return static_cast<char*>(buf) + delta;
250 }
251 
252 bool
254 {
255  debugs(79,9, "FD " << fd <<
256  " munmap(" << buf << ", " << length << '+' << delta << ')');
257 
258  if (!buf) // forgot or failed to map
259  return false;
260 
261  const bool error = munmap(buf, length + delta) != 0;
262  if (error) {
263  const int errNo = errno;
264  debugs(79,3, "error FD " << fd <<
265  " munmap(" << buf << ", " << length << '+' << delta << "): " <<
266  "): " << xstrerr(errNo));
267  }
268  buf = nullptr;
269  return !error;
270 }
271 
272 // TODO: check MAP_NORESERVE, consider MAP_POPULATE and MAP_FIXED
273 
CBDATA_CLASS_INIT(MmappedFile)
const char * xstrerr(int error)
Definition: xstrerror.cc:83
const int fd
descriptor of the mmapped file
Definition: MmappedFile.cc:46
const int flags
other mmap(2) flags
Definition: MmappedFile.cc:49
#define MAP_NORESERVE
Definition: MmappedFile.cc:30
const int prot
mmap(2) "protection" flags
Definition: MmappedFile.cc:48
void close() override
Definition: MmappedFile.cc:118
void error(char *format,...)
bool ioInProgress() const override
we only support blocking I/O
Definition: MmappedFile.cc:215
bool canWrite() const override
Definition: MmappedFile.cc:133
const char * path_
Definition: MmappedFile.h:36
#define xstrdup
bool canRead() const override
Definition: MmappedFile.cc:127
int64_t minOffset
enforced if not negative (to preserve file headers)
Definition: MmappedFile.h:43
void file_close(int fd)
Definition: fs_io.cc:93
int store_open_disk_fd
#define DISK_OK
Definition: defines.h:27
void read(ReadRequest *) override
Definition: MmappedFile.cc:145
void open(int flags, mode_t mode, RefCount< IORequestor > callback) override
Definition: MmappedFile.cc:72
bool error() const override
Definition: MmappedFile.cc:139
off_t delta
mapped buffer increment to hit user offset
Definition: MmappedFile.cc:52
virtual void ioCompletedNotification()=0
RefCount< IORequestor > ioRequestor
Definition: MmappedFile.h:37
~MmappedFile() override
Definition: MmappedFile.cc:64
Mmapping(int fd, size_t length, int prot, int flags, off_t offset)
Definition: MmappedFile.cc:220
#define safe_free(x)
Definition: xalloc.h:73
#define assert(EX)
Definition: assert.h:17
const off_t offset
user-requested data offset
Definition: MmappedFile.cc:50
size_t len
Definition: ReadRequest.h:26
bool unmap()
unmaps previously mapped buffer, if any
Definition: MmappedFile.cc:253
FREE * free_func
Definition: WriteRequest.h:28
int64_t maxOffset
enforced if not negative (to avoid crashes)
Definition: MmappedFile.h:44
virtual void writeCompleted(int errflag, size_t len, RefCount< WriteRequest >)=0
char * buf
Definition: ReadRequest.h:24
void write(WriteRequest *) override
Definition: MmappedFile.cc:175
const char * buf
Definition: WriteRequest.h:25
unsigned short mode_t
Definition: types.h:129
#define DBG_IMPORTANT
Definition: Stream.h:38
MmappedFile(char const *path)
Definition: MmappedFile.cc:56
virtual void closeCompleted()=0
void * buf
buffer returned by mmap, needed for munmap
Definition: MmappedFile.cc:53
const size_t length
user-requested data length, needed for munmap
Definition: MmappedFile.cc:47
#define DISK_ERROR
Definition: defines.h:28
void * map()
calls mmap(2); returns usable buffer or nil on failure
Definition: MmappedFile.cc:233
int file_open(const char *path, int mode)
Definition: fs_io.cc:65
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
off_t offset
Definition: ReadRequest.h:25
virtual void readCompleted(const char *buf, int len, int errflag, RefCount< ReadRequest >)=0
void doClose()
Definition: MmappedFile.cc:108
void create(int flags, mode_t mode, RefCount< IORequestor > callback) override
Definition: MmappedFile.cc:102

 

Introduction

Documentation

Support

Miscellaneous