Code

use posix_fallocate when initialiying rrd files ... instead of filling them with...
[rrdtool.git] / src / rrd_open.c
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     }
127     
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.    
207                 
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);
271         
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                 }
280                 
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;
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)
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
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)
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);
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)
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 */
594 int rrd_close(
595     rrd_file_t *rrd_file)
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;
619 /* Set position of rrd_file.  */
621 off_t rrd_seek(
622     rrd_file_t *rrd_file,
623     off_t off,
624     int whence)
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 */
648 /* Get current position in rrd_file.  */
650 off_t rrd_tell(
651     rrd_file_t *rrd_file)
653     return rrd_file->pos;
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)
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
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)
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 */
711     
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
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))
737 /* Initialize RRD header.  */
739 void rrd_init(
740     rrd_t *rrd)
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;
754 /* free RRD header data.  */
756 #ifdef HAVE_MMAP
757 void rrd_free(
758     rrd_t *rrd)
760     if (rrd->legacy_last_up) {  /* this gets set for version < 3 only */
761         free(rrd->live_head);
762     }
764 #else
765 void rrd_free(
766     rrd_t *rrd)
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);
777 #endif
780 /* routine used by external libraries to free memory allocated by
781  * rrd library */
783 void rrd_freemem(
784     void *mem)
786     free(mem);
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))
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     )
815     return rrd_random() % rra->row_cnt;