1 /*****************************************************************************
2 * RRDtool 1.4.7 Copyright by Tobi Oetiker, 1997-2012
3 *****************************************************************************
4 * rrd_open.c Open an RRD File
5 *****************************************************************************
6 * $Id$
7 *****************************************************************************/
9 #include "rrd_tool.h"
10 #include "unused.h"
12 #ifdef WIN32
13 #include <stdlib.h>
14 #include <fcntl.h>
15 #include <sys/stat.h>
16 #endif
18 #ifdef HAVE_BROKEN_MS_ASYNC
19 #include <sys/types.h>
20 #include <utime.h>
21 #endif
23 #define MEMBLK 8192
25 #ifdef WIN32
26 #define _LK_UNLCK 0 /* Unlock */
27 #define _LK_LOCK 1 /* Lock */
28 #define _LK_NBLCK 2 /* Non-blocking lock */
29 #define _LK_RLCK 3 /* Lock for read only */
30 #define _LK_NBRLCK 4 /* Non-blocking lock for read only */
33 #define LK_UNLCK _LK_UNLCK
34 #define LK_LOCK _LK_LOCK
35 #define LK_NBLCK _LK_NBLCK
36 #define LK_RLCK _LK_RLCK
37 #define LK_NBRLCK _LK_NBRLCK
38 #endif
40 /* DEBUG 2 prints information obtained via mincore(2) */
41 #define DEBUG 1
42 /* do not calculate exact madvise hints but assume 1 page for headers and
43 * set DONTNEED for the rest, which is assumed to be data */
44 /* Avoid calling madvise on areas that were already hinted. May be benefical if
45 * your syscalls are very slow */
47 #ifdef HAVE_MMAP
48 /* the cast to void* is there to avoid this warning seen on ia64 with certain
49 versions of gcc: 'cast increases required alignment of target type'
50 */
51 #define __rrd_read(dst, dst_t, cnt) { \
52 size_t wanted = sizeof(dst_t)*(cnt); \
53 if (offset + wanted > rrd_file->file_len) { \
54 rrd_set_error("reached EOF while loading header " #dst); \
55 goto out_nullify_head; \
56 } \
57 (dst) = (dst_t*)(void*) (data + offset); \
58 offset += wanted; \
59 }
60 #else
61 #define __rrd_read(dst, dst_t, cnt) { \
62 size_t wanted = sizeof(dst_t)*(cnt); \
63 size_t got; \
64 if ((dst = (dst_t*)malloc(wanted)) == NULL) { \
65 rrd_set_error(#dst " malloc"); \
66 goto out_nullify_head; \
67 } \
68 got = read (rrd_simple_file->fd, dst, wanted); \
69 if (got != wanted) { \
70 rrd_set_error("short read while reading header " #dst); \
71 goto out_nullify_head; \
72 } \
73 offset += got; \
74 }
75 #endif
77 /* get the address of the start of this page */
78 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
79 #ifndef PAGE_START
80 #define PAGE_START(addr) ((addr)&(~(_page_size-1)))
81 #endif
82 #endif
84 /* Open a database file, return its header and an open filehandle,
85 * positioned to the first cdp in the first rra.
86 * In the error path of rrd_open, only rrd_free(&rrd) has to be called
87 * before returning an error. Do not call rrd_close upon failure of rrd_open.
88 * If creating a new file, the parameter rrd must be initialised with
89 * details of the file content.
90 * If opening an existing file, then use rrd must be initialised by
91 * rrd_init(rrd) prior to invoking rrd_open
92 */
94 rrd_file_t *rrd_open(
95 const char *const file_name,
96 rrd_t *rrd,
97 unsigned rdwr)
98 {
99 unsigned long ui;
100 int flags = 0;
101 int version;
103 #ifdef HAVE_MMAP
104 ssize_t _page_size = sysconf(_SC_PAGESIZE);
105 char *data = MAP_FAILED;
106 #endif
107 off_t offset = 0;
108 struct stat statb;
109 rrd_file_t *rrd_file = NULL;
110 rrd_simple_file_t *rrd_simple_file = NULL;
111 size_t newfile_size = 0;
112 size_t header_len, value_cnt, data_len;
114 /* Are we creating a new file? */
115 if((rdwr & RRD_CREAT) && (rrd->stat_head != NULL))
116 {
117 header_len = rrd_get_header_size(rrd);
119 value_cnt = 0;
120 for (ui = 0; ui < rrd->stat_head->rra_cnt; ui++)
121 value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[ui].row_cnt;
123 data_len = sizeof(rrd_value_t) * value_cnt;
125 newfile_size = header_len + data_len;
126 }
128 rrd_file = (rrd_file_t*)malloc(sizeof(rrd_file_t));
129 if (rrd_file == NULL) {
130 rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
131 return NULL;
132 }
133 memset(rrd_file, 0, sizeof(rrd_file_t));
135 rrd_file->pvt = malloc(sizeof(rrd_simple_file_t));
136 if(rrd_file->pvt == NULL) {
137 rrd_set_error("allocating rrd_simple_file for '%s'", file_name);
138 return NULL;
139 }
140 memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t));
141 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
143 #ifdef DEBUG
144 if ((rdwr & (RRD_READONLY | RRD_READWRITE)) ==
145 (RRD_READONLY | RRD_READWRITE)) {
146 /* Both READONLY and READWRITE were given, which is invalid. */
147 rrd_set_error("in read/write request mask");
148 exit(-1);
149 }
150 #endif
152 #ifdef HAVE_MMAP
153 rrd_simple_file->mm_prot = PROT_READ;
154 rrd_simple_file->mm_flags = 0;
155 #endif
157 if (rdwr & RRD_READONLY) {
158 flags |= O_RDONLY;
159 #ifdef HAVE_MMAP
160 # if !defined(AIX)
161 rrd_simple_file->mm_flags = MAP_PRIVATE;
162 # endif
163 # ifdef MAP_NORESERVE
164 rrd_simple_file->mm_flags |= MAP_NORESERVE; /* readonly, so no swap backing needed */
165 # endif
166 #endif
167 } else {
168 if (rdwr & RRD_READWRITE) {
169 flags |= O_RDWR;
170 #ifdef HAVE_MMAP
171 rrd_simple_file->mm_flags = MAP_SHARED;
172 rrd_simple_file->mm_prot |= PROT_WRITE;
173 #endif
174 }
175 if (rdwr & RRD_CREAT) {
176 flags |= (O_CREAT | O_TRUNC);
177 }
178 if (rdwr & RRD_EXCL) {
179 flags |= O_EXCL;
180 }
181 }
182 if (rdwr & RRD_READAHEAD) {
183 #ifdef MAP_POPULATE
184 rrd_simple_file->mm_flags |= MAP_POPULATE; /* populate ptes and data */
185 #endif
186 #if defined MAP_NONBLOCK
187 rrd_simple_file->mm_flags |= MAP_NONBLOCK; /* just populate ptes */
188 #endif
189 }
190 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
191 flags |= O_BINARY;
192 #endif
194 if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
195 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
196 goto out_free;
197 }
199 #ifdef HAVE_MMAP
200 #ifdef HAVE_BROKEN_MS_ASYNC
201 if (rdwr & RRD_READWRITE) {
202 /* some unices, the files mtime does not get update
203 on msync MS_ASYNC, in order to help them,
204 we update the the timestamp at this point.
205 The thing happens pretty 'close' to the open
206 call so the chances of a race should be minimal.
208 Maybe ask your vendor to fix your OS ... */
209 utime(file_name,NULL);
210 }
211 #endif
212 #endif
214 /* Better try to avoid seeks as much as possible. stat may be heavy but
215 * many concurrent seeks are even worse. */
216 if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
217 rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
218 goto out_close;
219 }
220 if (newfile_size == 0) {
221 rrd_file->file_len = statb.st_size;
222 } else {
223 rrd_file->file_len = newfile_size;
224 #ifdef HAVE_POSIX_FALLOCATE
225 if (posix_fallocate(rrd_simple_file->fd, 0, newfile_size) == -1) {
226 rrd_set_error("posix_fallocate '%s': %s", file_name,
227 rrd_strerror(errno));
228 goto out_close;
229 }
230 #else
231 lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
232 if ( write(rrd_simple_file->fd, "\0", 1) == -1){ /* poke */
233 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
234 goto out_close;
235 }
236 lseek(rrd_simple_file->fd, 0, SEEK_SET);
237 #endif
238 }
239 #ifdef HAVE_POSIX_FADVISE
240 /* In general we need no read-ahead when dealing with rrd_files.
241 When we stop reading, it is highly unlikely that we start up again.
242 In this manner we actually save time and diskaccess (and buffer cache).
243 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
244 posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
245 #endif
247 /*
248 if (rdwr & RRD_READWRITE)
249 {
250 if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
251 rrd_set_error("failed to disable the stream buffer\n");
252 return (-1);
253 }
254 }
255 */
257 #ifdef HAVE_MMAP
258 #ifndef HAVE_POSIX_FALLOCATE
259 /* force allocating the file on the underlaying filesystem to prevent any
260 * future bus error when the filesystem is full and attempting to write
261 * trough the file mapping. Filling the file using memset on the file
262 * mapping can also lead some bus error, so we use the old fashioned
263 * write().
264 */
265 if (rdwr & RRD_CREAT) {
266 char buf[4096];
267 unsigned i;
269 memset(buf, DNAN, sizeof buf);
270 lseek(rrd_simple_file->fd, offset, SEEK_SET);
272 for (i = 0; i < (newfile_size - 1) / sizeof buf; ++i)
273 {
274 if (write(rrd_simple_file->fd, buf, sizeof buf) == -1)
275 {
276 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
277 goto out_close;
278 }
279 }
281 if (write(rrd_simple_file->fd, buf,
282 (newfile_size - 1) % sizeof buf) == -1)
283 {
284 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
285 goto out_close;
286 }
288 lseek(rrd_simple_file->fd, 0, SEEK_SET);
289 }
290 #endif
292 data = mmap(0, rrd_file->file_len,
293 rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
294 rrd_simple_file->fd, offset);
296 /* lets see if the first read worked */
297 if (data == MAP_FAILED) {
298 rrd_set_error("mmaping file '%s': %s", file_name,
299 rrd_strerror(errno));
300 goto out_close;
301 }
302 rrd_simple_file->file_start = data;
303 if (rdwr & RRD_CREAT) {
304 goto out_done;
305 }
306 #endif
307 if (rdwr & RRD_CREAT)
308 goto out_done;
309 #ifdef USE_MADVISE
310 if (rdwr & RRD_COPY) {
311 /* We will read everything in a moment (copying) */
312 madvise(data, rrd_file->file_len, MADV_WILLNEED );
313 madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
314 } else {
315 /* We do not need to read anything in for the moment */
316 madvise(data, rrd_file->file_len, MADV_RANDOM);
317 /* the stat_head will be needed soonish, so hint accordingly */
318 madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
319 madvise(data, sizeof(stat_head_t), MADV_RANDOM);
320 }
321 #endif
323 __rrd_read(rrd->stat_head, stat_head_t,
324 1);
326 /* lets do some test if we are on track ... */
327 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
328 rrd_set_error("'%s' is not an RRD file", file_name);
329 goto out_nullify_head;
330 }
332 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
333 rrd_set_error("This RRD was created on another architecture");
334 goto out_nullify_head;
335 }
337 version = atoi(rrd->stat_head->version);
339 if (version > atoi(RRD_VERSION)) {
340 rrd_set_error("can't handle RRD file version %s",
341 rrd->stat_head->version);
342 goto out_nullify_head;
343 }
344 #if defined USE_MADVISE
345 /* the ds_def will be needed soonish, so hint accordingly */
346 madvise(data + PAGE_START(offset),
347 sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
348 #endif
349 __rrd_read(rrd->ds_def, ds_def_t,
350 rrd->stat_head->ds_cnt);
352 #if defined USE_MADVISE
353 /* the rra_def will be needed soonish, so hint accordingly */
354 madvise(data + PAGE_START(offset),
355 sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
356 #endif
357 __rrd_read(rrd->rra_def, rra_def_t,
358 rrd->stat_head->rra_cnt);
360 /* handle different format for the live_head */
361 if (version < 3) {
362 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
363 if (rrd->live_head == NULL) {
364 rrd_set_error("live_head_t malloc");
365 goto out_close;
366 }
367 #if defined USE_MADVISE
368 /* the live_head will be needed soonish, so hint accordingly */
369 madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
370 #endif
371 __rrd_read(rrd->legacy_last_up, time_t,
372 1);
374 rrd->live_head->last_up = *rrd->legacy_last_up;
375 rrd->live_head->last_up_usec = 0;
376 } else {
377 #if defined USE_MADVISE
378 /* the live_head will be needed soonish, so hint accordingly */
379 madvise(data + PAGE_START(offset),
380 sizeof(live_head_t), MADV_WILLNEED);
381 #endif
382 __rrd_read(rrd->live_head, live_head_t,
383 1);
384 }
385 __rrd_read(rrd->pdp_prep, pdp_prep_t,
386 rrd->stat_head->ds_cnt);
387 __rrd_read(rrd->cdp_prep, cdp_prep_t,
388 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
389 __rrd_read(rrd->rra_ptr, rra_ptr_t,
390 rrd->stat_head->rra_cnt);
392 rrd_file->header_len = offset;
393 rrd_file->pos = offset;
395 {
396 unsigned long row_cnt = 0;
398 for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
399 row_cnt += rrd->rra_def[ui].row_cnt;
401 size_t correct_len = rrd_file->header_len +
402 sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
404 if (correct_len > rrd_file->file_len)
405 {
406 rrd_set_error("'%s' is too small (should be %ld bytes)",
407 file_name, (long long) correct_len);
408 goto out_nullify_head;
409 }
410 }
412 out_done:
413 return (rrd_file);
414 out_nullify_head:
415 rrd->stat_head = NULL;
416 out_close:
417 #ifdef HAVE_MMAP
418 if (data != MAP_FAILED)
419 munmap(data, rrd_file->file_len);
420 #endif
422 close(rrd_simple_file->fd);
423 out_free:
424 free(rrd_file->pvt);
425 free(rrd_file);
426 return NULL;
427 }
430 #if defined DEBUG && DEBUG > 1
431 /* Print list of in-core pages of a the current rrd_file. */
432 static
433 void mincore_print(
434 rrd_file_t *rrd_file,
435 char *mark)
436 {
437 rrd_simple_file_t *rrd_simple_file;
438 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
439 #ifdef HAVE_MMAP
440 /* pretty print blocks in core */
441 size_t off;
442 unsigned char *vec;
443 ssize_t _page_size = sysconf(_SC_PAGESIZE);
445 off = rrd_file->file_len +
446 ((rrd_file->file_len + _page_size - 1) / _page_size);
447 vec = malloc(off);
448 if (vec != NULL) {
449 memset(vec, 0, off);
450 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
451 int prev;
452 unsigned is_in = 0, was_in = 0;
454 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
455 is_in = vec[off] & 1; /* if lsb set then is core resident */
456 if (off == 0)
457 was_in = is_in;
458 if (was_in != is_in) {
459 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
460 was_in ? "" : "not ", vec + prev, off - prev);
461 was_in = is_in;
462 prev = off;
463 }
464 }
465 fprintf(stderr,
466 "%s: %sin core: %p len %ld\n", mark,
467 was_in ? "" : "not ", vec + prev, off - prev);
468 } else
469 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
470 }
471 #else
472 fprintf(stderr, "sorry mincore only works with mmap");
473 #endif
474 }
475 #endif /* defined DEBUG && DEBUG > 1 */
477 /*
478 * get exclusive lock to whole file.
479 * lock gets removed when we close the file
480 *
481 * returns 0 on success
482 */
483 int rrd_lock(
484 rrd_file_t *rrd_file)
485 {
486 int rcstat;
487 rrd_simple_file_t *rrd_simple_file;
488 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
490 {
491 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
492 struct _stat st;
494 if (_fstat(rrd_simple_file->fd, &st) == 0) {
495 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
496 } else {
497 rcstat = -1;
498 }
499 #else
500 struct flock lock;
502 lock.l_type = F_WRLCK; /* exclusive write lock */
503 lock.l_len = 0; /* whole file */
504 lock.l_start = 0; /* start of file */
505 lock.l_whence = SEEK_SET; /* end of file */
507 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
508 #endif
509 }
511 return (rcstat);
512 }
515 /* drop cache except for the header and the active pages */
516 void rrd_dontneed(
517 rrd_file_t *rrd_file,
518 rrd_t *rrd)
519 {
520 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
521 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
522 size_t dontneed_start;
523 size_t rra_start;
524 size_t active_block;
525 size_t i;
526 ssize_t _page_size = sysconf(_SC_PAGESIZE);
528 if (rrd_file == NULL) {
529 #if defined DEBUG && DEBUG
530 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
531 #endif
532 return;
533 }
535 #if defined DEBUG && DEBUG > 1
536 mincore_print(rrd_file, "before");
537 #endif
539 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
540 rra_start = rrd_file->header_len;
541 dontneed_start = PAGE_START(rra_start) + _page_size;
542 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
543 active_block =
544 PAGE_START(rra_start
545 + rrd->rra_ptr[i].cur_row
546 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
547 if (active_block > dontneed_start) {
548 #ifdef USE_MADVISE
549 madvise(rrd_simple_file->file_start + dontneed_start,
550 active_block - dontneed_start - 1, MADV_DONTNEED);
551 #endif
552 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
553 #ifdef HAVE_POSIX_FADVISE
554 posix_fadvise(rrd_simple_file->fd, dontneed_start,
555 active_block - dontneed_start - 1,
556 POSIX_FADV_DONTNEED);
557 #endif
558 }
559 dontneed_start = active_block;
560 /* do not release 'hot' block if update for this RAA will occur
561 * within 10 minutes */
562 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
563 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
564 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
565 dontneed_start += _page_size;
566 }
567 rra_start +=
568 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
569 sizeof(rrd_value_t);
570 }
572 if (dontneed_start < rrd_file->file_len) {
573 #ifdef USE_MADVISE
574 madvise(rrd_simple_file->file_start + dontneed_start,
575 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
576 #endif
577 #ifdef HAVE_POSIX_FADVISE
578 posix_fadvise(rrd_simple_file->fd, dontneed_start,
579 rrd_file->file_len - dontneed_start,
580 POSIX_FADV_DONTNEED);
581 #endif
582 }
584 #if defined DEBUG && DEBUG > 1
585 mincore_print(rrd_file, "after");
586 #endif
587 #endif /* without madvise and posix_fadvise it does not make much sense todo anything */
588 }
594 int rrd_close(
595 rrd_file_t *rrd_file)
596 {
597 rrd_simple_file_t *rrd_simple_file;
598 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
599 int ret;
601 #ifdef HAVE_MMAP
602 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
603 if (ret != 0)
604 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
605 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
606 if (ret != 0)
607 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
608 #endif
609 ret = close(rrd_simple_file->fd);
610 if (ret != 0)
611 rrd_set_error("closing file: %s", rrd_strerror(errno));
612 free(rrd_file->pvt);
613 free(rrd_file);
614 rrd_file = NULL;
615 return ret;
616 }
619 /* Set position of rrd_file. */
621 off_t rrd_seek(
622 rrd_file_t *rrd_file,
623 off_t off,
624 int whence)
625 {
626 off_t ret = 0;
627 rrd_simple_file_t *rrd_simple_file;
628 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
630 #ifdef HAVE_MMAP
631 if (whence == SEEK_SET)
632 rrd_file->pos = off;
633 else if (whence == SEEK_CUR)
634 rrd_file->pos += off;
635 else if (whence == SEEK_END)
636 rrd_file->pos = rrd_file->file_len + off;
637 #else
638 ret = lseek(rrd_simple_file->fd, off, whence);
639 if (ret < 0)
640 rrd_set_error("lseek: %s", rrd_strerror(errno));
641 rrd_file->pos = ret;
642 #endif
643 /* mimic fseek, which returns 0 upon success */
644 return ret < 0; /*XXX: or just ret to mimic lseek */
645 }
648 /* Get current position in rrd_file. */
650 off_t rrd_tell(
651 rrd_file_t *rrd_file)
652 {
653 return rrd_file->pos;
654 }
657 /* Read count bytes into buffer buf, starting at rrd_file->pos.
658 * Returns the number of bytes read or <0 on error. */
660 ssize_t rrd_read(
661 rrd_file_t *rrd_file,
662 void *buf,
663 size_t count)
664 {
665 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
666 #ifdef HAVE_MMAP
667 size_t _cnt = count;
668 ssize_t _surplus;
670 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
671 return 0;
672 if (buf == NULL)
673 return -1; /* EINVAL */
674 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
675 if (_surplus > 0) { /* short read */
676 _cnt -= _surplus;
677 }
678 if (_cnt == 0)
679 return 0; /* EOF */
680 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
682 rrd_file->pos += _cnt; /* mimmic read() semantics */
683 return _cnt;
684 #else
685 ssize_t ret;
687 ret = read(rrd_simple_file->fd, buf, count);
688 if (ret > 0)
689 rrd_file->pos += ret; /* mimmic read() semantics */
690 return ret;
691 #endif
692 }
695 /* Write count bytes from buffer buf to the current position
696 * rrd_file->pos of rrd_simple_file->fd.
697 * Returns the number of bytes written or <0 on error. */
699 ssize_t rrd_write(
700 rrd_file_t *rrd_file,
701 const void *buf,
702 size_t count)
703 {
704 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
705 #ifdef HAVE_MMAP
706 size_t old_size = rrd_file->file_len;
707 if (count == 0)
708 return 0;
709 if (buf == NULL)
710 return -1; /* EINVAL */
712 if((rrd_file->pos + count) > old_size)
713 {
714 rrd_set_error("attempting to write beyond end of file");
715 return -1;
716 }
717 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
718 rrd_file->pos += count;
719 return count; /* mimmic write() semantics */
720 #else
721 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
723 if (_sz > 0)
724 rrd_file->pos += _sz;
725 return _sz;
726 #endif
727 }
730 /* this is a leftover from the old days, it serves no purpose
731 and is therefore turned into a no-op */
732 void rrd_flush(
733 rrd_file_t UNUSED(*rrd_file))
734 {
735 }
737 /* Initialize RRD header. */
739 void rrd_init(
740 rrd_t *rrd)
741 {
742 rrd->stat_head = NULL;
743 rrd->ds_def = NULL;
744 rrd->rra_def = NULL;
745 rrd->live_head = NULL;
746 rrd->legacy_last_up = NULL;
747 rrd->rra_ptr = NULL;
748 rrd->pdp_prep = NULL;
749 rrd->cdp_prep = NULL;
750 rrd->rrd_value = NULL;
751 }
754 /* free RRD header data. */
756 #ifdef HAVE_MMAP
757 void rrd_free(
758 rrd_t *rrd)
759 {
760 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
761 free(rrd->live_head);
762 }
763 }
764 #else
765 void rrd_free(
766 rrd_t *rrd)
767 {
768 free(rrd->live_head);
769 free(rrd->stat_head);
770 free(rrd->ds_def);
771 free(rrd->rra_def);
772 free(rrd->rra_ptr);
773 free(rrd->pdp_prep);
774 free(rrd->cdp_prep);
775 free(rrd->rrd_value);
776 }
777 #endif
780 /* routine used by external libraries to free memory allocated by
781 * rrd library */
783 void rrd_freemem(
784 void *mem)
785 {
786 free(mem);
787 }
789 /*
790 * rra_update informs us about the RRAs being updated
791 * The low level storage API may use this information for
792 * aligning RRAs within stripes, or other performance enhancements
793 */
794 void rrd_notify_row(
795 rrd_file_t UNUSED(*rrd_file),
796 int UNUSED(rra_idx),
797 unsigned long UNUSED(rra_row),
798 time_t UNUSED(rra_time))
799 {
800 }
802 /*
803 * This function is called when creating a new RRD
804 * The storage implementation can use this opportunity to select
805 * a sensible starting row within the file.
806 * The default implementation is random, to ensure that all RRAs
807 * don't change to a new disk block at the same time
808 */
809 unsigned long rrd_select_initial_row(
810 rrd_file_t UNUSED(*rrd_file),
811 int UNUSED(rra_idx),
812 rra_def_t *rra
813 )
814 {
815 return rrd_random() % rra->row_cnt;
816 }