165 |
|
|
166 |
static error_t |
static error_t |
167 |
nbd_read (struct store *store, |
nbd_read (struct store *store, |
168 |
store_offset_t addr, size_t index, size_t amount, void **buf, |
store_offset_t addr, size_t index, size_t amount, |
169 |
size_t *len) |
void **buf, size_t *len) |
170 |
{ |
{ |
171 |
struct nbd_request req = |
struct nbd_request req = |
172 |
{ |
{ |
174 |
type: htonl (0), /* READ */ |
type: htonl (0), /* READ */ |
175 |
}; |
}; |
176 |
error_t err; |
error_t err; |
177 |
mach_msg_type_number_t cc; |
size_t ofs, chunk; |
178 |
|
char *databuf, *piecebuf; |
179 |
if (amount > NBD_IO_MAX) |
size_t databuflen, piecelen; |
180 |
amount = NBD_IO_MAX; |
|
181 |
|
/* Send a request for the largest possible piece of remaining data and |
182 |
|
read the first piece of its reply into PIECEBUF, PIECELEN. The amount |
183 |
|
requested can be found in CHUNK. */ |
184 |
|
inline error_t request_chunk (char **buf, size_t *len) |
185 |
|
{ |
186 |
|
mach_msg_type_number_t cc; |
187 |
|
|
188 |
|
chunk = (amount - ofs) < NBD_IO_MAX ? (amount - ofs) : NBD_IO_MAX; |
189 |
|
|
190 |
|
req.from = htonll (addr); |
191 |
|
req.len = htonl (chunk); |
192 |
|
|
193 |
|
/* Advance ADDR immediately, so it always points past what we've |
194 |
|
already requested. */ |
195 |
|
addr += chunk; |
196 |
|
|
197 |
|
return (io_write (store->port, (char *) &req, sizeof req, -1, &cc) ? |
198 |
|
: cc != sizeof req ? EIO |
199 |
|
: read_reply (store, req.handle) ? |
200 |
|
: io_read (store->port, buf, len, (off_t) -1, chunk)); |
201 |
|
} |
202 |
|
|
203 |
addr <<= store->log2_block_size; |
addr <<= store->log2_block_size; |
204 |
|
|
205 |
req.from = htonll (addr); |
/* Read the first piece, which can go directly into the caller's buffer. */ |
206 |
req.len = htonl (amount); |
databuf = *buf; |
207 |
|
databuflen = *len; |
208 |
err = io_write (store->port, (char *) &req, sizeof req, -1, &cc); |
err = request_chunk (&databuf, &piecelen); |
209 |
if (err) |
if (err) |
210 |
return err; |
return err; |
211 |
if (cc != sizeof req) |
if (databuflen >= amount) |
212 |
return EIO; |
{ |
213 |
|
/* That got it all. We're done. */ |
214 |
|
*buf = databuf; |
215 |
|
*len = piecelen; |
216 |
|
return 0; |
217 |
|
} |
218 |
|
|
219 |
|
/* We haven't read the entire amount yet. */ |
220 |
|
ofs = 0; |
221 |
|
do |
222 |
|
{ |
223 |
|
/* Account for what we just read. */ |
224 |
|
ofs += piecelen; |
225 |
|
chunk -= piecelen; |
226 |
|
if (ofs == amount) |
227 |
|
{ |
228 |
|
/* That got it all. We're done. */ |
229 |
|
*buf = databuf; |
230 |
|
*len = ofs; |
231 |
|
return 0; |
232 |
|
} |
233 |
|
|
234 |
|
/* Now we'll read another piece of the data, hopefully |
235 |
|
into the latter part of the existing buffer. */ |
236 |
|
piecebuf = databuf + ofs; |
237 |
|
piecelen = databuflen - ofs; |
238 |
|
|
239 |
|
if (chunk > 0) |
240 |
|
/* We haven't finishing reading the last chunk we requested. */ |
241 |
|
err = io_read (store->port, &piecebuf, &piecelen, |
242 |
|
(off_t) -1, chunk); |
243 |
|
else |
244 |
|
/* Request the next chunk from the server. */ |
245 |
|
err = request_chunk (&piecebuf, &piecelen); |
246 |
|
|
247 |
|
if (!err && piecebuf != databuf + ofs) |
248 |
|
{ |
249 |
|
/* Now we have two discontiguous pieces of the buffer. */ |
250 |
|
size_t newbuflen = round_page (databuflen + piecelen); |
251 |
|
char *newbuf = mmap (0, newbuflen, |
252 |
|
PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); |
253 |
|
if (newbuf == MAP_FAILED) |
254 |
|
{ |
255 |
|
err = errno; |
256 |
|
break; |
257 |
|
} |
258 |
|
memcpy (newbuf, databuf, ofs); |
259 |
|
memcpy (newbuf + ofs, piecebuf, piecelen); |
260 |
|
if (databuf != *buf) |
261 |
|
munmap (databuf, databuflen); |
262 |
|
databuf = newbuf; |
263 |
|
databuflen = newbuflen; |
264 |
|
} |
265 |
|
} while (! err); |
266 |
|
|
267 |
err = read_reply (store, req.handle); |
if (databuf != *buf) |
268 |
if (err == 0) |
munmap (databuf, databuflen); |
|
err = io_read (store->port, (char **) buf, len, (off_t) -1, amount); |
|
269 |
return err; |
return err; |
270 |
} |
} |
271 |
|
|