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