*** empty log message ***
[arla.git] / arlad / abuf.c
blob1309026eef943e0bb07a3764b0698cdab9574ef3
1 /*
2 * Copyright (c) 1995 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include <config.h>
36 RCSID("$Id$") ;
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <errno.h>
43 #ifdef HAVE_SYS_MMAN_H
44 #include <sys/mman.h>
45 #endif
46 #include <unistd.h>
48 #include <fs_errors.h>
50 #include <roken.h>
52 #include <arla_local.h>
54 struct abuf_data {
55 FCacheEntry *entry;
58 static int
59 abuf_flush(fbuf *f);
61 static inline FCacheEntry *
62 abuf_entry(fbuf *f)
64 return ((struct abuf_data *)(f)->data)->entry;
68 #ifdef HAVE_MMAP
71 * mmap implementation for copy{rx2cache,cache2rx}. It's a little
72 * complicated to support reading/writing on non page boundaries, plus
73 * the block handling.
76 #if !defined(MAP_FAILED)
77 #define MAP_FAILED ((void *)(-1))
78 #endif
82 * use mmap for transfer between rx call and cache files
85 static int
86 cachetransfer(struct rx_call *call, FCacheEntry *entry,
87 off_t off, off_t len, Bool rxwritep)
89 void *buf;
90 int rw_len;
91 int ret = 0;
92 off_t adjust_off, adjust_len;
93 size_t mmap_len, block_len;
94 size_t size;
95 int iosize = getpagesize();
96 int fd = 0;
98 if (len == 0)
99 return ret;
101 adjust_off = off % iosize;
103 while (len > 0) {
104 off_t real_off = off - adjust_off;
105 off_t block_off = block_offset(real_off);
106 off_t mmap_off = real_off - block_off;
108 block_len = fcache_getblocksize() - mmap_off;
109 size = len + adjust_off;
110 if (size > block_len)
111 size = block_len;
113 adjust_len = iosize - (size % iosize);
114 mmap_len = size + adjust_len;
116 if (fd == 0 || mmap_off == 0) {
117 if (fd != 0)
118 if (close(fd))
119 return errno;
121 fd = fcache_open_block(entry, block_off, !rxwritep);
122 if (fd < 0)
123 return errno;
125 if (!rxwritep) {
127 * always truncate to be on the "safe" side.
128 * We assume that we always get a full block or to EOF.
131 ret = ftruncate(fd, mmap_len);
132 if (ret)
133 break;
137 if (rxwritep)
138 buf = mmap(0, mmap_len, PROT_READ, MAP_PRIVATE, fd, mmap_off);
139 else
140 buf = mmap(0, mmap_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_off);
142 if (buf == (void *) MAP_FAILED) {
143 ret = errno;
144 break;
147 if (rxwritep)
148 rw_len = rx_Write(call, (char *)buf + adjust_off, size - adjust_off);
149 else
150 rw_len = rx_Read(call, (char *)buf + adjust_off, size - adjust_off);
152 if (rw_len != mmap_len - adjust_off)
153 ret = conv_to_arla_errno(rx_GetCallError(call));
155 len -= rw_len;
156 off += rw_len;
157 adjust_off = 0;
159 if (!rxwritep) {
160 if (msync(buf, mmap_len, MS_ASYNC))
161 ret = errno;
163 if (munmap(buf, mmap_len))
164 ret = errno;
166 if (ret)
167 break;
170 if (fd != 0)
171 close(fd);
173 return ret;
175 #else /* !HAVE_MMAP */
178 * use malloc for transfer between rx call and cache files
181 static int
182 cachetransfer(struct rx_call *call, FCacheEntry *entry,
183 off_t off, off_t len, Bool rxwritep)
185 void *buf;
186 int ret = 0;
187 size_t io_len, block_len;
188 ssize_t nread, nwrite;
189 u_long bufsize = 8192;
190 int fd = 0;
192 if (len == 0)
193 return 0;
195 buf = malloc(bufsize);
196 if (buf == NULL)
197 return ENOMEM;
199 while (len > 0) {
200 uint64_t block_off = block_offset(off);
201 off_t buf_off = off - block_off;
203 if (block_off == off) {
204 if ((fd != 0 && close(fd) != 0)
205 || (fd = fcache_open_block(entry, block_off, !rxwritep)) < 0) {
206 ret = errno;
207 fd = 0;
208 arla_debug_assert(0);
209 break;
213 io_len = min(bufsize, len);
214 block_len = fcache_getblocksize() - buf_off;
216 if (io_len > block_len)
217 io_len = block_len;
219 if (rxwritep) {
220 nread = pread(fd, buf, io_len, buf_off);
221 if (nread <= 0) {
222 ret = errno;
223 arla_debug_assert(0);
224 break;
227 nwrite = rx_Write(call, buf, nread);
228 if (nwrite != nread) {
229 ret = conv_to_arla_errno(rx_GetCallError(call));
230 break;
232 } else {
233 nread = rx_Read(call, buf, io_len);
234 if (nread != io_len) {
235 ret = conv_to_arla_errno(rx_GetCallError(call));
236 break;
239 nwrite = pwrite(fd, buf, nread, buf_off);
240 if (nwrite != nread) {
241 ret = errno;
242 arla_debug_assert(0);
243 break;
246 len -= nread;
247 off += nread;
249 if (ret)
250 break;
253 if (fd != 0)
254 close(fd);
256 free(buf);
258 return ret;
260 #endif /* !HAVE_MMAP */
263 * Copy from a RX_call to a cache node.
264 * The area between offset and len + offset should be present in the cache.
266 * Returns 0 or error.
270 copyrx2cache(struct rx_call *call, FCacheEntry *entry, off_t off, off_t len)
272 return cachetransfer(call, entry, off, len, FALSE);
276 * Copy `len' bytes from `entry' to `call'.
277 * Returns 0 or error.
281 copycache2rx(FCacheEntry *entry, struct rx_call *call, off_t off, off_t len)
283 return cachetransfer(call, entry, off, len, TRUE);
287 * actually do the malloc + read thing
290 static int
291 abuf_populate(fbuf *f)
293 uint64_t block_off = 0;
294 ssize_t nread;
295 off_t off = 0;
296 char *buf;
297 int fd = 0;
298 size_t len = f->len;
299 int ret = 0;
301 buf = malloc(len);
302 if (buf == NULL) {
303 int ret = errno;
304 arla_warnx(ADEBWARN, "abuf_populate: malloc(%lu) failed",
305 (unsigned long)len);
306 arla_debug_assert(0);
307 return ret;
310 while (len > 0) {
311 block_off = block_offset(off);
312 off_t r_len = min(len, fcache_getblocksize());
314 if ((fd != 0 && close(fd) != 0)
315 || (fd = fcache_open_block(abuf_entry(f),
316 block_off, FALSE)) < 0) {
317 ret = errno;
318 fd = 0;
319 break;
322 nread = pread(fd, buf + off, r_len, off - block_off);
323 if (nread != r_len) {
324 ret = errno;
325 break;
328 len -= nread;
329 off += nread;
332 if (ret)
333 free(buf);
334 else
335 f->buf = buf;
337 if (fd)
338 close(fd);
340 return ret;
345 * infrastructure for truncate handling
348 struct truncate_cb_data {
349 off_t length;
350 uint64_t last_off;
351 uint64_t prev_last_off;
355 abuf_truncate_block(FCacheEntry *entry, uint64_t offset, uint64_t blocklen)
357 int ret;
358 int fd = fcache_open_block(entry, offset, TRUE);
359 if (fd < 0) {
360 ret = errno;
361 arla_warnx(ADEBWARN, "abuf_truncate_block: "
362 "open failed at offset 0x%" PRIX64 "\n", offset);
363 arla_debug_assert(0);
364 return ret;
367 ret = ftruncate(fd, blocklen);
368 if (ret) {
369 ret = errno;
370 arla_warnx(ADEBWARN, "abuf_truncate_block: "
371 "truncate failed at offset 0x%" PRIX64 "\n", offset);
372 arla_debug_assert(0);
373 return ret;
376 close(fd);
377 return 0;
380 static void
381 truncate_callback(struct block *block, void *data)
383 struct truncate_cb_data *cb_data = (struct truncate_cb_data *)data;
385 if (block->offset >= cb_data->length && block->offset != 0) {
386 fcache_throw_block(block);
387 } else if (cb_data->last_off == block->offset) {
388 (void)abuf_truncate_block(block->node, block->offset,
389 cb_data->length - block->offset);
390 } else if (cb_data->prev_last_off == block->offset) {
391 uint64_t blocklen = cb_data->length - block->offset;
392 uint64_t blocksize = fcache_getblocksize();
394 if (blocklen > blocksize)
395 blocklen = blocksize;
397 (void)abuf_truncate_block(block->node, block->offset, blocklen);
398 } else {
399 /* block should be ok */
404 * truncate the cache data for real, update 'have' flags
407 static int
408 abuf_truncate_int(FCacheEntry *entry, off_t length)
410 struct truncate_cb_data data;
412 data.length = length;
413 data.last_off = block_offset(length);
414 data.prev_last_off =
415 block_offset(fcache_get_status_length(&entry->status));
417 block_foreach(entry, truncate_callback, &data);
418 return 0;
422 * Change the size of the underlying cache and the fbuf to `new_len'
423 * bytes.
424 * Returns 0 or error.
427 static int
428 abuf_truncate_op(fbuf *f, size_t new_len)
430 int ret;
432 ret = abuf_flush(f);
433 if (ret)
434 goto fail;
436 ret = abuf_truncate_int(abuf_entry(f), new_len);
437 if (ret)
438 goto fail;
440 if (f->buf) {
441 void *buf = realloc(f->buf, new_len);
442 if (buf == NULL) {
443 ret = ENOMEM;
444 goto fail;
447 f->buf = buf;
450 f->len = new_len;
452 return 0;
454 fail:
455 if (f->buf) {
456 free(f->buf);
457 f->buf = NULL;
460 arla_debug_assert(0);
462 return ret;
466 * Change the size of the underlying cache and the fbuf to `new_len'
467 * bytes.
468 * Returns 0 or error.
472 abuf_truncate(FCacheEntry *entry, size_t new_len)
474 return abuf_truncate_int(entry, new_len);
477 static void
478 purge_callback(struct block *block, void *data)
480 fcache_throw_block(block);
484 * Throw all data in the node. This is a special case of truncate
485 * that does not leave block zero.
489 abuf_purge(FCacheEntry *entry)
491 block_foreach(entry, purge_callback, NULL);
492 return 0;
496 * Create a fbuf with (fd, len, flags).
497 * Returns 0 or error.
501 abuf_create(fbuf *f, FCacheEntry *entry, size_t len, fbuf_flags flags)
503 struct abuf_data *data = malloc(sizeof(*data));
504 if (data == NULL)
505 return ENOMEM;
507 data->entry = entry;
509 f->data = data;
510 f->len = len;
511 f->buf = NULL;
512 f->flags = flags;
514 f->truncate = abuf_truncate_op;
516 return abuf_populate(f);
520 * Write out the data of `f' to the file.
521 * Returns 0 or error.
524 static int
525 abuf_flush(fbuf *f)
527 size_t len = f->len;
528 uint64_t block_off = 0;
529 ssize_t nwrite;
530 int fd, ret = 0;
532 if (!f->buf)
533 return 0;
535 if ((f->flags & FBUF_WRITE) != FBUF_WRITE)
536 return 0;
538 while (len > 0 && ret == 0) {
539 size_t size = min(len, fcache_getblocksize());
541 if ((fd = fcache_open_block(abuf_entry(f),
542 block_off, TRUE)) < 0) {
543 ret = errno;
544 break;
547 nwrite = write(fd, (char *)f->buf + block_off, size);
548 if (nwrite != size) {
549 ret = errno;
550 close(fd);
551 break;
554 len -= nwrite;
555 block_off += fcache_getblocksize();
556 ret = close(fd);
559 return ret;
563 * End using `f'.
564 * Returns 0 or error.
568 abuf_end(fbuf *f)
570 int ret = 0;
572 if (f->buf) {
573 ret = abuf_flush(f);
574 free(f->buf);
575 f->buf = NULL;
578 free(f->data);
580 return ret;