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